From 7e96933cf2269f0792824d3a09d41a4c88bd0095 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 8 Aug 2013 16:42:14 +0100 Subject: [PATCH 001/549] Initial commit of script for compressing one docs history --- services/track-changes/.gitignore | 4 + .../app/coffee/ConcatManager.coffee | 121 ++++++++++ .../track-changes/app/coffee/mongojs.coffee | 8 + services/track-changes/compressHistory.coffee | 74 ++++++ .../config/settings.testing.coffee | 3 + services/track-changes/package.json | 11 + services/track-changes/rakefile.rb | 112 +++++++++ .../ConcatManager/ConcatManagerTests.coffee | 214 ++++++++++++++++++ 8 files changed, 547 insertions(+) create mode 100644 services/track-changes/.gitignore create mode 100644 services/track-changes/app/coffee/ConcatManager.coffee create mode 100644 services/track-changes/app/coffee/mongojs.coffee create mode 100644 services/track-changes/compressHistory.coffee create mode 100755 services/track-changes/config/settings.testing.coffee create mode 100644 services/track-changes/package.json create mode 100644 services/track-changes/rakefile.rb create mode 100644 services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore new file mode 100644 index 0000000000..40d016e39d --- /dev/null +++ b/services/track-changes/.gitignore @@ -0,0 +1,4 @@ +**.swp +node_modules/ +app/js +test/unit/js diff --git a/services/track-changes/app/coffee/ConcatManager.coffee b/services/track-changes/app/coffee/ConcatManager.coffee new file mode 100644 index 0000000000..0dd2790fe8 --- /dev/null +++ b/services/track-changes/app/coffee/ConcatManager.coffee @@ -0,0 +1,121 @@ +strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] +strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] + +module.exports = ConcatManager = + normalizeUpdate: (update) -> + updates = [] + for op in update.op + updates.push + op: [op] + meta: + start_ts: update.meta.ts + end_ts: update.meta.ts + user_id: update.meta.user_id + return updates + + MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 + + concatTwoUpdates: (firstUpdate, secondUpdate) -> + firstUpdate = + op: firstUpdate.op + meta: + user_id: firstUpdate.meta.user_id + start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts + end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts + secondUpdate = + op: secondUpdate.op + meta: + user_id: secondUpdate.meta.user_id + start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts + end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts + + if firstUpdate.meta.user_id != secondUpdate.meta.user_id + return [firstUpdate, secondUpdate] + + if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > ConcatManager.MAX_TIME_BETWEEN_UPDATES + return [firstUpdate, secondUpdate] + + firstOp = firstUpdate.op[0] + secondOp = secondUpdate.op[0] + # Two inserts + if firstOp.i? and secondOp.i? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + return [ + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: [ + p: firstOp.p + i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) + ] + ] + # Two deletes + else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) + return [ + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: [ + p: secondOp.p + d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) + ] + ] + # An insert and then a delete + else if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + offset = secondOp.p - firstOp.p + insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) + if insertedText == secondOp.d + insert = strRemove(firstOp.i, offset, secondOp.d.length) + return [] if insert == "" + return [ + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: [ + p: firstOp.p + i: insert + ] + ] + else + # This shouldn't be possible! + return [firstUpdate, secondUpdate] + else if firstOp.d? and secondOp.i? and firstOp.p == secondOp.p + offset = firstOp.d.indexOf(secondOp.i) + if offset == -1 + return [firstUpdate, secondUpdate] + headD = firstOp.d.slice(0, offset) + inserted = firstOp.d.slice(offset, secondOp.i.length) + tailD = firstOp.d.slice(offset + secondOp.i.length) + headP = firstOp.p + tailP = firstOp.p + secondOp.i.length + updates = [] + if headD != "" + updates.push + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: [ + p: headP + d: headD + ] + if tailD != "" + updates.push + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: [ + p: tailP + d: tailD + ] + if updates.length == 2 + updates[0].meta.start_ts = updates[0].meta.end_ts = firstUpdate.meta.start_ts + updates[1].meta.start_ts = updates[1].meta.end_ts = secondUpdate.meta.end_ts + return updates + + else + return [firstUpdate, secondUpdate] + diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee new file mode 100644 index 0000000000..667f03fcd5 --- /dev/null +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -0,0 +1,8 @@ +Settings = require "settings-sharelatex" +mongojs = require "mongojs" +db = mongojs.connect(Settings.mongo.url, ["docHistory", "docOps"]) +module.exports = + db: db + ObjectId: mongojs.ObjectId + + diff --git a/services/track-changes/compressHistory.coffee b/services/track-changes/compressHistory.coffee new file mode 100644 index 0000000000..ae2cbde219 --- /dev/null +++ b/services/track-changes/compressHistory.coffee @@ -0,0 +1,74 @@ +{db, ObjectId} = require "./app/js/mongojs" +ConcatManager = require "./app/js/ConcatManager" + +doc_id = process.argv.pop() +console.log "DOC ID", doc_id + +OPS_TO_LEAVE = 10 + +removeLatestCompressedUpdate = (doc_id, callback = (error) ->) -> + db.docHistory.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: 1 } }, callback + +getLatestCompressedUpdate = (doc_id, callback = (error) ->) -> + db.docHistory.find { doc_id: ObjectId(doc_id) }, { docOps: { $slice: -1 } }, (error, history) -> + return callback(error) if error? + history = history[0] or { docOps: [] } + callback null, history.docOps.slice(-1)[0] + +insertCompressedUpdates = (doc_id, updates, callback = (error) ->) -> + db.docHistory.update { doc_id: ObjectId(doc_id) }, { $push: { docOps: { $each: updates } } }, { upsert: true }, callback + +trimLastRawUpdate = (doc_id, tailVersion, callback = (error) ->) -> + db.docOps.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: -1 }, $set: { tailVersion: tailVersion + 1 } }, callback + +done = () -> + console.log "DONE! Here's the history:" + db.docHistory.find { doc_id: ObjectId(doc_id) }, (error, docs) -> + throw error if error? + doc = docs[0] + for update in doc.docOps + op = update.op[0] + if op.i? + console.log update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "INSERT", op.p, op.i + else if op.d? + console.log update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "DELETE", op.p, op.d + process.exit() + +do next = () -> + db.docOps.find { doc_id: ObjectId(doc_id) }, { version: true, tailVersion: true, docOps: { $slice: 1 } }, (error, docs) -> + throw error if error? + throw "doc not found" if docs.length < 1 + doc = docs[0] + tailVersion = doc.tailVersion or 0 + version = doc.version + + rawUpdate = doc.docOps[0] + rawUpdates = ConcatManager.normalizeUpdate(rawUpdate) + + if version - tailVersion > OPS_TO_LEAVE + getLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + throw error if error? + if lastCompressedUpdate? + compressedUpdates = [lastCompressedUpdate] + for rawUpdate in rawUpdates + lastCompressedUpdate = compressedUpdates.pop() + compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate + removeLatestCompressedUpdate doc_id, (error) -> + throw error if error? + insertCompressedUpdates doc_id, compressedUpdates, (error) -> + throw error if error? + trimLastRawUpdate doc_id, tailVersion, (error) -> + throw error if error? + console.log "Pushed compressed op" + next() + else + insertCompressedUpdates doc_id, rawUpdates, (error) -> + trimLastRawUpdate doc_id, tailVersion, (error) -> + throw error if error? + console.log "Pushed first op" + next() + else + console.log "Up to date" + done() + + diff --git a/services/track-changes/config/settings.testing.coffee b/services/track-changes/config/settings.testing.coffee new file mode 100755 index 0000000000..528c6a7114 --- /dev/null +++ b/services/track-changes/config/settings.testing.coffee @@ -0,0 +1,3 @@ +module.exports = + mongo: + url: 'mongodb://127.0.0.1/sharelatexTesting' diff --git a/services/track-changes/package.json b/services/track-changes/package.json new file mode 100644 index 0000000000..1a6be78455 --- /dev/null +++ b/services/track-changes/package.json @@ -0,0 +1,11 @@ +{ + "name": "history-sharelatex", + "version": "0.0.1", + "dependencies": { + "chai": "", + "sandboxed-module": "", + "sinon": "", + "mongojs": "0.7.2", + "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master" + } +} diff --git a/services/track-changes/rakefile.rb b/services/track-changes/rakefile.rb new file mode 100644 index 0000000000..6baa462558 --- /dev/null +++ b/services/track-changes/rakefile.rb @@ -0,0 +1,112 @@ +require 'fileutils' + +siteurl = "https://www.sharelatex.com" + +desc "Compile JavaScirpt into CoffeeScript" +namespace 'setup' do + + desc "installes npm packages json and global stuff like less" + task :installDependencys do + sh %{npm install} + sh %{npm install -g coffee-script} + sh %{git submodule init} + sh %{git submodule update} + end +end + + +namespace 'run' do + desc "compiles and runs the javascirpt version of the app" + task :app => ["compile:app"] do + sh %{node app.js | bunyan} do |ok, res| + if ! ok + raise "error compiling app folder tests : #{res}" + end + puts 'finished app compile' + end + end +end + +namespace 'compile' do + desc "compiles main app folder" + task :app do + puts "Compiling app folder to JS" + FileUtils.rm_rf "app/js" + sh %{coffee -c -o app/js/ app/coffee/} do |ok, res| + if ! ok + raise "error compiling app folder tests : #{res}" + end + puts 'finished app compile' + end + end + + desc "compiles unit tests" + task :unittests => ["compile:app"] do + puts "Compiling Unit Tests to JS" + `coffee -c -o test/unit/js/ test/unit/coffee/` + end + + desc "compiles acceptance tests" + task :acceptancetests => ["compile:app"] do + puts "Compiling Acceptance Tests to JS" + sh %{coffee -c -o test/acceptance/js/ test/acceptance/coffee/} do |ok, res| + if ! ok + raise "error compiling acceptance tests: #{res}" + end + end + end +end + +namespace 'test' do + + desc "runs all test" + task :all => ["test:unit", "test:acceptance"] do + puts "testing everything" + end + + desc "Run Acceptance Tests" + task :acceptance => ["compile:acceptancetests"]do + puts "Running Acceptance Tests" + feature = ENV['feature'] + if feature.nil? + featureFlags = "" + else + featureFlags = "-g \"#{feature}\"" + end + sh %{mocha -R spec #{featureFlags} test/acceptance/js/*} do |ok, res| + if ! ok + raise "error running acceptance tests: #{res}" + end + end + end + + desc "run unit tests" + task :unit => ["compile:unittests"]do + puts "Running Unit Tests" + featurePath = ENV['feature'] + puts featurePath + if featurePath.nil? + featurePath = '' + elsif featurePath.include? '/' + elsif !featurePath.include? '/' + featurePath +='/' + else + featurePath = '' + end + + sh %{mocha -R spec test/unit/js/#{featurePath}* --ignore-leaks} do |ok, res| + if ! ok + raise "error running unit tests : #{res}" + end + end + end +end + + +namespace 'deploy' do + desc "safley deploys app" + task :live do + sh %{git push origin} + sh %{cap live deploy} + end +end diff --git a/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee b/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee new file mode 100644 index 0000000000..620813c462 --- /dev/null +++ b/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee @@ -0,0 +1,214 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/ConcatManager.js" +SandboxedModule = require('sandboxed-module') + +describe "ConcatManager", -> + beforeEach -> + @ConcatManager = SandboxedModule.require modulePath + @user_id = "user-id-1" + @other_user_id = "user-id-2" + @ts1 = Date.now() + @ts2 = Date.now() + 1000 + + describe "concatTwoUpdates", -> + describe "insert - insert", -> + it "should append one insert to the other", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 6, i: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, i: "foobar" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should insert one insert inside the other", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 5, i: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, i: "fobaro" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should not append separated inserts", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 9, i: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, i: "foo" ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: [ p: 9, i: "bar" ] + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + describe "delete - delete", -> + it "should append one delete to the other", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, d: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, d: "foobar" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should insert one delete inside the other", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 1, d: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 1, d: "bafoor" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should not append separated deletes", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 9, d: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, d: "foo" ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: [ p: 9, d: "bar" ] + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + describe "insert - delete", -> + it "should undo a previous insert", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 5, d: "o" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, i: "fo" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should remove part of an insert from the middle", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "fobaro" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 5, d: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, i: "foo" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should cancel out two opposite updates", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, d: "foo" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [] + + it "should not combine separated updates", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, i: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 9, d: "bar" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, i: "foo" ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: [ p: 9, d: "bar" ] + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + describe "delete - insert", -> + it "should redo a previous delete at the beginning", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, i: "f" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 4, d: "oo" ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should redo a previous delete from halfway through", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foobar" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, i: "oo" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, d: "f" ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: [ p: 5, d: "bar" ] + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + it "should not combine the ops if the insert text does not match the delete text", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foobar" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, i: "xy" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [{ + op: [ p: 3, d: "foobar" ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, i: "xy" ] + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + it "should cancel two equal updates", -> + expect(@ConcatManager.concatTwoUpdates({ + op: [ p: 3, d: "foo" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, i: "foo" ] + meta: ts: @ts2, user_id: @user_id + })) + .to.deep.equal [] + + + + From c5e7f14ba1af55b9244012bf7c149988315fe3f4 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 11:10:19 +0100 Subject: [PATCH 002/549] Factor methods out into ConversionManager --- .../app/coffee/ConversionManager.coffee | 62 +++++++++++++++++++ services/track-changes/compressHistory.coffee | 58 ++--------------- 2 files changed, 68 insertions(+), 52 deletions(-) create mode 100644 services/track-changes/app/coffee/ConversionManager.coffee diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee new file mode 100644 index 0000000000..5e450fc01d --- /dev/null +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -0,0 +1,62 @@ +{db, ObjectId} = require "./mongojs" +ConcatManager = require "./ConcatManager" + +module.exports = ConversionManager = + OPS_TO_LEAVE: 10 + + removeLatestCompressedUpdate: (doc_id, callback = (error) ->) -> + db.docHistory.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: 1 } }, callback + + getLatestCompressedUpdate: (doc_id, callback = (error) ->) -> + db.docHistory.find { doc_id: ObjectId(doc_id) }, { docOps: { $slice: -1 } }, (error, history) -> + return callback(error) if error? + history = history[0] or { docOps: [] } + callback null, history.docOps.slice(-1)[0] + + insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> + db.docHistory.update { doc_id: ObjectId(doc_id) }, { $push: { docOps: { $each: updates } } }, { upsert: true }, callback + + trimLastRawUpdate: (doc_id, tailVersion, callback = (error) ->) -> + db.docOps.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: -1 }, $set: { tailVersion: tailVersion + 1 } }, callback + + getLastRawUpdateAndVersion: (doc_id, callback = (error, update, currentVersion, tailVersion) ->) -> + db.docOps.find { doc_id: ObjectId(doc_id) }, { version: true, tailVersion: true, docOps: { $slice: 1 } }, (error, docs) -> + return callback(error) if error? + return callback(new Error("doc not found")) if docs.length == 0 + doc = docs[0] + callback null, doc.docOps[0], doc.version, doc.tailVersion or 0 + + convertOldestRawUpdate: (doc_id, callback = (error, converted) ->) -> + ConversionManager.getLastRawUpdateAndVersion doc_id, (error, rawUpdate, currentVersion, tailVersion) -> + return callback(error) if error? + + rawUpdates = ConcatManager.normalizeUpdate(rawUpdate) + + if currentVersion - tailVersion > ConcatManager.OPS_TO_LEAVE + ConversonManager.getLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + return callback(error) if error? + + removeAndModifyPreviousCompressedUpdate = (callback, compressedUpdates) -> + if lastCompressedUpdate? + compressedUpdates = [lastCompressedUpdate] + for rawUpdate in rawUpdates + lastCompressedUpdate = compressedUpdates.pop() + compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate + ConversionManager.removeLatestCompressedUpdate doc_id, (error) -> + return callback(error) if error? + callback null, compressUpdates + else + callback null, rawUpdates + + removeAndModifyPreviousCompressedUpdate (error, newCompressedUpdates) -> + return callback(error) if error? + ConversionManager.insertCompressedUpdates doc_id, newCompressedUpdates, (error) -> + return callback(error) if error? + ConversionManager.trimLastRawUpdate doc_id, tailVersion, (error) -> + return callback(error) if error? + console.log "Pushed op", tailVersion + callback null, true + + else + console.log "Up to date" + callback null, false diff --git a/services/track-changes/compressHistory.coffee b/services/track-changes/compressHistory.coffee index ae2cbde219..d3ed3eae58 100644 --- a/services/track-changes/compressHistory.coffee +++ b/services/track-changes/compressHistory.coffee @@ -1,26 +1,9 @@ -{db, ObjectId} = require "./app/js/mongojs" -ConcatManager = require "./app/js/ConcatManager" +{db, ObjectId} = require "./app/coffee/mongojs" +ConversionManager = require "./app/coffee/ConversionManager" doc_id = process.argv.pop() console.log "DOC ID", doc_id -OPS_TO_LEAVE = 10 - -removeLatestCompressedUpdate = (doc_id, callback = (error) ->) -> - db.docHistory.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: 1 } }, callback - -getLatestCompressedUpdate = (doc_id, callback = (error) ->) -> - db.docHistory.find { doc_id: ObjectId(doc_id) }, { docOps: { $slice: -1 } }, (error, history) -> - return callback(error) if error? - history = history[0] or { docOps: [] } - callback null, history.docOps.slice(-1)[0] - -insertCompressedUpdates = (doc_id, updates, callback = (error) ->) -> - db.docHistory.update { doc_id: ObjectId(doc_id) }, { $push: { docOps: { $each: updates } } }, { upsert: true }, callback - -trimLastRawUpdate = (doc_id, tailVersion, callback = (error) ->) -> - db.docOps.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: -1 }, $set: { tailVersion: tailVersion + 1 } }, callback - done = () -> console.log "DONE! Here's the history:" db.docHistory.find { doc_id: ObjectId(doc_id) }, (error, docs) -> @@ -35,40 +18,11 @@ done = () -> process.exit() do next = () -> - db.docOps.find { doc_id: ObjectId(doc_id) }, { version: true, tailVersion: true, docOps: { $slice: 1 } }, (error, docs) -> + ConversionManager.convertOldestRawUpdate doc_id, (error, converted) -> throw error if error? - throw "doc not found" if docs.length < 1 - doc = docs[0] - tailVersion = doc.tailVersion or 0 - version = doc.version - - rawUpdate = doc.docOps[0] - rawUpdates = ConcatManager.normalizeUpdate(rawUpdate) - - if version - tailVersion > OPS_TO_LEAVE - getLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> - throw error if error? - if lastCompressedUpdate? - compressedUpdates = [lastCompressedUpdate] - for rawUpdate in rawUpdates - lastCompressedUpdate = compressedUpdates.pop() - compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate - removeLatestCompressedUpdate doc_id, (error) -> - throw error if error? - insertCompressedUpdates doc_id, compressedUpdates, (error) -> - throw error if error? - trimLastRawUpdate doc_id, tailVersion, (error) -> - throw error if error? - console.log "Pushed compressed op" - next() - else - insertCompressedUpdates doc_id, rawUpdates, (error) -> - trimLastRawUpdate doc_id, tailVersion, (error) -> - throw error if error? - console.log "Pushed first op" - next() + if converted + next() else - console.log "Up to date" done() - + From 64eb794c02441efd7c601853faba7b46c272d85a Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 11:36:19 +0100 Subject: [PATCH 003/549] Compress all docs using compressHistory script --- .../app/coffee/ConversionManager.coffee | 22 ++++++--- services/track-changes/compressHistory.coffee | 46 ++++++++++--------- services/track-changes/package.json | 1 + 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index 5e450fc01d..d7849818c0 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -30,10 +30,9 @@ module.exports = ConversionManager = ConversionManager.getLastRawUpdateAndVersion doc_id, (error, rawUpdate, currentVersion, tailVersion) -> return callback(error) if error? - rawUpdates = ConcatManager.normalizeUpdate(rawUpdate) - - if currentVersion - tailVersion > ConcatManager.OPS_TO_LEAVE - ConversonManager.getLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + if currentVersion - tailVersion > ConversionManager.OPS_TO_LEAVE + rawUpdates = ConcatManager.normalizeUpdate(rawUpdate) + ConversionManager.getLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> return callback(error) if error? removeAndModifyPreviousCompressedUpdate = (callback, compressedUpdates) -> @@ -44,7 +43,7 @@ module.exports = ConversionManager = compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate ConversionManager.removeLatestCompressedUpdate doc_id, (error) -> return callback(error) if error? - callback null, compressUpdates + callback null, compressedUpdates else callback null, rawUpdates @@ -54,9 +53,18 @@ module.exports = ConversionManager = return callback(error) if error? ConversionManager.trimLastRawUpdate doc_id, tailVersion, (error) -> return callback(error) if error? - console.log "Pushed op", tailVersion + console.log doc_id, "Pushed op", tailVersion callback null, true else - console.log "Up to date" + console.log doc_id, "Up to date" callback null, false + + convertAllOldRawUpdates: (doc_id, callback = (error) ->) -> + ConversionManager.convertOldestRawUpdate doc_id, (error, converted) -> + return callback(error) if error? + if converted + # Keep going + ConversionManager.convertAllOldRawUpdates doc_id, callback + else + callback() diff --git a/services/track-changes/compressHistory.coffee b/services/track-changes/compressHistory.coffee index d3ed3eae58..95679a4dcd 100644 --- a/services/track-changes/compressHistory.coffee +++ b/services/track-changes/compressHistory.coffee @@ -1,28 +1,32 @@ {db, ObjectId} = require "./app/coffee/mongojs" ConversionManager = require "./app/coffee/ConversionManager" +async = require "async" -doc_id = process.argv.pop() -console.log "DOC ID", doc_id - -done = () -> - console.log "DONE! Here's the history:" - db.docHistory.find { doc_id: ObjectId(doc_id) }, (error, docs) -> +db.docOps.find { }, { doc_id: true }, (error, docs) -> + throw error if error? + jobs = [] + for doc in docs + do (doc) -> + jobs.push (callback) -> + doc_id = doc.doc_id.toString() + ConversionManager.convertAllOldRawUpdates doc_id, (error) -> + return callback(error) if error? + console.log doc_id, "DONE" + db.docHistory.find { doc_id: ObjectId(doc_id) }, (error, docs) -> + return callback(error) if error? + doc = docs[0] + if doc? + for update in doc.docOps + op = update.op[0] + if op.i? + console.log doc_id, update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "INSERT", op.p, op.i + else if op.d? + console.log doc_id, update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "DELETE", op.p, op.d + else + console.log doc_id, "NO HISTORY" + callback() + async.series jobs, (error) -> throw error if error? - doc = docs[0] - for update in doc.docOps - op = update.op[0] - if op.i? - console.log update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "INSERT", op.p, op.i - else if op.d? - console.log update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "DELETE", op.p, op.d process.exit() - -do next = () -> - ConversionManager.convertOldestRawUpdate doc_id, (error, converted) -> - throw error if error? - if converted - next() - else - done() diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 1a6be78455..5698e2dd3b 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -2,6 +2,7 @@ "name": "history-sharelatex", "version": "0.0.1", "dependencies": { + "async": "", "chai": "", "sandboxed-module": "", "sinon": "", From 54c795964726f7908f12e5a895c84756aedd9e03 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 12:26:23 +0100 Subject: [PATCH 004/549] Match if both user ids are null/undefined --- services/track-changes/app/coffee/ConcatManager.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/ConcatManager.coffee b/services/track-changes/app/coffee/ConcatManager.coffee index 0dd2790fe8..622616b1cb 100644 --- a/services/track-changes/app/coffee/ConcatManager.coffee +++ b/services/track-changes/app/coffee/ConcatManager.coffee @@ -19,13 +19,13 @@ module.exports = ConcatManager = firstUpdate = op: firstUpdate.op meta: - user_id: firstUpdate.meta.user_id + user_id: firstUpdate.meta.user_id or null start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts secondUpdate = op: secondUpdate.op meta: - user_id: secondUpdate.meta.user_id + user_id: secondUpdate.meta.user_id or null start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts From 3ab45c2a35044ce036041990665912461aca7304 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 12:36:17 +0100 Subject: [PATCH 005/549] Handle if two updates cancel each other --- services/track-changes/app/coffee/ConversionManager.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index d7849818c0..fef745b220 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -40,7 +40,10 @@ module.exports = ConversionManager = compressedUpdates = [lastCompressedUpdate] for rawUpdate in rawUpdates lastCompressedUpdate = compressedUpdates.pop() - compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate + if lastCompressedUpdate? + compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate + else + compressedUpdates.push rawUpdate ConversionManager.removeLatestCompressedUpdate doc_id, (error) -> return callback(error) if error? callback null, compressedUpdates From fe503010aaee6efb1666f8febd077792f0363505 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 13:27:35 +0100 Subject: [PATCH 006/549] Pop and set history in batches for speed --- .../app/coffee/ConversionManager.coffee | 106 +++++++++--------- services/track-changes/compressHistory.coffee | 8 +- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index fef745b220..5dca40a476 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -4,70 +4,70 @@ ConcatManager = require "./ConcatManager" module.exports = ConversionManager = OPS_TO_LEAVE: 10 - removeLatestCompressedUpdate: (doc_id, callback = (error) ->) -> - db.docHistory.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: 1 } }, callback - - getLatestCompressedUpdate: (doc_id, callback = (error) ->) -> - db.docHistory.find { doc_id: ObjectId(doc_id) }, { docOps: { $slice: -1 } }, (error, history) -> + popLatestCompressedUpdate: (doc_id, callback = (error, update) ->) -> + db.docHistory.findAndModify + query: { doc_id: ObjectId(doc_id) } + fields: { docOps: { $slice: -1 } } + update: { $pop: { docOps: 1 } } + , (error, history = { docOps: [] }) -> return callback(error) if error? - history = history[0] or { docOps: [] } - callback null, history.docOps.slice(-1)[0] + callback null, history.docOps[0] insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> db.docHistory.update { doc_id: ObjectId(doc_id) }, { $push: { docOps: { $each: updates } } }, { upsert: true }, callback - trimLastRawUpdate: (doc_id, tailVersion, callback = (error) ->) -> - db.docOps.update { doc_id: ObjectId(doc_id) }, { $pop: { docOps: -1 }, $set: { tailVersion: tailVersion + 1 } }, callback - - getLastRawUpdateAndVersion: (doc_id, callback = (error, update, currentVersion, tailVersion) ->) -> - db.docOps.find { doc_id: ObjectId(doc_id) }, { version: true, tailVersion: true, docOps: { $slice: 1 } }, (error, docs) -> + popOldRawUpdates: (doc_id, callback = (error, updates) ->) -> + db.docOps.find { doc_id: ObjectId(doc_id) }, { version: true, tailVersion: true }, (error, docs) -> return callback(error) if error? return callback(new Error("doc not found")) if docs.length == 0 doc = docs[0] - callback null, doc.docOps[0], doc.version, doc.tailVersion or 0 - - convertOldestRawUpdate: (doc_id, callback = (error, converted) ->) -> - ConversionManager.getLastRawUpdateAndVersion doc_id, (error, rawUpdate, currentVersion, tailVersion) -> - return callback(error) if error? - + currentVersion = doc.version + tailVersion = doc.tailVersion or 0 if currentVersion - tailVersion > ConversionManager.OPS_TO_LEAVE - rawUpdates = ConcatManager.normalizeUpdate(rawUpdate) - ConversionManager.getLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + db.docOps.findAndModify + query: { doc_id: ObjectId(doc_id), version: currentVersion } + update: { + $push: { docOps: { $each: [], $slice: - ConversionManager.OPS_TO_LEAVE } } + $set: tailVersion: currentVersion - ConversionManager.OPS_TO_LEAVE, + } + fields: { docOps: $slice: currentVersion - tailVersion - ConversionManager.OPS_TO_LEAVE } + , (error, doc) -> return callback(error) if error? - - removeAndModifyPreviousCompressedUpdate = (callback, compressedUpdates) -> - if lastCompressedUpdate? - compressedUpdates = [lastCompressedUpdate] - for rawUpdate in rawUpdates - lastCompressedUpdate = compressedUpdates.pop() - if lastCompressedUpdate? - compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate - else - compressedUpdates.push rawUpdate - ConversionManager.removeLatestCompressedUpdate doc_id, (error) -> - return callback(error) if error? - callback null, compressedUpdates - else - callback null, rawUpdates + if !doc? + # Version was modified since so try again + return ConversionManager.popOldRawUpdates doc_id, callback + else + return callback null, doc.docOps - removeAndModifyPreviousCompressedUpdate (error, newCompressedUpdates) -> - return callback(error) if error? - ConversionManager.insertCompressedUpdates doc_id, newCompressedUpdates, (error) -> - return callback(error) if error? - ConversionManager.trimLastRawUpdate doc_id, tailVersion, (error) -> - return callback(error) if error? - console.log doc_id, "Pushed op", tailVersion - callback null, true - else - console.log doc_id, "Up to date" - callback null, false + callback null, [] - convertAllOldRawUpdates: (doc_id, callback = (error) ->) -> - ConversionManager.convertOldestRawUpdate doc_id, (error, converted) -> + convertOldRawUpdates: (doc_id, callback = (error) ->) -> + ConversionManager.popOldRawUpdates doc_id, (error, rawUpdates) -> return callback(error) if error? - if converted - # Keep going - ConversionManager.convertAllOldRawUpdates doc_id, callback - else - callback() + + length = rawUpdates.length + + normalizedRawUpdates = [] + for rawUpdate in rawUpdates + normalizedRawUpdates = normalizedRawUpdates.concat ConcatManager.normalizeUpdate(rawUpdate) + rawUpdates = normalizedRawUpdates + + ConversionManager.popLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + return callback(error) if error? + + if !lastCompressedUpdate? + lastCompressedUpdate = rawUpdates.shift() + + compressedUpdates = [lastCompressedUpdate] + for rawUpdate in rawUpdates + lastCompressedUpdate = compressedUpdates.pop() + if lastCompressedUpdate? + compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate + else + compressedUpdates.push rawUpdate + ConversionManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> + return callback(error) if error? + console.log doc_id, "Pushed doc ops", length + callback null, true + diff --git a/services/track-changes/compressHistory.coffee b/services/track-changes/compressHistory.coffee index 95679a4dcd..c76988526f 100644 --- a/services/track-changes/compressHistory.coffee +++ b/services/track-changes/compressHistory.coffee @@ -9,7 +9,7 @@ db.docOps.find { }, { doc_id: true }, (error, docs) -> do (doc) -> jobs.push (callback) -> doc_id = doc.doc_id.toString() - ConversionManager.convertAllOldRawUpdates doc_id, (error) -> + ConversionManager.convertOldRawUpdates doc_id, (error) -> return callback(error) if error? console.log doc_id, "DONE" db.docHistory.find { doc_id: ObjectId(doc_id) }, (error, docs) -> @@ -17,10 +17,10 @@ db.docOps.find { }, { doc_id: true }, (error, docs) -> doc = docs[0] if doc? for update in doc.docOps - op = update.op[0] - if op.i? + op = update?.op[0] + if op?.i? console.log doc_id, update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "INSERT", op.p, op.i - else if op.d? + else if op?.d? console.log doc_id, update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "DELETE", op.p, op.d else console.log doc_id, "NO HISTORY" From 07f078b5276eb9ef9773abc4dd1259aae6a345a1 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 13:46:03 +0100 Subject: [PATCH 007/549] Don't insert a null update if there are no updates or raw updates --- services/track-changes/app/coffee/ConversionManager.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index 5dca40a476..2821c057be 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -59,6 +59,10 @@ module.exports = ConversionManager = if !lastCompressedUpdate? lastCompressedUpdate = rawUpdates.shift() + if !lastCompressedUpdate? + # No saved versions, no raw updates, nothing to do + callback() + compressedUpdates = [lastCompressedUpdate] for rawUpdate in rawUpdates lastCompressedUpdate = compressedUpdates.pop() @@ -69,5 +73,5 @@ module.exports = ConversionManager = ConversionManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> return callback(error) if error? console.log doc_id, "Pushed doc ops", length - callback null, true + callback() From 7a3b78da43a28ebe524e90f4a4f0f8dafbcf9fa0 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 13:51:02 +0100 Subject: [PATCH 008/549] Return when calling callback --- services/track-changes/app/coffee/ConversionManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index 2821c057be..8ffcb54dfd 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -61,7 +61,7 @@ module.exports = ConversionManager = if !lastCompressedUpdate? # No saved versions, no raw updates, nothing to do - callback() + return callback() compressedUpdates = [lastCompressedUpdate] for rawUpdate in rawUpdates From bef05c31f4b00852272cca44af25a06ba3f94e4e Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 Aug 2013 13:54:30 +0100 Subject: [PATCH 009/549] No need to print the entire history each time --- services/track-changes/compressHistory.coffee | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/services/track-changes/compressHistory.coffee b/services/track-changes/compressHistory.coffee index c76988526f..1ce1dad1be 100644 --- a/services/track-changes/compressHistory.coffee +++ b/services/track-changes/compressHistory.coffee @@ -12,19 +12,7 @@ db.docOps.find { }, { doc_id: true }, (error, docs) -> ConversionManager.convertOldRawUpdates doc_id, (error) -> return callback(error) if error? console.log doc_id, "DONE" - db.docHistory.find { doc_id: ObjectId(doc_id) }, (error, docs) -> - return callback(error) if error? - doc = docs[0] - if doc? - for update in doc.docOps - op = update?.op[0] - if op?.i? - console.log doc_id, update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "INSERT", op.p, op.i - else if op?.d? - console.log doc_id, update.meta.start_ts, update.meta.end_ts, update.meta.user_id, "DELETE", op.p, op.d - else - console.log doc_id, "NO HISTORY" - callback() + callback() async.series jobs, (error) -> throw error if error? process.exit() From dbee4a57fb6fe45a3a6b51dd0b48307d75087391 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 23 Aug 2013 14:35:13 +0100 Subject: [PATCH 010/549] Compress doc ops in two passes --- services/track-changes/app.coffee | 19 +++++ .../app/coffee/ConcatManager.coffee | 51 ++++++++++- .../app/coffee/ConversionManager.coffee | 34 ++++---- services/track-changes/package.json | 4 +- .../ConcatManager/ConcatManagerTests.coffee | 84 ++++++++++++------- 5 files changed, 145 insertions(+), 47 deletions(-) create mode 100644 services/track-changes/app.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee new file mode 100644 index 0000000000..7398ca8252 --- /dev/null +++ b/services/track-changes/app.coffee @@ -0,0 +1,19 @@ +express = require "express" +app = express() + +ConversoinManager = require "./app/js/ConversionManager" +logger = require "logger-sharelatex" +logger.initialize("history") + +app.post "/doc/:doc_id/flush", (req, res, next) -> + project_id = req.params.project_id + logger.log doc_id: doc_id, "compressing doc history" + ConversionManager.convertOldRawUpdates doc_id, (error) -> + return next(error) if error? + res.send 204 # No content + +app.use (error, req, res, next) -> + logger.error err: error, "an internal error occured" + req.send 500 + +app.listen(3014) diff --git a/services/track-changes/app/coffee/ConcatManager.coffee b/services/track-changes/app/coffee/ConcatManager.coffee index 622616b1cb..67fc387ab8 100644 --- a/services/track-changes/app/coffee/ConcatManager.coffee +++ b/services/track-changes/app/coffee/ConcatManager.coffee @@ -13,9 +13,30 @@ module.exports = ConcatManager = user_id: update.meta.user_id return updates + compressUpdates: (rawUpdates) -> + return [] if rawUpdates.length == 0 + firstPass = [rawUpdates.shift()] + for update in rawUpdates + lastCompressedUpdate = firstPass.pop() + if lastCompressedUpdate? + firstPass = firstPass.concat ConcatManager._concatTwoUpdatesOfTheSameType lastCompressedUpdate, update + else + firstPass.push rawUpdate + + return [] if firstPass.length == 0 + secondPass = [firstPass.shift()] + for update in firstPass + lastCompressedUpdate = secondPass.pop() + if lastCompressedUpdate? + secondPass = secondPass.concat ConcatManager._cancelOppositeInsertsAndDeletes lastCompressedUpdate, update + else + secondPass.push update + + return secondPass + MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 - concatTwoUpdates: (firstUpdate, secondUpdate) -> + _concatTwoUpdatesOfTheSameType: (firstUpdate, secondUpdate) -> firstUpdate = op: firstUpdate.op meta: @@ -61,8 +82,34 @@ module.exports = ConcatManager = d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) ] ] + else + return [firstUpdate, secondUpdate] + + _cancelOppositeInsertsAndDeletes: (firstUpdate, secondUpdate) -> + firstUpdate = + op: firstUpdate.op + meta: + user_id: firstUpdate.meta.user_id or null + start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts + end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts + secondUpdate = + op: secondUpdate.op + meta: + user_id: secondUpdate.meta.user_id or null + start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts + end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts + + if firstUpdate.meta.user_id != secondUpdate.meta.user_id + return [firstUpdate, secondUpdate] + + if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > ConcatManager.MAX_TIME_BETWEEN_UPDATES + return [firstUpdate, secondUpdate] + + firstOp = firstUpdate.op[0] + secondOp = secondUpdate.op[0] + # An insert and then a delete - else if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) offset = secondOp.p - firstOp.p insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) if insertedText == secondOp.d diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index 8ffcb54dfd..188339d150 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -1,8 +1,9 @@ {db, ObjectId} = require "./mongojs" ConcatManager = require "./ConcatManager" +logger = require "logger-sharelatex" module.exports = ConversionManager = - OPS_TO_LEAVE: 10 + OPS_TO_LEAVE: 100 popLatestCompressedUpdate: (doc_id, callback = (error, update) ->) -> db.docHistory.findAndModify @@ -25,12 +26,19 @@ module.exports = ConversionManager = tailVersion = doc.tailVersion or 0 if currentVersion - tailVersion > ConversionManager.OPS_TO_LEAVE db.docOps.findAndModify - query: { doc_id: ObjectId(doc_id), version: currentVersion } - update: { - $push: { docOps: { $each: [], $slice: - ConversionManager.OPS_TO_LEAVE } } - $set: tailVersion: currentVersion - ConversionManager.OPS_TO_LEAVE, - } - fields: { docOps: $slice: currentVersion - tailVersion - ConversionManager.OPS_TO_LEAVE } + query: + doc_id: ObjectId(doc_id) + version: currentVersion + update: + $push: + docOps: + $each: [] + $slice: - ConversionManager.OPS_TO_LEAVE + $set: + tailVersion: currentVersion - ConversionManager.OPS_TO_LEAVE + fields: + docOps: + $slice: currentVersion - tailVersion - ConversionManager.OPS_TO_LEAVE , (error, doc) -> return callback(error) if error? if !doc? @@ -63,15 +71,11 @@ module.exports = ConversionManager = # No saved versions, no raw updates, nothing to do return callback() - compressedUpdates = [lastCompressedUpdate] - for rawUpdate in rawUpdates - lastCompressedUpdate = compressedUpdates.pop() - if lastCompressedUpdate? - compressedUpdates = compressedUpdates.concat ConcatManager.concatTwoUpdates lastCompressedUpdate, rawUpdate - else - compressedUpdates.push rawUpdate + uncompressedUpdates = [lastCompressedUpdate].concat rawUpdates + compressedUpdates = ConcatManager.compressUpdates uncompressedUpdates + ConversionManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> return callback(error) if error? - console.log doc_id, "Pushed doc ops", length + logger.log doc_id: doc_id, rawOpsLength: length, compressedOpsLength: compressedUpdates.length, "compressed doc ops" callback() diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 5698e2dd3b..e1a8dba4a6 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -4,9 +4,11 @@ "dependencies": { "async": "", "chai": "", + "express": "3.3.5", "sandboxed-module": "", "sinon": "", "mongojs": "0.7.2", - "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master" + "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master", + "logger": "git+ssh://git@bitbucket.org:sharelatex/logger-sharelatex.git#bunyan" } } diff --git a/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee b/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee index 620813c462..93909bc99c 100644 --- a/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee @@ -13,42 +13,42 @@ describe "ConcatManager", -> @ts1 = Date.now() @ts2 = Date.now() + 1000 - describe "concatTwoUpdates", -> + describe "compress", -> describe "insert - insert", -> it "should append one insert to the other", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 6, i: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, i: "foobar" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should insert one insert inside the other", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 5, i: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, i: "fobaro" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should not append separated inserts", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 9, i: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, i: "foo" ] meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id @@ -59,39 +59,39 @@ describe "ConcatManager", -> describe "delete - delete", -> it "should append one delete to the other", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 3, d: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, d: "foobar" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should insert one delete inside the other", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 1, d: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 1, d: "bafoor" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should not append separated deletes", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 9, d: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, d: "foo" ] meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id @@ -102,49 +102,49 @@ describe "ConcatManager", -> describe "insert - delete", -> it "should undo a previous insert", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 5, d: "o" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, i: "fo" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should remove part of an insert from the middle", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "fobaro" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 5, d: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, i: "foo" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should cancel out two opposite updates", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 3, d: "foo" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [] it "should not combine separated updates", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 9, d: "bar" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, i: "foo" ] meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id @@ -155,26 +155,26 @@ describe "ConcatManager", -> describe "delete - insert", -> it "should redo a previous delete at the beginning", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 3, i: "f" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 4, d: "oo" ] meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id }] it "should redo a previous delete from halfway through", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foobar" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 3, i: "oo" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, d: "f" ] meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id @@ -183,14 +183,40 @@ describe "ConcatManager", -> meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id }] + it "should keep words together", -> + expect(@ConcatManager.compressUpdates [{ + op: [ p: 3, d: "abcdefghijklmnopqrstuvwxyz hello world" ] + meta: ts: @ts1, user_id: @user_id + }, { + op: [ p: 3, i: "w" ] + meta: ts: @ts2, user_id: @user_id + }, { + op: [ p: 4, i: "o" ] + meta: ts: @ts2, user_id: @user_id + }, { + op: [ p: 5, i: "r" ] + meta: ts: @ts2, user_id: @user_id + }, { + op: [ p: 6, i: "l" ] + meta: ts: @ts2, user_id: @user_id + }, { + op: [ p: 7, i: "d" ] + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: [ p: 3, d: "abcdefghijklmnopqrstuvwxyz hello " ] + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should not combine the ops if the insert text does not match the delete text", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foobar" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 3, i: "xy" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [{ op: [ p: 3, d: "foobar" ] meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id @@ -200,13 +226,13 @@ describe "ConcatManager", -> }] it "should cancel two equal updates", -> - expect(@ConcatManager.concatTwoUpdates({ + expect(@ConcatManager.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { op: [ p: 3, i: "foo" ] meta: ts: @ts2, user_id: @user_id - })) + }]) .to.deep.equal [] From dab2781e87abefe142b1f3d5f284407dec9baaab Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 23 Aug 2013 14:58:00 +0100 Subject: [PATCH 011/549] Do two passes better --- .../app/coffee/ConcatManager.coffee | 36 +++---------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/services/track-changes/app/coffee/ConcatManager.coffee b/services/track-changes/app/coffee/ConcatManager.coffee index 67fc387ab8..d56a53c811 100644 --- a/services/track-changes/app/coffee/ConcatManager.coffee +++ b/services/track-changes/app/coffee/ConcatManager.coffee @@ -19,7 +19,7 @@ module.exports = ConcatManager = for update in rawUpdates lastCompressedUpdate = firstPass.pop() if lastCompressedUpdate? - firstPass = firstPass.concat ConcatManager._concatTwoUpdatesOfTheSameType lastCompressedUpdate, update + firstPass = firstPass.concat ConcatManager._concatTwoUpdates lastCompressedUpdate, update, false else firstPass.push rawUpdate @@ -28,7 +28,7 @@ module.exports = ConcatManager = for update in firstPass lastCompressedUpdate = secondPass.pop() if lastCompressedUpdate? - secondPass = secondPass.concat ConcatManager._cancelOppositeInsertsAndDeletes lastCompressedUpdate, update + secondPass = secondPass.concat ConcatManager._concatTwoUpdates lastCompressedUpdate, update, true else secondPass.push update @@ -36,7 +36,7 @@ module.exports = ConcatManager = MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 - _concatTwoUpdatesOfTheSameType: (firstUpdate, secondUpdate) -> + _concatTwoUpdates: (firstUpdate, secondUpdate, mergeInsertsAndDeletes) -> firstUpdate = op: firstUpdate.op meta: @@ -82,34 +82,8 @@ module.exports = ConcatManager = d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) ] ] - else - return [firstUpdate, secondUpdate] - - _cancelOppositeInsertsAndDeletes: (firstUpdate, secondUpdate) -> - firstUpdate = - op: firstUpdate.op - meta: - user_id: firstUpdate.meta.user_id or null - start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts - end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts - secondUpdate = - op: secondUpdate.op - meta: - user_id: secondUpdate.meta.user_id or null - start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts - end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts - - if firstUpdate.meta.user_id != secondUpdate.meta.user_id - return [firstUpdate, secondUpdate] - - if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > ConcatManager.MAX_TIME_BETWEEN_UPDATES - return [firstUpdate, secondUpdate] - - firstOp = firstUpdate.op[0] - secondOp = secondUpdate.op[0] - # An insert and then a delete - if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + if mergeInsertsAndDeletes and firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) offset = secondOp.p - firstOp.p insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) if insertedText == secondOp.d @@ -128,7 +102,7 @@ module.exports = ConcatManager = else # This shouldn't be possible! return [firstUpdate, secondUpdate] - else if firstOp.d? and secondOp.i? and firstOp.p == secondOp.p + else if mergeInsertsAndDeletes and firstOp.d? and secondOp.i? and firstOp.p == secondOp.p offset = firstOp.d.indexOf(secondOp.i) if offset == -1 return [firstUpdate, secondUpdate] From 8a0aa55c9194376770252ddab423788bf82691d6 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 3 Sep 2013 17:17:27 +0100 Subject: [PATCH 012/549] Refactor to take doc ops from an HTTP request from doc updater --- services/track-changes/app.coffee | 5 +- .../app/coffee/ConversionManager.coffee | 85 ++++++------------ ...atManager.coffee => HistoryBuilder.coffee} | 20 +++-- .../ConversionManagerTests.coffee | 87 +++++++++++++++++++ .../HistoryBuilderTests.coffee} | 36 ++++---- 5 files changed, 148 insertions(+), 85 deletions(-) rename services/track-changes/app/coffee/{ConcatManager.coffee => HistoryBuilder.coffee} (85%) create mode 100644 services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee rename services/track-changes/test/unit/coffee/{ConcatManager/ConcatManagerTests.coffee => HistoryBuilder/HistoryBuilderTests.coffee} (87%) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 7398ca8252..85aa7ced6b 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -5,10 +5,11 @@ ConversoinManager = require "./app/js/ConversionManager" logger = require "logger-sharelatex" logger.initialize("history") -app.post "/doc/:doc_id/flush", (req, res, next) -> +app.post express.bodyParser(), "/doc/:doc_id/flush", (req, res, next) -> project_id = req.params.project_id + docOps = req.body.docOps logger.log doc_id: doc_id, "compressing doc history" - ConversionManager.convertOldRawUpdates doc_id, (error) -> + ConversionManager.convertAndSaveRawOps doc_id, docOps, (error) -> return next(error) if error? res.send 204 # No content diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee index 188339d150..2db3aaad51 100644 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ b/services/track-changes/app/coffee/ConversionManager.coffee @@ -1,11 +1,11 @@ {db, ObjectId} = require "./mongojs" -ConcatManager = require "./ConcatManager" +HistoryBuilder = require "./HistoryBuilder" logger = require "logger-sharelatex" module.exports = ConversionManager = OPS_TO_LEAVE: 100 - popLatestCompressedUpdate: (doc_id, callback = (error, update) ->) -> + popLastCompressedOp: (doc_id, callback = (error, op) ->) -> db.docHistory.findAndModify query: { doc_id: ObjectId(doc_id) } fields: { docOps: { $slice: -1 } } @@ -14,68 +14,35 @@ module.exports = ConversionManager = return callback(error) if error? callback null, history.docOps[0] - insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> - db.docHistory.update { doc_id: ObjectId(doc_id) }, { $push: { docOps: { $each: updates } } }, { upsert: true }, callback + insertCompressedOps: (doc_id, docOps, callback = (error) ->) -> + db.docHistory.update { + doc_id: ObjectId(doc_id) + }, { + $push: + docOps: + $each: docOps + }, { + upsert: true + }, callback - popOldRawUpdates: (doc_id, callback = (error, updates) ->) -> - db.docOps.find { doc_id: ObjectId(doc_id) }, { version: true, tailVersion: true }, (error, docs) -> - return callback(error) if error? - return callback(new Error("doc not found")) if docs.length == 0 - doc = docs[0] - currentVersion = doc.version - tailVersion = doc.tailVersion or 0 - if currentVersion - tailVersion > ConversionManager.OPS_TO_LEAVE - db.docOps.findAndModify - query: - doc_id: ObjectId(doc_id) - version: currentVersion - update: - $push: - docOps: - $each: [] - $slice: - ConversionManager.OPS_TO_LEAVE - $set: - tailVersion: currentVersion - ConversionManager.OPS_TO_LEAVE - fields: - docOps: - $slice: currentVersion - tailVersion - ConversionManager.OPS_TO_LEAVE - , (error, doc) -> - return callback(error) if error? - if !doc? - # Version was modified since so try again - return ConversionManager.popOldRawUpdates doc_id, callback - else - return callback null, doc.docOps - - else - callback null, [] + convertAndSaveRawOps: (doc_id, rawOps, callback = (error) ->) -> + length = rawOps.length + if length == 0 + return callback() - convertOldRawUpdates: (doc_id, callback = (error) ->) -> - ConversionManager.popOldRawUpdates doc_id, (error, rawUpdates) -> + + ConversionManager.popLastCompressedOp doc_id, (error, lastCompressedOp) -> return callback(error) if error? - length = rawUpdates.length + if !lastCompressedOp? + rawOps = rawOps.slice(0) # Clone so we can modify in place + lastCompressedOp = rawOps.shift() - normalizedRawUpdates = [] - for rawUpdate in rawUpdates - normalizedRawUpdates = normalizedRawUpdates.concat ConcatManager.normalizeUpdate(rawUpdate) - rawUpdates = normalizedRawUpdates + uncompressedOps = [lastCompressedOp].concat rawOps + compressedOps = HistoryBuilder.compressOps uncompressedOps - ConversionManager.popLatestCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + ConversionManager.insertCompressedOps doc_id, compressedOps, (error) -> return callback(error) if error? - - if !lastCompressedUpdate? - lastCompressedUpdate = rawUpdates.shift() - - if !lastCompressedUpdate? - # No saved versions, no raw updates, nothing to do - return callback() - - uncompressedUpdates = [lastCompressedUpdate].concat rawUpdates - compressedUpdates = ConcatManager.compressUpdates uncompressedUpdates - - ConversionManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> - return callback(error) if error? - logger.log doc_id: doc_id, rawOpsLength: length, compressedOpsLength: compressedUpdates.length, "compressed doc ops" - callback() + logger.log doc_id: doc_id, rawOpsLength: length, compressedOpsLength: compressedOps.length, "compressed doc ops" + callback() diff --git a/services/track-changes/app/coffee/ConcatManager.coffee b/services/track-changes/app/coffee/HistoryBuilder.coffee similarity index 85% rename from services/track-changes/app/coffee/ConcatManager.coffee rename to services/track-changes/app/coffee/HistoryBuilder.coffee index d56a53c811..2a9405f439 100644 --- a/services/track-changes/app/coffee/ConcatManager.coffee +++ b/services/track-changes/app/coffee/HistoryBuilder.coffee @@ -1,8 +1,11 @@ strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] -module.exports = ConcatManager = +module.exports = HistoryBuilder = normalizeUpdate: (update) -> + if update.meta.start_ts? + return [update] # already normalized + updates = [] for op in update.op updates.push @@ -15,11 +18,16 @@ module.exports = ConcatManager = compressUpdates: (rawUpdates) -> return [] if rawUpdates.length == 0 - firstPass = [rawUpdates.shift()] - for update in rawUpdates + normalizedUpdates = [] + for rawUpdate in rawUpdates + normalizedUpdates = normalizedUpdates.concat HistoryBuilder.normalizeUpdate(rawUpdate) + + return [] if normalizedUpdates.length == 0 + firstPass = [normalizedUpdates.shift()] + for update in normalizedUpdates lastCompressedUpdate = firstPass.pop() if lastCompressedUpdate? - firstPass = firstPass.concat ConcatManager._concatTwoUpdates lastCompressedUpdate, update, false + firstPass = firstPass.concat HistoryBuilder._concatTwoUpdates lastCompressedUpdate, update, false else firstPass.push rawUpdate @@ -28,7 +36,7 @@ module.exports = ConcatManager = for update in firstPass lastCompressedUpdate = secondPass.pop() if lastCompressedUpdate? - secondPass = secondPass.concat ConcatManager._concatTwoUpdates lastCompressedUpdate, update, true + secondPass = secondPass.concat HistoryBuilder._concatTwoUpdates lastCompressedUpdate, update, true else secondPass.push update @@ -53,7 +61,7 @@ module.exports = ConcatManager = if firstUpdate.meta.user_id != secondUpdate.meta.user_id return [firstUpdate, secondUpdate] - if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > ConcatManager.MAX_TIME_BETWEEN_UPDATES + if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > HistoryBuilder.MAX_TIME_BETWEEN_UPDATES return [firstUpdate, secondUpdate] firstOp = firstUpdate.op[0] diff --git a/services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee b/services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee new file mode 100644 index 0000000000..cdc72b69ca --- /dev/null +++ b/services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee @@ -0,0 +1,87 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/ConversionManager.js" +SandboxedModule = require('sandboxed-module') + +describe "ConversionManager", -> + beforeEach -> + @ConversionManager = SandboxedModule.require modulePath, requires: + "./HistoryBuilder": @HistoryBuilder = {} + "./mongojs" : {} + "logger-sharelatex": { log: sinon.stub() } + @doc_id = "doc-id-123" + @callback = sinon.stub() + + describe "when there are no raw ops", -> + beforeEach -> + @ConversionManager.popLastCompressedOp = sinon.stub() + @ConversionManager.insertCompressedOps = sinon.stub() + @ConversionManager.convertAndSaveRawOps @doc_id, [], @callback + + it "should not need to access the database", -> + @ConversionManager.popLastCompressedOp.called.should.equal false + @ConversionManager.insertCompressedOps.called.should.equal false + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when there is no compressed history to begin with", -> + beforeEach -> + @rawOps = ["mock-raw-op-1", "mock-raw-op-2"] + @compressedOps = ["mock-compressed-op"] + + @ConversionManager.popLastCompressedOp = sinon.stub().callsArgWith(1, null, null) + @ConversionManager.insertCompressedOps = sinon.stub().callsArg(2) + @HistoryBuilder.compressOps = sinon.stub().returns(@compressedOps) + @ConversionManager.convertAndSaveRawOps @doc_id, @rawOps, @callback + + it "should try to pop the last compressed op", -> + @ConversionManager.popLastCompressedOp + .calledWith(@doc_id) + .should.equal true + + it "should compress the raw ops", -> + @HistoryBuilder.compressOps + .calledWith(@rawOps) + .should.equal true + + it "should save the compressed ops", -> + @ConversionManager.insertCompressedOps + .calledWith(@doc_id, @compressedOps) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the raw ops need appending to existing history", -> + beforeEach -> + @rawOps = ["mock-raw-op-1", "mock-raw-op-2"] + @lastCompressedOp = "mock-last-compressed-op-0" + @compressedOps = ["mock-compressed-op-1"] + + @ConversionManager.popLastCompressedOp = sinon.stub().callsArgWith(1, null, @lastCompressedOp) + @ConversionManager.insertCompressedOps = sinon.stub().callsArg(2) + @HistoryBuilder.compressOps = sinon.stub().returns(@compressedOps) + @ConversionManager.convertAndSaveRawOps @doc_id, @rawOps, @callback + + it "should try to pop the last compressed op", -> + @ConversionManager.popLastCompressedOp + .calledWith(@doc_id) + .should.equal true + + it "should compress the last compressed op and the raw ops", -> + @HistoryBuilder.compressOps + .calledWith([@lastCompressedOp].concat(@rawOps)) + .should.equal true + + it "should save the compressed ops", -> + @ConversionManager.insertCompressedOps + .calledWith(@doc_id, @compressedOps) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + diff --git a/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee similarity index 87% rename from services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee rename to services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee index 93909bc99c..bcbb0a157a 100644 --- a/services/track-changes/test/unit/coffee/ConcatManager/ConcatManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee @@ -2,12 +2,12 @@ sinon = require('sinon') chai = require('chai') should = chai.should() expect = chai.expect -modulePath = "../../../../app/js/ConcatManager.js" +modulePath = "../../../../app/js/HistoryBuilder.js" SandboxedModule = require('sandboxed-module') -describe "ConcatManager", -> +describe "HistoryBuilder", -> beforeEach -> - @ConcatManager = SandboxedModule.require modulePath + @HistoryBuilder = SandboxedModule.require modulePath @user_id = "user-id-1" @other_user_id = "user-id-2" @ts1 = Date.now() @@ -16,7 +16,7 @@ describe "ConcatManager", -> describe "compress", -> describe "insert - insert", -> it "should append one insert to the other", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -29,7 +29,7 @@ describe "ConcatManager", -> }] it "should insert one insert inside the other", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -42,7 +42,7 @@ describe "ConcatManager", -> }] it "should not append separated inserts", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -59,7 +59,7 @@ describe "ConcatManager", -> describe "delete - delete", -> it "should append one delete to the other", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -72,7 +72,7 @@ describe "ConcatManager", -> }] it "should insert one delete inside the other", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -85,7 +85,7 @@ describe "ConcatManager", -> }] it "should not append separated deletes", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -102,7 +102,7 @@ describe "ConcatManager", -> describe "insert - delete", -> it "should undo a previous insert", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -115,7 +115,7 @@ describe "ConcatManager", -> }] it "should remove part of an insert from the middle", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "fobaro" ] meta: ts: @ts1, user_id: @user_id }, { @@ -128,7 +128,7 @@ describe "ConcatManager", -> }] it "should cancel out two opposite updates", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -138,7 +138,7 @@ describe "ConcatManager", -> .to.deep.equal [] it "should not combine separated updates", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, i: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -155,7 +155,7 @@ describe "ConcatManager", -> describe "delete - insert", -> it "should redo a previous delete at the beginning", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { @@ -168,7 +168,7 @@ describe "ConcatManager", -> }] it "should redo a previous delete from halfway through", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foobar" ] meta: ts: @ts1, user_id: @user_id }, { @@ -184,7 +184,7 @@ describe "ConcatManager", -> }] it "should keep words together", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "abcdefghijklmnopqrstuvwxyz hello world" ] meta: ts: @ts1, user_id: @user_id }, { @@ -210,7 +210,7 @@ describe "ConcatManager", -> it "should not combine the ops if the insert text does not match the delete text", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foobar" ] meta: ts: @ts1, user_id: @user_id }, { @@ -226,7 +226,7 @@ describe "ConcatManager", -> }] it "should cancel two equal updates", -> - expect(@ConcatManager.compressUpdates [{ + expect(@HistoryBuilder.compressUpdates [{ op: [ p: 3, d: "foo" ] meta: ts: @ts1, user_id: @user_id }, { From 533b8e59a3d64bf11c6339728e08a20ad912e3bb Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 27 Jan 2014 16:26:58 +0000 Subject: [PATCH 013/549] Big refactor to use better names and separation of concerns --- .../app/coffee/ConversionManager.coffee | 48 ---- .../app/coffee/HistoryBuilder.coffee | 150 ----------- .../app/coffee/HistoryManager.coffee | 54 ++++ .../app/coffee/UpdateCompressor.coffee | 118 +++++++++ .../ConversionManagerTests.coffee | 87 ------- .../HistoryBuilder/HistoryBuilderTests.coffee | 240 ------------------ .../HistoryManager/HistoryManagerTests.coffee | 87 +++++++ .../UpdateCompressorTests.coffee | 176 +++++++++++++ 8 files changed, 435 insertions(+), 525 deletions(-) delete mode 100644 services/track-changes/app/coffee/ConversionManager.coffee delete mode 100644 services/track-changes/app/coffee/HistoryBuilder.coffee create mode 100644 services/track-changes/app/coffee/HistoryManager.coffee create mode 100644 services/track-changes/app/coffee/UpdateCompressor.coffee delete mode 100644 services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee delete mode 100644 services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee create mode 100644 services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee create mode 100644 services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee diff --git a/services/track-changes/app/coffee/ConversionManager.coffee b/services/track-changes/app/coffee/ConversionManager.coffee deleted file mode 100644 index 2db3aaad51..0000000000 --- a/services/track-changes/app/coffee/ConversionManager.coffee +++ /dev/null @@ -1,48 +0,0 @@ -{db, ObjectId} = require "./mongojs" -HistoryBuilder = require "./HistoryBuilder" -logger = require "logger-sharelatex" - -module.exports = ConversionManager = - OPS_TO_LEAVE: 100 - - popLastCompressedOp: (doc_id, callback = (error, op) ->) -> - db.docHistory.findAndModify - query: { doc_id: ObjectId(doc_id) } - fields: { docOps: { $slice: -1 } } - update: { $pop: { docOps: 1 } } - , (error, history = { docOps: [] }) -> - return callback(error) if error? - callback null, history.docOps[0] - - insertCompressedOps: (doc_id, docOps, callback = (error) ->) -> - db.docHistory.update { - doc_id: ObjectId(doc_id) - }, { - $push: - docOps: - $each: docOps - }, { - upsert: true - }, callback - - convertAndSaveRawOps: (doc_id, rawOps, callback = (error) ->) -> - length = rawOps.length - if length == 0 - return callback() - - - ConversionManager.popLastCompressedOp doc_id, (error, lastCompressedOp) -> - return callback(error) if error? - - if !lastCompressedOp? - rawOps = rawOps.slice(0) # Clone so we can modify in place - lastCompressedOp = rawOps.shift() - - uncompressedOps = [lastCompressedOp].concat rawOps - compressedOps = HistoryBuilder.compressOps uncompressedOps - - ConversionManager.insertCompressedOps doc_id, compressedOps, (error) -> - return callback(error) if error? - logger.log doc_id: doc_id, rawOpsLength: length, compressedOpsLength: compressedOps.length, "compressed doc ops" - callback() - diff --git a/services/track-changes/app/coffee/HistoryBuilder.coffee b/services/track-changes/app/coffee/HistoryBuilder.coffee deleted file mode 100644 index 2a9405f439..0000000000 --- a/services/track-changes/app/coffee/HistoryBuilder.coffee +++ /dev/null @@ -1,150 +0,0 @@ -strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] -strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] - -module.exports = HistoryBuilder = - normalizeUpdate: (update) -> - if update.meta.start_ts? - return [update] # already normalized - - updates = [] - for op in update.op - updates.push - op: [op] - meta: - start_ts: update.meta.ts - end_ts: update.meta.ts - user_id: update.meta.user_id - return updates - - compressUpdates: (rawUpdates) -> - return [] if rawUpdates.length == 0 - normalizedUpdates = [] - for rawUpdate in rawUpdates - normalizedUpdates = normalizedUpdates.concat HistoryBuilder.normalizeUpdate(rawUpdate) - - return [] if normalizedUpdates.length == 0 - firstPass = [normalizedUpdates.shift()] - for update in normalizedUpdates - lastCompressedUpdate = firstPass.pop() - if lastCompressedUpdate? - firstPass = firstPass.concat HistoryBuilder._concatTwoUpdates lastCompressedUpdate, update, false - else - firstPass.push rawUpdate - - return [] if firstPass.length == 0 - secondPass = [firstPass.shift()] - for update in firstPass - lastCompressedUpdate = secondPass.pop() - if lastCompressedUpdate? - secondPass = secondPass.concat HistoryBuilder._concatTwoUpdates lastCompressedUpdate, update, true - else - secondPass.push update - - return secondPass - - MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 - - _concatTwoUpdates: (firstUpdate, secondUpdate, mergeInsertsAndDeletes) -> - firstUpdate = - op: firstUpdate.op - meta: - user_id: firstUpdate.meta.user_id or null - start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts - end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts - secondUpdate = - op: secondUpdate.op - meta: - user_id: secondUpdate.meta.user_id or null - start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts - end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts - - if firstUpdate.meta.user_id != secondUpdate.meta.user_id - return [firstUpdate, secondUpdate] - - if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > HistoryBuilder.MAX_TIME_BETWEEN_UPDATES - return [firstUpdate, secondUpdate] - - firstOp = firstUpdate.op[0] - secondOp = secondUpdate.op[0] - # Two inserts - if firstOp.i? and secondOp.i? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) - return [ - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts - user_id: firstUpdate.meta.user_id - op: [ - p: firstOp.p - i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) - ] - ] - # Two deletes - else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) - return [ - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts - user_id: firstUpdate.meta.user_id - op: [ - p: secondOp.p - d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) - ] - ] - # An insert and then a delete - if mergeInsertsAndDeletes and firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) - offset = secondOp.p - firstOp.p - insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) - if insertedText == secondOp.d - insert = strRemove(firstOp.i, offset, secondOp.d.length) - return [] if insert == "" - return [ - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts - user_id: firstUpdate.meta.user_id - op: [ - p: firstOp.p - i: insert - ] - ] - else - # This shouldn't be possible! - return [firstUpdate, secondUpdate] - else if mergeInsertsAndDeletes and firstOp.d? and secondOp.i? and firstOp.p == secondOp.p - offset = firstOp.d.indexOf(secondOp.i) - if offset == -1 - return [firstUpdate, secondUpdate] - headD = firstOp.d.slice(0, offset) - inserted = firstOp.d.slice(offset, secondOp.i.length) - tailD = firstOp.d.slice(offset + secondOp.i.length) - headP = firstOp.p - tailP = firstOp.p + secondOp.i.length - updates = [] - if headD != "" - updates.push - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts - user_id: firstUpdate.meta.user_id - op: [ - p: headP - d: headD - ] - if tailD != "" - updates.push - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts - user_id: firstUpdate.meta.user_id - op: [ - p: tailP - d: tailD - ] - if updates.length == 2 - updates[0].meta.start_ts = updates[0].meta.end_ts = firstUpdate.meta.start_ts - updates[1].meta.start_ts = updates[1].meta.end_ts = secondUpdate.meta.end_ts - return updates - - else - return [firstUpdate, secondUpdate] - diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee new file mode 100644 index 0000000000..bf7a4f6510 --- /dev/null +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -0,0 +1,54 @@ +{db, ObjectId} = require "./mongojs" +UpdateCompressor = require "./UpdateCompressor" +logger = require "logger-sharelatex" + +module.exports = HistoryManager = + getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> + db.docHistory + .find(doc_id: ObjectId(doc_id.toString())) + .sort(timestamp: -1) + .limit(1) + .toArray (error, compressedUpdates) -> + return callback(error) if error? + return callback null, compressedUpdates[0] or null + + deleteCompressedUpdate: (id, callback = (error) ->) -> + db.docHistory.delete({ _id: ObjectId(id.toString()) }, callback) + + popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> + HistoryManager.getLastCompressedUpdate doc_id, (error, update) -> + return callback(error) if error? + if update? + HistoryManager.deleteCompressedUpdate update._id, (error) -> + return callback(error) if error? + callback null, update + else + callback null, null + + insertCompressedUpdates: (doc_id, docUpdates, callback = (error) ->) -> + db.docHistory.update { + doc_id: ObjectId(doc_id) + }, { + $push: + docUpdates: + $each: docUpdates + }, { + upsert: true + }, callback + + compressAndSaveRawUpdates: (doc_id, rawUpdates, callback = (error) ->) -> + length = rawUpdates.length + if length == 0 + return callback() + + + HistoryManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + return callback(error) if error? + + compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates + + HistoryManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> + return callback(error) if error? + logger.log doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" + callback() + diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee new file mode 100644 index 0000000000..5a69cb6e6b --- /dev/null +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -0,0 +1,118 @@ +strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] +strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] + +module.exports = UpdateCompressor = + # Updates come from the doc updater in format + # { + # op: [ { ... op1 ... }, { ... op2 ... } ] + # meta: { ts: ..., user_id: ... } + # } + # but it's easier to work with on op per update, so convert these updates to + # our compressed format + # [{ + # op: op1 + # meta: { start_ts: ... , end_ts: ..., user_id: ... } + # }, { + # op: op2 + # meta: { start_ts: ... , end_ts: ..., user_id: ... } + # }] + convertRawUpdatesToCompressedFormat: (updates) -> + normalizedUpdates = [] + for update in updates + for op in update.op + normalizedUpdates.push + op: op + meta: + start_ts: update.meta.start_ts or update.meta.ts + end_ts: update.meta.end_ts or update.meta.ts + user_id: update.meta.user_id + return normalizedUpdates + + compressRawUpdates: (lastPreviousUpdate, rawUpdates) -> + updates = UpdateCompressor.convertRawUpdatesToCompressedFormat(rawUpdates) + if lastPreviousUpdate? + updates.unshift(lastPreviousUpdate) + return UpdateCompressor.compressUpdates(updates) + + compressUpdates: (updates) -> + return [] if updates.length == 0 + + compressedUpdates = [updates.shift()] + for update in updates + lastCompressedUpdate = compressedUpdates.pop() + if lastCompressedUpdate? + compressedUpdates = compressedUpdates.concat UpdateCompressor._concatTwoUpdates lastCompressedUpdate, update + else + compressedUpdates.push update + + return compressedUpdates + + MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 + + _concatTwoUpdates: (firstUpdate, secondUpdate) -> + firstUpdate = + op: firstUpdate.op + meta: + user_id: firstUpdate.meta.user_id or null + start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts + end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts + secondUpdate = + op: secondUpdate.op + meta: + user_id: secondUpdate.meta.user_id or null + start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts + end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts + + if firstUpdate.meta.user_id != secondUpdate.meta.user_id + return [firstUpdate, secondUpdate] + + if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > UpdateCompressor.MAX_TIME_BETWEEN_UPDATES + return [firstUpdate, secondUpdate] + + firstOp = firstUpdate.op + secondOp = secondUpdate.op + # Two inserts + if firstOp.i? and secondOp.i? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + return [ + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: + p: firstOp.p + i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) + ] + # Two deletes + else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) + return [ + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: + p: secondOp.p + d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) + ] + # An insert and then a delete + else if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + offset = secondOp.p - firstOp.p + insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) + if insertedText == secondOp.d + insert = strRemove(firstOp.i, offset, secondOp.d.length) + return [] if insert == "" + return [ + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: + p: firstOp.p + i: insert + ] + else + # This shouldn't be possible! + return [firstUpdate, secondUpdate] + + else + return [firstUpdate, secondUpdate] + diff --git a/services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee b/services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee deleted file mode 100644 index cdc72b69ca..0000000000 --- a/services/track-changes/test/unit/coffee/ConversionManager/ConversionManagerTests.coffee +++ /dev/null @@ -1,87 +0,0 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/ConversionManager.js" -SandboxedModule = require('sandboxed-module') - -describe "ConversionManager", -> - beforeEach -> - @ConversionManager = SandboxedModule.require modulePath, requires: - "./HistoryBuilder": @HistoryBuilder = {} - "./mongojs" : {} - "logger-sharelatex": { log: sinon.stub() } - @doc_id = "doc-id-123" - @callback = sinon.stub() - - describe "when there are no raw ops", -> - beforeEach -> - @ConversionManager.popLastCompressedOp = sinon.stub() - @ConversionManager.insertCompressedOps = sinon.stub() - @ConversionManager.convertAndSaveRawOps @doc_id, [], @callback - - it "should not need to access the database", -> - @ConversionManager.popLastCompressedOp.called.should.equal false - @ConversionManager.insertCompressedOps.called.should.equal false - - it "should call the callback", -> - @callback.called.should.equal true - - describe "when there is no compressed history to begin with", -> - beforeEach -> - @rawOps = ["mock-raw-op-1", "mock-raw-op-2"] - @compressedOps = ["mock-compressed-op"] - - @ConversionManager.popLastCompressedOp = sinon.stub().callsArgWith(1, null, null) - @ConversionManager.insertCompressedOps = sinon.stub().callsArg(2) - @HistoryBuilder.compressOps = sinon.stub().returns(@compressedOps) - @ConversionManager.convertAndSaveRawOps @doc_id, @rawOps, @callback - - it "should try to pop the last compressed op", -> - @ConversionManager.popLastCompressedOp - .calledWith(@doc_id) - .should.equal true - - it "should compress the raw ops", -> - @HistoryBuilder.compressOps - .calledWith(@rawOps) - .should.equal true - - it "should save the compressed ops", -> - @ConversionManager.insertCompressedOps - .calledWith(@doc_id, @compressedOps) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "when the raw ops need appending to existing history", -> - beforeEach -> - @rawOps = ["mock-raw-op-1", "mock-raw-op-2"] - @lastCompressedOp = "mock-last-compressed-op-0" - @compressedOps = ["mock-compressed-op-1"] - - @ConversionManager.popLastCompressedOp = sinon.stub().callsArgWith(1, null, @lastCompressedOp) - @ConversionManager.insertCompressedOps = sinon.stub().callsArg(2) - @HistoryBuilder.compressOps = sinon.stub().returns(@compressedOps) - @ConversionManager.convertAndSaveRawOps @doc_id, @rawOps, @callback - - it "should try to pop the last compressed op", -> - @ConversionManager.popLastCompressedOp - .calledWith(@doc_id) - .should.equal true - - it "should compress the last compressed op and the raw ops", -> - @HistoryBuilder.compressOps - .calledWith([@lastCompressedOp].concat(@rawOps)) - .should.equal true - - it "should save the compressed ops", -> - @ConversionManager.insertCompressedOps - .calledWith(@doc_id, @compressedOps) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - diff --git a/services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee b/services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee deleted file mode 100644 index bcbb0a157a..0000000000 --- a/services/track-changes/test/unit/coffee/HistoryBuilder/HistoryBuilderTests.coffee +++ /dev/null @@ -1,240 +0,0 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/HistoryBuilder.js" -SandboxedModule = require('sandboxed-module') - -describe "HistoryBuilder", -> - beforeEach -> - @HistoryBuilder = SandboxedModule.require modulePath - @user_id = "user-id-1" - @other_user_id = "user-id-2" - @ts1 = Date.now() - @ts2 = Date.now() + 1000 - - describe "compress", -> - describe "insert - insert", -> - it "should append one insert to the other", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 6, i: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, i: "foobar" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should insert one insert inside the other", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 5, i: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, i: "fobaro" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should not append separated inserts", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 9, i: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, i: "foo" ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id - }, { - op: [ p: 9, i: "bar" ] - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id - }] - - describe "delete - delete", -> - it "should append one delete to the other", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, d: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, d: "foobar" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should insert one delete inside the other", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 1, d: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 1, d: "bafoor" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should not append separated deletes", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 9, d: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, d: "foo" ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id - }, { - op: [ p: 9, d: "bar" ] - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id - }] - - describe "insert - delete", -> - it "should undo a previous insert", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 5, d: "o" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, i: "fo" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should remove part of an insert from the middle", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "fobaro" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 5, d: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, i: "foo" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should cancel out two opposite updates", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, d: "foo" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [] - - it "should not combine separated updates", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, i: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 9, d: "bar" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, i: "foo" ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id - }, { - op: [ p: 9, d: "bar" ] - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id - }] - - describe "delete - insert", -> - it "should redo a previous delete at the beginning", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, i: "f" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 4, d: "oo" ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - it "should redo a previous delete from halfway through", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foobar" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, i: "oo" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, d: "f" ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id - }, { - op: [ p: 5, d: "bar" ] - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id - }] - - it "should keep words together", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "abcdefghijklmnopqrstuvwxyz hello world" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, i: "w" ] - meta: ts: @ts2, user_id: @user_id - }, { - op: [ p: 4, i: "o" ] - meta: ts: @ts2, user_id: @user_id - }, { - op: [ p: 5, i: "r" ] - meta: ts: @ts2, user_id: @user_id - }, { - op: [ p: 6, i: "l" ] - meta: ts: @ts2, user_id: @user_id - }, { - op: [ p: 7, i: "d" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, d: "abcdefghijklmnopqrstuvwxyz hello " ] - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - }] - - - it "should not combine the ops if the insert text does not match the delete text", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foobar" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, i: "xy" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [{ - op: [ p: 3, d: "foobar" ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, i: "xy" ] - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id - }] - - it "should cancel two equal updates", -> - expect(@HistoryBuilder.compressUpdates [{ - op: [ p: 3, d: "foo" ] - meta: ts: @ts1, user_id: @user_id - }, { - op: [ p: 3, i: "foo" ] - meta: ts: @ts2, user_id: @user_id - }]) - .to.deep.equal [] - - - - diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee new file mode 100644 index 0000000000..9750f95461 --- /dev/null +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -0,0 +1,87 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/HistoryManager.js" +SandboxedModule = require('sandboxed-module') + +describe "HistoryManager", -> + beforeEach -> + @HistoryManager = SandboxedModule.require modulePath, requires: + "./UpdateCompressor": @UpdateCompressor = {} + "./mongojs" : {} + "logger-sharelatex": { log: sinon.stub() } + @doc_id = "doc-id-123" + @callback = sinon.stub() + + describe "when there are no raw ops", -> + beforeEach -> + @HistoryManager.popLastCompressedUpdate = sinon.stub() + @HistoryManager.insertCompressedUpdates = sinon.stub() + @HistoryManager.compressAndSaveRawUpdates @doc_id, [], @callback + + it "should not need to access the database", -> + @HistoryManager.popLastCompressedUpdate.called.should.equal false + @HistoryManager.insertCompressedUpdates.called.should.equal false + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when there is no compressed history to begin with", -> + beforeEach -> + @rawUpdates = ["mock-raw-op-1", "mock-raw-op-2"] + @compressedUpdates = ["mock-compressed-op"] + + @HistoryManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) + @HistoryManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should try to pop the last compressed op", -> + @HistoryManager.popLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should compress the raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(null, @rawUpdates) + .should.equal true + + it "should save the compressed ops", -> + @HistoryManager.insertCompressedUpdates + .calledWith(@doc_id, @compressedUpdates) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the raw ops need appending to existing history", -> + beforeEach -> + @rawUpdates = ["mock-raw-op-1", "mock-raw-op-2"] + @lastCompressedUpdate = "mock-last-compressed-op-0" + @compressedUpdates = ["mock-compressed-op-1"] + + @HistoryManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) + @HistoryManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should try to pop the last compressed op", -> + @HistoryManager.popLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should compress the last compressed op and the raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(@lastCompressedUpdate, @rawUpdates) + .should.equal true + + it "should save the compressed ops", -> + @HistoryManager.insertCompressedUpdates + .calledWith(@doc_id, @compressedUpdates) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee new file mode 100644 index 0000000000..21cb02b13d --- /dev/null +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -0,0 +1,176 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/UpdateCompressor.js" +SandboxedModule = require('sandboxed-module') + +describe "UpdateCompressor", -> + beforeEach -> + @UpdateCompressor = SandboxedModule.require modulePath + @user_id = "user-id-1" + @other_user_id = "user-id-2" + @ts1 = Date.now() + @ts2 = Date.now() + 1000 + + describe "convertRawUpdatesToCompressedFormat", -> + it "should split grouped updates into individual updates", -> + expect(@UpdateCompressor.convertRawUpdatesToCompressedFormat [{ + op: [ @op1 = { p: 0, i: "Foo" }, @op2 = { p: 6, i: "bar"} ] + meta: { ts: @ts1, user_id: @user_id } + }, { + op: [ @op3 = { p: 10, i: "baz" } ] + meta: { ts: @ts2, user_id: @other_user_id } + }]) + .to.deep.equal [{ + op: @op1, + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + }, { + op: @op2, + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + }, { + op: @op3, + meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id } + }] + + describe "compress", -> + describe "insert - insert", -> + it "should append one insert to the other", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 6, i: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, i: "foobar" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should insert one insert inside the other", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 5, i: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, i: "fobaro" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should not append separated inserts", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 9, i: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, i: "foo" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: { p: 9, i: "bar" } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + describe "delete - delete", -> + it "should append one delete to the other", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, d: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 3, d: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, d: "foobar" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should insert one delete inside the other", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, d: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 1, d: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 1, d: "bafoor" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should not append separated deletes", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, d: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 9, d: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, d: "foo" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: { p: 9, d: "bar" } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + describe "insert - delete", -> + it "should undo a previous insert", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 5, d: "o" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, i: "fo" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should remove part of an insert from the middle", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "fobaro" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 5, d: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, i: "foo" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }] + + it "should cancel out two opposite updates", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 3, d: "foo" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [] + + it "should not combine separated updates", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + }, { + op: { p: 9, d: "bar" } + meta: ts: @ts2, user_id: @user_id + }]) + .to.deep.equal [{ + op: { p: 3, i: "foo" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }, { + op: { p: 9, d: "bar" } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + }] + + From 5dea123b13d8ccdc74a42dc90dbf3023362998db Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 27 Jan 2014 17:51:09 +0000 Subject: [PATCH 014/549] Get append end point working with a simple acceptance test --- services/track-changes/.gitignore | 2 + services/track-changes/app.coffee | 22 +++++++---- .../app/coffee/HistoryManager.coffee | 26 +++++++------ .../config/settings.testing.coffee | 1 + services/track-changes/package.json | 25 ++++++------ services/track-changes/rakefile.rb | 6 +++ .../coffee/AppendingUpdatesTests.coffee | 38 +++++++++++++++++++ 7 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index 40d016e39d..c39cd12b4d 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -1,4 +1,6 @@ **.swp node_modules/ app/js +app.js test/unit/js +test/acceptance/js diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 85aa7ced6b..b208df2368 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -1,20 +1,26 @@ +Settings = require "settings-sharelatex" express = require "express" app = express() -ConversoinManager = require "./app/js/ConversionManager" +HistoryManager = require "./app/js/HistoryManager" logger = require "logger-sharelatex" logger.initialize("history") -app.post express.bodyParser(), "/doc/:doc_id/flush", (req, res, next) -> - project_id = req.params.project_id - docOps = req.body.docOps - logger.log doc_id: doc_id, "compressing doc history" - ConversionManager.convertAndSaveRawOps doc_id, docOps, (error) -> +app.post "/doc/:doc_id/history", express.bodyParser(), (req, res, next) -> + doc_id = req.params.doc_id + docOps = req.body.docOps + version = req.body.version + logger.log doc_id: doc_id, version: version, "compressing doc history" + HistoryManager.compressAndSaveRawUpdates doc_id, docOps, (error) -> return next(error) if error? - res.send 204 # No content + res.send 204 app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" req.send 500 -app.listen(3014) +app.listen (Settings.port ||= 3014), (error) -> + if error? + logger.error err: error, "could not start history server" + logger.log "history api listening on port 3014" + diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index bf7a4f6510..32b3ddf758 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -1,6 +1,7 @@ {db, ObjectId} = require "./mongojs" UpdateCompressor = require "./UpdateCompressor" logger = require "logger-sharelatex" +async = require "async" module.exports = HistoryManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> @@ -25,15 +26,19 @@ module.exports = HistoryManager = else callback null, null - insertCompressedUpdates: (doc_id, docUpdates, callback = (error) ->) -> - db.docHistory.update { - doc_id: ObjectId(doc_id) - }, { - $push: - docUpdates: - $each: docUpdates - }, { - upsert: true + insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> + jobs = [] + for update in updates + do (update) -> + jobs.push (callback) -> HistoryManager.insertCompressedUpdate doc_id, update, callback + async.series jobs, callback + + insertCompressedUpdate: (doc_id, update, callback = (error) ->) -> + logger.log doc_id: doc_id, update: update, "inserting compressed update" + db.docHistory.insert { + doc_id: ObjectId(doc_id.toString()) + op: update.op + meta: update.meta }, callback compressAndSaveRawUpdates: (doc_id, rawUpdates, callback = (error) ->) -> @@ -41,12 +46,9 @@ module.exports = HistoryManager = if length == 0 return callback() - HistoryManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> return callback(error) if error? - compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - HistoryManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> return callback(error) if error? logger.log doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" diff --git a/services/track-changes/config/settings.testing.coffee b/services/track-changes/config/settings.testing.coffee index 528c6a7114..0e4e181dfd 100755 --- a/services/track-changes/config/settings.testing.coffee +++ b/services/track-changes/config/settings.testing.coffee @@ -1,3 +1,4 @@ module.exports = mongo: url: 'mongodb://127.0.0.1/sharelatexTesting' + port: 3014 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e1a8dba4a6..9f1cf78669 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -1,14 +1,15 @@ { - "name": "history-sharelatex", - "version": "0.0.1", - "dependencies": { - "async": "", - "chai": "", - "express": "3.3.5", - "sandboxed-module": "", - "sinon": "", - "mongojs": "0.7.2", - "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master", - "logger": "git+ssh://git@bitbucket.org:sharelatex/logger-sharelatex.git#bunyan" - } + "name": "history-sharelatex", + "version": "0.0.1", + "dependencies": { + "async": "", + "chai": "", + "express": "3.3.5", + "sandboxed-module": "", + "sinon": "", + "mongojs": "~0.9.11", + "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master", + "logger": "git+ssh://git@bitbucket.org:sharelatex/logger-sharelatex.git#bunyan", + "request": "~2.33.0" + } } diff --git a/services/track-changes/rakefile.rb b/services/track-changes/rakefile.rb index 6baa462558..a3b3cd6ecd 100644 --- a/services/track-changes/rakefile.rb +++ b/services/track-changes/rakefile.rb @@ -38,6 +38,12 @@ namespace 'compile' do end puts 'finished app compile' end + sh %{coffee -c app.coffee} do |ok, res| + if ! ok + raise "error compiling root app file: #{res}" + end + puts 'finished root app file compile' + end end desc "compiles unit tests" diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee new file mode 100644 index 0000000000..c901917ac1 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -0,0 +1,38 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +mongojs = require "../../../app/js/mongojs" +db = mongojs.db +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" +request = require "request" + +describe "Appending doc ops to the history", -> + describe "when the history does not exist yet", -> + before (done) -> + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + @updates = [{ + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + }, { + op: [{ i: "o", p: 4 }] + meta: { ts: Date.now(), user_id: @user_id } + }, { + op: [{ i: "o", p: 5 }] + meta: { ts: Date.now(), user_id: @user_id } + }] + @version = 3 + + request.post { + url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" + json: + version: @version + docOps: @updates + }, (@error, @response, @body) => + done() + + it "should return a successful response", -> + @response.statusCode.should.equal 204 + + From d4295c20232c41a7e1c8e9252e8400978ca3d423 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 27 Jan 2014 18:09:37 +0000 Subject: [PATCH 015/549] Create HttpController for HTTP requestS --- services/track-changes/app.coffee | 17 +++------ .../app/coffee/HttpController.coffee | 12 +++++++ .../HttpController/HttpControllerTests.coffee | 36 +++++++++++++++++++ 3 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 services/track-changes/app/coffee/HttpController.coffee create mode 100644 services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index b208df2368..412f32315a 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -1,19 +1,12 @@ Settings = require "settings-sharelatex" -express = require "express" -app = express() - -HistoryManager = require "./app/js/HistoryManager" logger = require "logger-sharelatex" logger.initialize("history") -app.post "/doc/:doc_id/history", express.bodyParser(), (req, res, next) -> - doc_id = req.params.doc_id - docOps = req.body.docOps - version = req.body.version - logger.log doc_id: doc_id, version: version, "compressing doc history" - HistoryManager.compressAndSaveRawUpdates doc_id, docOps, (error) -> - return next(error) if error? - res.send 204 +HttpController = require "./app/js/HttpController" +express = require "express" +app = express() + +app.post "/doc/:doc_id/history", express.bodyParser(), HttpController.appendUpdates app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee new file mode 100644 index 0000000000..99e745078f --- /dev/null +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -0,0 +1,12 @@ +HistoryManager = require "./HistoryManager" +logger = require "logger-sharelatex" + +module.exports = HttpController = + appendUpdates: (req, res, next = (error) ->) -> + doc_id = req.params.doc_id + docOps = req.body.docOps + version = req.body.version + logger.log doc_id: doc_id, version: version, "compressing doc history" + HistoryManager.compressAndSaveRawUpdates doc_id, docOps, (error) -> + return next(error) if error? + res.send 204 diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee new file mode 100644 index 0000000000..f1b84020dc --- /dev/null +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -0,0 +1,36 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/HttpController.js" +SandboxedModule = require('sandboxed-module') + +describe "HttpController", -> + beforeEach -> + @HttpController = SandboxedModule.require modulePath, requires: + "logger-sharelatex": { log: sinon.stub() } + "./HistoryManager": @HistoryManager = {} + @doc_id = "doc-id-123" + @version = 42 + @next = sinon.stub() + + describe "appendUpdates", -> + beforeEach -> + @req = + params: + doc_id: @doc_id + body: + docOps: @docOps = ["mock-ops"] + version: @version + @res = + send: sinon.stub() + @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArg(2) + @HttpController.appendUpdates @req, @res, @next + + it "should append the updates", -> + @HistoryManager.compressAndSaveRawUpdates + .calledWith(@doc_id, @docOps) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true \ No newline at end of file From d040e7c410eb5908a58f8fac9fd00ef3670f44c7 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 27 Jan 2014 18:20:38 +0000 Subject: [PATCH 016/549] Add acceptance tests for multiple appends --- .../app/coffee/HistoryManager.coffee | 2 +- .../coffee/AppendingUpdatesTests.coffee | 81 ++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 32b3ddf758..9d332a6f79 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -14,7 +14,7 @@ module.exports = HistoryManager = return callback null, compressedUpdates[0] or null deleteCompressedUpdate: (id, callback = (error) ->) -> - db.docHistory.delete({ _id: ObjectId(id.toString()) }, callback) + db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> HistoryManager.getLastCompressedUpdate doc_id, (error, update) -> diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index c901917ac1..1b6f335914 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -12,7 +12,7 @@ describe "Appending doc ops to the history", -> before (done) -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - @updates = [{ + updates = [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } }, { @@ -28,7 +28,7 @@ describe "Appending doc ops to the history", -> url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" json: version: @version - docOps: @updates + docOps: updates }, (@error, @response, @body) => done() @@ -36,3 +36,80 @@ describe "Appending doc ops to the history", -> @response.statusCode.should.equal 204 + describe "when the history has already been started", -> + beforeEach (done) -> + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + updates = [{ + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + }, { + op: [{ i: "o", p: 4 }] + meta: { ts: Date.now(), user_id: @user_id } + }, { + op: [{ i: "o", p: 5 }] + meta: { ts: Date.now(), user_id: @user_id } + }] + @version = 3 + + request.post { + url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" + json: + version: @version + docOps: updates + }, (@error, @response, @body) => + done() + + describe "when the updates are recent and from the same user", -> + beforeEach (done) -> + updates = [{ + op: [{ i: "b", p: 6 }] + meta: { ts: Date.now(), user_id: @user_id } + }, { + op: [{ i: "a", p: 7 }] + meta: { ts: Date.now(), user_id: @user_id } + }, { + op: [{ i: "r", p: 8 }] + meta: { ts: Date.now(), user_id: @user_id } + }] + @version = 6 + + request.post { + url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" + json: + version: @version + docOps: updates + }, (@error, @response, @body) => + done() + + it "should return a successful response", -> + @response.statusCode.should.equal 204 + + + describe "when the updates are far apart", -> + beforeEach (done) -> + oneDay = 24 * 60 * 60 * 1000 + updates = [{ + op: [{ i: "b", p: 6 }] + meta: { ts: Date.now() + oneDay, user_id: @user_id } + }, { + op: [{ i: "a", p: 7 }] + meta: { ts: Date.now() + oneDay, user_id: @user_id } + }, { + op: [{ i: "r", p: 8 }] + meta: { ts: Date.now() + oneDay, user_id: @user_id } + }] + @version = 6 + + request.post { + url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" + json: + version: @version + docOps: updates + }, (@error, @response, @body) => + done() + + it "should return a successful response", -> + @response.statusCode.should.equal 204 + + From de783bf5b079f5d661cf6f102f851582fba764ba Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 24 Feb 2014 17:43:27 +0000 Subject: [PATCH 017/549] Pull out MongoManager module and add tests --- .../app/coffee/HistoryManager.coffee | 44 +----- .../app/coffee/MongoManager.coffee | 40 ++++++ .../HistoryManager/HistoryManagerTests.coffee | 26 ++-- .../MongoManager/MongoManagerTests.coffee | 133 ++++++++++++++++++ 4 files changed, 189 insertions(+), 54 deletions(-) create mode 100644 services/track-changes/app/coffee/MongoManager.coffee create mode 100644 services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 9d332a6f79..7ed112f996 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -1,55 +1,17 @@ -{db, ObjectId} = require "./mongojs" +MongoManager = require "./MongoManager" UpdateCompressor = require "./UpdateCompressor" logger = require "logger-sharelatex" -async = require "async" module.exports = HistoryManager = - getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> - db.docHistory - .find(doc_id: ObjectId(doc_id.toString())) - .sort(timestamp: -1) - .limit(1) - .toArray (error, compressedUpdates) -> - return callback(error) if error? - return callback null, compressedUpdates[0] or null - - deleteCompressedUpdate: (id, callback = (error) ->) -> - db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) - - popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> - HistoryManager.getLastCompressedUpdate doc_id, (error, update) -> - return callback(error) if error? - if update? - HistoryManager.deleteCompressedUpdate update._id, (error) -> - return callback(error) if error? - callback null, update - else - callback null, null - - insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> - jobs = [] - for update in updates - do (update) -> - jobs.push (callback) -> HistoryManager.insertCompressedUpdate doc_id, update, callback - async.series jobs, callback - - insertCompressedUpdate: (doc_id, update, callback = (error) ->) -> - logger.log doc_id: doc_id, update: update, "inserting compressed update" - db.docHistory.insert { - doc_id: ObjectId(doc_id.toString()) - op: update.op - meta: update.meta - }, callback - compressAndSaveRawUpdates: (doc_id, rawUpdates, callback = (error) ->) -> length = rawUpdates.length if length == 0 return callback() - HistoryManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> return callback(error) if error? compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - HistoryManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> + MongoManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> return callback(error) if error? logger.log doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" callback() diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee new file mode 100644 index 0000000000..dc17f12d76 --- /dev/null +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -0,0 +1,40 @@ +{db, ObjectId} = require "./mongojs" +async = require "async" + +module.exports = MongoManager = + getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> + db.docHistory + .find(doc_id: ObjectId(doc_id.toString())) + .sort( "meta.end_ts": -1) + .limit(1) + .toArray (error, compressedUpdates) -> + return callback(error) if error? + return callback null, compressedUpdates[0] or null + + deleteCompressedUpdate: (id, callback = (error) ->) -> + db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) + + popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> + MongoManager.getLastCompressedUpdate doc_id, (error, update) -> + return callback(error) if error? + if update? + MongoManager.deleteCompressedUpdate update._id, (error) -> + return callback(error) if error? + callback null, update + else + callback null, null + + insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> + jobs = [] + for update in updates + do (update) -> + jobs.push (callback) -> MongoManager.insertCompressedUpdate doc_id, update, callback + async.series jobs, callback + + insertCompressedUpdate: (doc_id, update, callback = (error) ->) -> + db.docHistory.insert { + doc_id: ObjectId(doc_id.toString()) + op: update.op + meta: update.meta + v: update.v + }, callback \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index 9750f95461..4c1bae0564 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -9,20 +9,20 @@ describe "HistoryManager", -> beforeEach -> @HistoryManager = SandboxedModule.require modulePath, requires: "./UpdateCompressor": @UpdateCompressor = {} - "./mongojs" : {} + "./MongoManager" : @MongoManager = {} "logger-sharelatex": { log: sinon.stub() } @doc_id = "doc-id-123" @callback = sinon.stub() describe "when there are no raw ops", -> beforeEach -> - @HistoryManager.popLastCompressedUpdate = sinon.stub() - @HistoryManager.insertCompressedUpdates = sinon.stub() + @MongoManager.popLastCompressedUpdate = sinon.stub() + @MongoManager.insertCompressedUpdates = sinon.stub() @HistoryManager.compressAndSaveRawUpdates @doc_id, [], @callback it "should not need to access the database", -> - @HistoryManager.popLastCompressedUpdate.called.should.equal false - @HistoryManager.insertCompressedUpdates.called.should.equal false + @MongoManager.popLastCompressedUpdate.called.should.equal false + @MongoManager.insertCompressedUpdates.called.should.equal false it "should call the callback", -> @callback.called.should.equal true @@ -32,13 +32,13 @@ describe "HistoryManager", -> @rawUpdates = ["mock-raw-op-1", "mock-raw-op-2"] @compressedUpdates = ["mock-compressed-op"] - @HistoryManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @HistoryManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> - @HistoryManager.popLastCompressedUpdate + @MongoManager.popLastCompressedUpdate .calledWith(@doc_id) .should.equal true @@ -48,7 +48,7 @@ describe "HistoryManager", -> .should.equal true it "should save the compressed ops", -> - @HistoryManager.insertCompressedUpdates + @MongoManager.insertCompressedUpdates .calledWith(@doc_id, @compressedUpdates) .should.equal true @@ -61,13 +61,13 @@ describe "HistoryManager", -> @lastCompressedUpdate = "mock-last-compressed-op-0" @compressedUpdates = ["mock-compressed-op-1"] - @HistoryManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) - @HistoryManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> - @HistoryManager.popLastCompressedUpdate + @MongoManager.popLastCompressedUpdate .calledWith(@doc_id) .should.equal true @@ -77,7 +77,7 @@ describe "HistoryManager", -> .should.equal true it "should save the compressed ops", -> - @HistoryManager.insertCompressedUpdates + @MongoManager.insertCompressedUpdates .calledWith(@doc_id, @compressedUpdates) .should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee new file mode 100644 index 0000000000..0aa43677e4 --- /dev/null +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -0,0 +1,133 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/MongoManager.js" +SandboxedModule = require('sandboxed-module') +{ObjectId} = require("mongojs") + +describe "MongoManager", -> + beforeEach -> + @MongoManager = SandboxedModule.require modulePath, requires: + "./mongojs" : { db: @db = {}, ObjectId: ObjectId } + @callback = sinon.stub() + @doc_id = ObjectId().toString() + + describe "getLastCompressedUpdate", -> + beforeEach -> + @update = "mock-update" + @db.docHistory = {} + @db.docHistory.find = sinon.stub().returns @db.docHistory + @db.docHistory.sort = sinon.stub().returns @db.docHistory + @db.docHistory.limit = sinon.stub().returns @db.docHistory + @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, [@update]) + + @MongoManager.getLastCompressedUpdate @doc_id, @callback + + it "should find the updates for the doc", -> + @db.docHistory.find + .calledWith(doc_id: ObjectId(@doc_id)) + .should.equal true + + it "should limit to one result", -> + @db.docHistory.limit + .calledWith(1) + .should.equal true + + it "should sort in descending timestamp order", -> + @db.docHistory.sort + .calledWith("meta.end_ts": -1) + .should.equal true + + it "should call the call back with the update", -> + @callback.calledWith(null, @update).should.equal true + + describe "deleteCompressedUpdate", -> + beforeEach -> + @update_id = ObjectId().toString() + @db.docHistory = + remove: sinon.stub().callsArg(1) + @MongoManager.deleteCompressedUpdate(@update_id, @callback) + + it "should remove the update", -> + @db.docHistory.remove + .calledWith(_id: ObjectId(@update_id)) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "popLastCompressedUpdate", -> + describe "when there is no last update", -> + beforeEach -> + @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) + @MongoManager.deleteCompressedUpdate = sinon.stub() + @MongoManager.popLastCompressedUpdate @doc_id, @callback + + it "should get the last update", -> + @MongoManager.getLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should not try to delete the last update", -> + @MongoManager.deleteCompressedUpdate.called.should.equal false + + it "should call the callback with no update", -> + @callback.calledWith(null, null).should.equal true + + describe "when there is an update", -> + beforeEach -> + @update = { _id: Object() } + @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) + @MongoManager.deleteCompressedUpdate = sinon.stub().callsArgWith(1, null) + @MongoManager.popLastCompressedUpdate @doc_id, @callback + + it "should get the last update", -> + @MongoManager.getLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should delete the last update", -> + @MongoManager.deleteCompressedUpdate + .calledWith(@update._id) + .should.equal true + + it "should call the callback with the update", -> + @callback.calledWith(null, @update).should.equal true + + describe "insertCompressedUpdate", -> + beforeEach -> + @update = { op: "op", meta: "meta", v: "v"} + @db.docHistory = + insert: sinon.stub().callsArg(1) + @MongoManager.insertCompressedUpdate @doc_id, @update, @callback + + it "should insert the update", -> + @db.docHistory.insert + .calledWith({ + doc_id: ObjectId(@doc_id), + op: @update.op, + meta: @update.meta, + v: @update.v + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "insertCompressedUpdates", -> + beforeEach (done) -> + @updates = [ "mock-update-1", "mock-update-2" ] + @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(2) + @MongoManager.insertCompressedUpdates @doc_id, @updates, (args...) => + @callback(args...) + done() + + it "should insert each update", -> + for update in @updates + @MongoManager.insertCompressedUpdate + .calledWith(@doc_id, update) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true From 8ae9bcd60fe52844d876c8b3c93ad70f5c32cc46 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 25 Feb 2014 12:27:42 +0000 Subject: [PATCH 018/549] Ensure that updates are compressed in continuous incrementing order --- .../app/coffee/HistoryManager.coffee | 16 +++++ .../HistoryManager/HistoryManagerTests.coffee | 70 +++++++++++++------ 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 7ed112f996..3024dcf7f8 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -10,9 +10,25 @@ module.exports = HistoryManager = MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> return callback(error) if error? + + # Ensure that raw updates start where lastCompressedUpdate left off + if lastCompressedUpdate? + rawUpdates = rawUpdates.slice(0) + while rawUpdates[0]? and rawUpdates[0].v <= lastCompressedUpdate.v + rawUpdates.shift() + + if rawUpdates[0]? and rawUpdates[0].v != lastCompressedUpdate.v + 1 + return callback new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") + compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates MongoManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> return callback(error) if error? logger.log doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" callback() + processUncompressedUpdates: (doc_id, callback = (error) ->) -> + # Get lock - here or elsewhere? + # Get batch from Redis left hand side (oldest) + # pass batch to compressAndSaveRawUpdates + # Delete batch from redis + # release lock diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index 4c1bae0564..a8bd1f9f74 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -29,8 +29,8 @@ describe "HistoryManager", -> describe "when there is no compressed history to begin with", -> beforeEach -> - @rawUpdates = ["mock-raw-op-1", "mock-raw-op-2"] - @compressedUpdates = ["mock-compressed-op"] + @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @compressedUpdates = { v: 13, op: "compressed-op-12" } @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) @@ -57,31 +57,55 @@ describe "HistoryManager", -> describe "when the raw ops need appending to existing history", -> beforeEach -> - @rawUpdates = ["mock-raw-op-1", "mock-raw-op-2"] - @lastCompressedUpdate = "mock-last-compressed-op-0" - @compressedUpdates = ["mock-compressed-op-1"] + @lastCompressedUpdate = { v: 11, op: "compressed-op-11" } + @compressedUpdates = { v: 13, op: "compressed-op-12" } @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback - it "should try to pop the last compressed op", -> - @MongoManager.popLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true - - it "should compress the last compressed op and the raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(@lastCompressedUpdate, @rawUpdates) - .should.equal true - - it "should save the compressed ops", -> - @MongoManager.insertCompressedUpdates - .calledWith(@doc_id, @compressedUpdates) - .should.equal true + describe "when the raw ops start where the existing history ends", -> + beforeEach -> + @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should try to pop the last compressed op", -> + @MongoManager.popLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should compress the last compressed op and the raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(@lastCompressedUpdate, @rawUpdates) + .should.equal true + + it "should save the compressed ops", -> + @MongoManager.insertCompressedUpdates + .calledWith(@doc_id, @compressedUpdates) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when some raw ops are passed that have already been compressed", -> + beforeEach -> + @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should only compress the more recent raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(@lastCompressedUpdate, @rawUpdates.slice(-2)) + .should.equal true + + describe "when the raw ops do not follow from the last compressed op version", -> + beforeEach -> + @rawUpdates = [{ v: 13, op: "mock-op-13" }] + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should call the callback with an error", -> + @callback + .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) + .should.equal true - it "should call the callback", -> - @callback.called.should.equal true - From 34d3847fe4f99f6cc817db5318a643c388919e15 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 25 Feb 2014 16:27:14 +0000 Subject: [PATCH 019/549] Add Redis Manager for fetching and deleting uncompressed ops --- .../app/coffee/RedisManager.coffee | 21 +++++++++ .../RedisManager/RedisManagerTests.coffee | 45 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 services/track-changes/app/coffee/RedisManager.coffee create mode 100644 services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee new file mode 100644 index 0000000000..81a1375bf6 --- /dev/null +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -0,0 +1,21 @@ +Settings = require "settings-sharelatex" +redis = require('redis') +redisConf = Settings.redis?.web or {host: "localhost", port: 6379} +rclient = redis.createClient(redisConf.port, redisConf.host) +rclient.auth(redisConf.password) + +buildRawUpdatesKey = (doc_id) -> "UncompressedHistoryOps:#{doc_id}" + +module.exports = RedisManager = + getOldestRawUpdates: (doc_id, batchSize, callback = (error, rawUpdates) ->) -> + key = buildRawUpdatesKey(doc_id) + rclient.lrange key, 0, batchSize - 1, (error, jsonUpdates) -> + try + rawUpdates = ( JSON.parse(update) for update in jsonUpdates or [] ) + catch e + return callback(e) + callback null, rawUpdates + + deleteOldestRawUpdates: (doc_id, batchSize, callback = (error, rawUpdates) ->) -> + key = buildRawUpdatesKey(doc_id) + rclient.ltrim key, batchSize, -1, callback diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee new file mode 100644 index 0000000000..669e64103d --- /dev/null +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -0,0 +1,45 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/RedisManager.js" +SandboxedModule = require('sandboxed-module') + +describe "RedisManager", -> + beforeEach -> + @RedisManager = SandboxedModule.require modulePath, requires: + "redis" : + createClient: () => @rclient = + auth: sinon.stub() + "settings-sharelatex": {} + @doc_id = "doc-id-123" + @batchSize = 100 + @callback = sinon.stub() + + describe "getOldestRawUpdates", -> + beforeEach -> + @rawUpdates = [ {v: 42, op: "mock-op-42"}, { v: 45, op: "mock-op-45" }] + @jsonUpdates = (JSON.stringify(update) for update in @rawUpdates) + @rclient.lrange = sinon.stub().callsArgWith(3, null, @jsonUpdates) + @RedisManager.getOldestRawUpdates @doc_id, @batchSize, @callback + + it "should read the updates from redis", -> + @rclient.lrange + .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @batchSize - 1) + .should.equal true + + it "should call the callback with the parsed ops", -> + @callback.calledWith(null, @rawUpdates).should.equal true + + describe "deleteOldestRawUpdates", -> + beforeEach -> + @rclient.ltrim = sinon.stub().callsArg(3) + @RedisManager.deleteOldestRawUpdates @doc_id, @batchSize, @callback + + it "should delete the updates from redis", -> + @rclient.ltrim + .calledWith("UncompressedHistoryOps:#{@doc_id}", @batchSize, -1) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true From f33a3bde3e76f236927e0040745ab9d1e8e65eff Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 25 Feb 2014 16:48:42 +0000 Subject: [PATCH 020/549] Create processUncompressedUpdates method --- .../app/coffee/HistoryManager.coffee | 17 +- .../HistoryManager/HistoryManagerTests.coffee | 158 +++++++++++------- 2 files changed, 105 insertions(+), 70 deletions(-) diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 3024dcf7f8..597d6e2460 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -1,4 +1,5 @@ MongoManager = require "./MongoManager" +RedisManager = require "./RedisManager" UpdateCompressor = require "./UpdateCompressor" logger = require "logger-sharelatex" @@ -26,9 +27,15 @@ module.exports = HistoryManager = logger.log doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" callback() + REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (doc_id, callback = (error) ->) -> - # Get lock - here or elsewhere? - # Get batch from Redis left hand side (oldest) - # pass batch to compressAndSaveRawUpdates - # Delete batch from redis - # release lock + RedisManager.getOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> + return callback(error) if error? + HistoryManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> + return callback(error) if error? + RedisManager.deleteOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error) -> + return callback(error) if error? + callback() + + processUncompressUpdatesWithLock: (doc_id, callback = (error) ->) -> + diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index a8bd1f9f74..b2a234c928 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -10,63 +10,33 @@ describe "HistoryManager", -> @HistoryManager = SandboxedModule.require modulePath, requires: "./UpdateCompressor": @UpdateCompressor = {} "./MongoManager" : @MongoManager = {} + "./RedisManager" : @RedisManager = {} "logger-sharelatex": { log: sinon.stub() } @doc_id = "doc-id-123" @callback = sinon.stub() - describe "when there are no raw ops", -> - beforeEach -> - @MongoManager.popLastCompressedUpdate = sinon.stub() - @MongoManager.insertCompressedUpdates = sinon.stub() - @HistoryManager.compressAndSaveRawUpdates @doc_id, [], @callback + describe "compressAndSaveRawUpdates", -> + describe "when there are no raw ops", -> + beforeEach -> + @MongoManager.popLastCompressedUpdate = sinon.stub() + @MongoManager.insertCompressedUpdates = sinon.stub() + @HistoryManager.compressAndSaveRawUpdates @doc_id, [], @callback - it "should not need to access the database", -> - @MongoManager.popLastCompressedUpdate.called.should.equal false - @MongoManager.insertCompressedUpdates.called.should.equal false + it "should not need to access the database", -> + @MongoManager.popLastCompressedUpdate.called.should.equal false + @MongoManager.insertCompressedUpdates.called.should.equal false - it "should call the callback", -> - @callback.called.should.equal true + it "should call the callback", -> + @callback.called.should.equal true - describe "when there is no compressed history to begin with", -> - beforeEach -> - @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @compressedUpdates = { v: 13, op: "compressed-op-12" } - - @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) - @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback - - it "should try to pop the last compressed op", -> - @MongoManager.popLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true - - it "should compress the raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(null, @rawUpdates) - .should.equal true - - it "should save the compressed ops", -> - @MongoManager.insertCompressedUpdates - .calledWith(@doc_id, @compressedUpdates) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "when the raw ops need appending to existing history", -> - beforeEach -> - @lastCompressedUpdate = { v: 11, op: "compressed-op-11" } - @compressedUpdates = { v: 13, op: "compressed-op-12" } - - @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) - @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - - describe "when the raw ops start where the existing history ends", -> + describe "when there is no compressed history to begin with", -> beforeEach -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @compressedUpdates = { v: 13, op: "compressed-op-12" } + + @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> @@ -74,9 +44,9 @@ describe "HistoryManager", -> .calledWith(@doc_id) .should.equal true - it "should compress the last compressed op and the raw ops", -> + it "should compress the raw ops", -> @UpdateCompressor.compressRawUpdates - .calledWith(@lastCompressedUpdate, @rawUpdates) + .calledWith(null, @rawUpdates) .should.equal true it "should save the compressed ops", -> @@ -87,25 +57,83 @@ describe "HistoryManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "when some raw ops are passed that have already been compressed", -> + describe "when the raw ops need appending to existing history", -> beforeEach -> - @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @lastCompressedUpdate = { v: 11, op: "compressed-op-11" } + @compressedUpdates = { v: 13, op: "compressed-op-12" } - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - it "should only compress the more recent raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(@lastCompressedUpdate, @rawUpdates.slice(-2)) - .should.equal true + describe "when the raw ops start where the existing history ends", -> + beforeEach -> + @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback - describe "when the raw ops do not follow from the last compressed op version", -> - beforeEach -> - @rawUpdates = [{ v: 13, op: "mock-op-13" }] - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + it "should try to pop the last compressed op", -> + @MongoManager.popLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should compress the last compressed op and the raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(@lastCompressedUpdate, @rawUpdates) + .should.equal true + + it "should save the compressed ops", -> + @MongoManager.insertCompressedUpdates + .calledWith(@doc_id, @compressedUpdates) + .should.equal true - it "should call the callback with an error", -> - @callback - .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) - .should.equal true + it "should call the callback", -> + @callback.called.should.equal true + + describe "when some raw ops are passed that have already been compressed", -> + beforeEach -> + @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should only compress the more recent raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(@lastCompressedUpdate, @rawUpdates.slice(-2)) + .should.equal true + + describe "when the raw ops do not follow from the last compressed op version", -> + beforeEach -> + @rawUpdates = [{ v: 13, op: "mock-op-13" }] + @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + + it "should call the callback with an error", -> + @callback + .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) + .should.equal true + + describe "processUncompressedUpdates", -> + beforeEach -> + @updates = ["mock-update"] + @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) + @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) + @HistoryManager.processUncompressedUpdates @doc_id, @callback + + it "should get the oldest updates", -> + @RedisManager.getOldestRawUpdates + .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) + .should.equal true + + it "should compress and save the updates", -> + @HistoryManager.compressAndSaveRawUpdates + .calledWith(@doc_id, @updates) + .should.equal true + + it "should delete the batch of uncompressed updates that was just processed", -> + @RedisManager.deleteOldestRawUpdates + .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true From 8405a37c2c92cf79c7afe0e6a1b5c3f5f1879721 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 10:55:20 +0000 Subject: [PATCH 021/549] Add a lock around processing updates --- .../app/coffee/HistoryManager.coffee | 8 +- .../app/coffee/LockManager.coffee | 54 ++++++ .../HistoryManager/HistoryManagerTests.coffee | 16 ++ .../LockManager/LockManagerTests.coffee | 171 ++++++++++++++++++ 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 services/track-changes/app/coffee/LockManager.coffee create mode 100644 services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 597d6e2460..e11e911698 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -1,6 +1,7 @@ MongoManager = require "./MongoManager" RedisManager = require "./RedisManager" UpdateCompressor = require "./UpdateCompressor" +LockManager = require "./LockManager" logger = require "logger-sharelatex" module.exports = HistoryManager = @@ -37,5 +38,10 @@ module.exports = HistoryManager = return callback(error) if error? callback() - processUncompressUpdatesWithLock: (doc_id, callback = (error) ->) -> + processUncompressedUpdatesWithLock: (doc_id, callback = (error) ->) -> + LockManager.runWithLock( + "HistoryLock:#{doc_id}", + HistoryManager.processUncompressedUpdates, + callback + ) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee new file mode 100644 index 0000000000..5f71a336fb --- /dev/null +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -0,0 +1,54 @@ +Settings = require "settings-sharelatex" +redis = require('redis') +redisConf = Settings.redis?.web or {host: "localhost", port: 6379} +rclient = redis.createClient(redisConf.port, redisConf.host) +rclient.auth(redisConf.password) + +module.exports = LockManager = + LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock + MAX_LOCK_WAIT_TIME: 10000 # 10s maximum time to spend trying to get the lock + LOCK_TTL: 10 # seconds + + tryLock : (key, callback = (err, gotLock) ->) -> + rclient.set key, "locked", "EX", @LOCK_TTL, "NX", (err, gotLock)-> + return callback(err) if err? + if gotLock == "OK" + callback err, true + else + callback err, false + + getLock: (key, callback = (error) ->) -> + startTime = Date.now() + do attempt = () -> + if Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME + return callback(new Error("Timeout")) + + LockManager.tryLock key, (error, gotLock) -> + return callback(error) if error? + if gotLock + callback(null) + else + setTimeout attempt, LockManager.LOCK_TEST_INTERVAL + + checkLock: (key, callback = (err, isFree) ->) -> + rclient.exists key, (err, exists) -> + return callback(err) if err? + exists = parseInt exists + if exists == 1 + callback err, false + else + callback err, true + + releaseLock: (key, callback) -> + rclient.del key, callback + + runWithLock: (key, runner = ( (releaseLock = (error) ->) -> ), callback = ( (error) -> )) -> + LockManager.getLock key, (error) -> + return callback(error) if error? + runner (error1) -> + LockManager.releaseLock key, (error2) -> + error = error1 or error2 + return callback(error) if error? + callback() + + diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index b2a234c928..0770b0518e 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -11,6 +11,7 @@ describe "HistoryManager", -> "./UpdateCompressor": @UpdateCompressor = {} "./MongoManager" : @MongoManager = {} "./RedisManager" : @RedisManager = {} + "./LockManager" : @LockManager = {} "logger-sharelatex": { log: sinon.stub() } @doc_id = "doc-id-123" @callback = sinon.stub() @@ -136,4 +137,19 @@ describe "HistoryManager", -> it "should call the callback", -> @callback.called.should.equal true + describe "processCompressedUpdatesWithLock", -> + beforeEach -> + @HistoryManager.processUncompressedUpdates = sinon.stub() + @LockManager.runWithLock = sinon.stub().callsArg(2) + @HistoryManager.processUncompressedUpdatesWithLock @doc_id, @callback + it "should run processUncompressedUpdates with the lock", -> + @LockManager.runWithLock + .calledWith( + "HistoryLock:#{@doc_id}", + @HistoryManager.processUncompressedUpdates + ) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee new file mode 100644 index 0000000000..e5caea82c1 --- /dev/null +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -0,0 +1,171 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/LockManager.js" +SandboxedModule = require('sandboxed-module') + +describe "LockManager", -> + beforeEach -> + @LockManager = SandboxedModule.require modulePath, requires: + "redis": + createClient: () => @rclient = + auth: sinon.stub() + "settings-sharelatex": @Settings = {} + @key = "lock-key" + @callback = sinon.stub() + + describe "checkLock", -> + describe "when the lock is taken", -> + beforeEach -> + @rclient.exists = sinon.stub().callsArgWith(1, null, "1") + @LockManager.checkLock @key, @callback + + it "should check the lock in redis", -> + @rclient.exists + .calledWith(@key) + .should.equal true + + it "should return the callback with false", -> + @callback.calledWith(null, false).should.equal true + + describe "when the lock is free", -> + beforeEach -> + @rclient.exists = sinon.stub().callsArgWith(1, null, "0") + @LockManager.checkLock @key, @callback + + it "should return the callback with true", -> + @callback.calledWith(null, true).should.equal true + + + describe "tryLock", -> + describe "when the lock is taken", -> + beforeEach -> + @rclient.set = sinon.stub().callsArgWith(5, null, null) + @LockManager.tryLock @key, @callback + + it "should check the lock in redis", -> + @rclient.set + .calledWith(@key, "locked", "EX", @LockManager.LOCK_TTL, "NX") + .should.equal true + + it "should return the callback with false", -> + @callback.calledWith(null, false).should.equal true + + describe "when the lock is free", -> + beforeEach -> + @rclient.set = sinon.stub().callsArgWith(5, null, "OK") + @LockManager.tryLock @key, @callback + + it "should return the callback with true", -> + @callback.calledWith(null, true).should.equal true + + describe "deleteLock", -> + beforeEach -> + beforeEach -> + @rclient.del = sinon.stub().callsArg(1) + @LockManager.deleteLock @key, @callback + + it "should delete the lock in redis", -> + @rclient.del + .calledWith(key) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "getLock", -> + describe "when the lock is not taken", -> + beforeEach (done) -> + @LockManager.tryLock = sinon.stub().callsArgWith(1, null, true) + @LockManager.getLock @key, (args...) => + @callback(args...) + done() + + it "should try to get the lock", -> + @LockManager.tryLock + .calledWith(@key) + .should.equal true + + it "should only need to try once", -> + @LockManager.tryLock.callCount.should.equal 1 + + it "should return the callback", -> + @callback.calledWith(null).should.equal true + + describe "when the lock is initially set", -> + beforeEach (done) -> + startTime = Date.now() + @LockManager.LOCK_TEST_INTERVAL = 5 + @LockManager.tryLock = (doc_id, callback = (error, isFree) ->) -> + if Date.now() - startTime < 20 + callback null, false + else + callback null, true + sinon.spy @LockManager, "tryLock" + + @LockManager.getLock @key, (args...) => + @callback(args...) + done() + + it "should call tryLock multiple times until free", -> + (@LockManager.tryLock.callCount > 1).should.equal true + + it "should return the callback", -> + @callback.calledWith(null).should.equal true + + describe "when the lock times out", -> + beforeEach (done) -> + time = Date.now() + @LockManager.MAX_LOCK_WAIT_TIME = 5 + @LockManager.tryLock = sinon.stub().callsArgWith(1, null, false) + @LockManager.getLock @key, (args...) => + @callback(args...) + done() + + it "should return the callback with an error", -> + @callback.calledWith(new Error("timeout")).should.equal true + + describe "runWithLock", -> + describe "with successful run", -> + beforeEach -> + @runner = (releaseLock = (error) ->) -> + releaseLock() + sinon.spy @, "runner" + @LockManager.getLock = sinon.stub().callsArg(1) + @LockManager.releaseLock = sinon.stub().callsArg(1) + @LockManager.runWithLock @key, @runner, @callback + + it "should get the lock", -> + @LockManager.getLock + .calledWith(@key) + .should.equal true + + it "should run the passed function", -> + @runner.called.should.equal true + + it "should release the lock", -> + @LockManager.releaseLock + .calledWith(@key) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the runner function returns an error", -> + beforeEach -> + @error = new Error("oops") + @runner = (releaseLock = (error) ->) => + releaseLock(@error) + sinon.spy @, "runner" + @LockManager.getLock = sinon.stub().callsArg(1) + @LockManager.releaseLock = sinon.stub().callsArg(1) + @LockManager.runWithLock @key, @runner, @callback + + it "should release the lock", -> + @LockManager.releaseLock + .calledWith(@key) + .should.equal true + + it "should call the callback with the error", -> + @callback.calledWith(@error).should.equal true From 65360a9a2b5c7976c9a9394f0bed22623d93edd8 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 11:18:26 +0000 Subject: [PATCH 022/549] Continue to process raw updates until redis is empty --- .../app/coffee/HistoryManager.coffee | 9 ++- .../HistoryManager/HistoryManagerTests.coffee | 77 ++++++++++++++----- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index e11e911698..89319ca9e7 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -32,11 +32,18 @@ module.exports = HistoryManager = processUncompressedUpdates: (doc_id, callback = (error) ->) -> RedisManager.getOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? + length = rawUpdates.length HistoryManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> return callback(error) if error? RedisManager.deleteOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error) -> return callback(error) if error? - callback() + if length == HistoryManager.REDIS_READ_BATCH_SIZE + # There might be more updates + setTimeout () -> + HistoryManager.processUncompressedUpdates doc_id, callback + , 0 + else + callback() processUncompressedUpdatesWithLock: (doc_id, callback = (error) ->) -> LockManager.runWithLock( diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index 0770b0518e..82d61cd21d 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -112,30 +112,67 @@ describe "HistoryManager", -> .should.equal true describe "processUncompressedUpdates", -> - beforeEach -> - @updates = ["mock-update"] - @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) - @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) - @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) - @HistoryManager.processUncompressedUpdates @doc_id, @callback + describe "when there is fewer than one batch to send", -> + beforeEach -> + @updates = ["mock-update"] + @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) + @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) + @HistoryManager.processUncompressedUpdates @doc_id, @callback - it "should get the oldest updates", -> - @RedisManager.getOldestRawUpdates - .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) - .should.equal true + it "should get the oldest updates", -> + @RedisManager.getOldestRawUpdates + .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) + .should.equal true - it "should compress and save the updates", -> - @HistoryManager.compressAndSaveRawUpdates - .calledWith(@doc_id, @updates) - .should.equal true + it "should compress and save the updates", -> + @HistoryManager.compressAndSaveRawUpdates + .calledWith(@doc_id, @updates) + .should.equal true - it "should delete the batch of uncompressed updates that was just processed", -> - @RedisManager.deleteOldestRawUpdates - .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) - .should.equal true + it "should delete the batch of uncompressed updates that was just processed", -> + @RedisManager.deleteOldestRawUpdates + .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) + .should.equal true - it "should call the callback", -> - @callback.called.should.equal true + it "should call the callback", -> + @callback.called.should.equal true + + describe "when there are multiple batches to send", -> + beforeEach (done) -> + @HistoryManager.REDIS_READ_BATCH_SIZE = 2 + @updates = ["mock-update-0", "mock-update-1", "mock-update-2", "mock-update-3", "mock-update-4"] + @redisArray = @updates.slice() + @RedisManager.getOldestRawUpdates = (doc_id, batchSize, callback = (error, updates) ->) => + updates = @redisArray.slice(0, batchSize) + @redisArray = @redisArray.slice(batchSize) + callback null, updates + sinon.spy @RedisManager, "getOldestRawUpdates" + @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) + @HistoryManager.processUncompressedUpdates @doc_id, (args...) => + @callback(args...) + done() + + it "should get the oldest updates in three batches ", -> + @RedisManager.getOldestRawUpdates.callCount.should.equal 3 + + it "should compress and save the updates in batches", -> + @HistoryManager.compressAndSaveRawUpdates + .calledWith(@doc_id, @updates.slice(0,2)) + .should.equal true + @HistoryManager.compressAndSaveRawUpdates + .calledWith(@doc_id, @updates.slice(2,4)) + .should.equal true + @HistoryManager.compressAndSaveRawUpdates + .calledWith(@doc_id, @updates.slice(4,5)) + .should.equal true + + it "should delete the batches of uncompressed updates", -> + @RedisManager.deleteOldestRawUpdates.callCount.should.equal 3 + + it "should call the callback", -> + @callback.called.should.equal true describe "processCompressedUpdatesWithLock", -> beforeEach -> From 45fe6978afa3c0f8fb91d816640827c2ca989328 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 11:34:56 +0000 Subject: [PATCH 023/549] Add in /doc//flush endpoint --- services/track-changes/app.coffee | 9 ++++++--- .../app/coffee/HttpController.coffee | 8 +++----- .../HttpController/HttpControllerTests.coffee | 15 ++++++--------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 412f32315a..e08a8a366a 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -6,14 +6,17 @@ HttpController = require "./app/js/HttpController" express = require "express" app = express() -app.post "/doc/:doc_id/history", express.bodyParser(), HttpController.appendUpdates +app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" req.send 500 -app.listen (Settings.port ||= 3014), (error) -> +port = Settings.internal?.history?.port or 3014 +host = Settings.internal?.history?.host or "localhost" +app.listen port, host, (error) -> if error? logger.error err: error, "could not start history server" - logger.log "history api listening on port 3014" + else + logger.log "history api listening on http://#{host}:#{port}" diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 99e745078f..c82f923550 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -2,11 +2,9 @@ HistoryManager = require "./HistoryManager" logger = require "logger-sharelatex" module.exports = HttpController = - appendUpdates: (req, res, next = (error) ->) -> + flushUpdatesWithLock: (req, res, next = (error) ->) -> doc_id = req.params.doc_id - docOps = req.body.docOps - version = req.body.version - logger.log doc_id: doc_id, version: version, "compressing doc history" - HistoryManager.compressAndSaveRawUpdates doc_id, docOps, (error) -> + logger.log doc_id: doc_id, "compressing doc history" + HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) -> return next(error) if error? res.send 204 diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index f1b84020dc..a0d7c23166 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -14,22 +14,19 @@ describe "HttpController", -> @version = 42 @next = sinon.stub() - describe "appendUpdates", -> + describe "flushUpdatesWithLock", -> beforeEach -> @req = params: doc_id: @doc_id - body: - docOps: @docOps = ["mock-ops"] - version: @version @res = send: sinon.stub() - @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArg(2) - @HttpController.appendUpdates @req, @res, @next + @HistoryManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) + @HttpController.flushUpdatesWithLock @req, @res, @next - it "should append the updates", -> - @HistoryManager.compressAndSaveRawUpdates - .calledWith(@doc_id, @docOps) + it "should process the updates", -> + @HistoryManager.processUncompressedUpdatesWithLock + .calledWith(@doc_id) .should.equal true it "should return a success code", -> From d27872c9bdc6bea752054266d72286af34e847ca Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 12:11:45 +0000 Subject: [PATCH 024/549] Get acceptance tests running --- services/track-changes/app.coffee | 4 +- .../app/coffee/HistoryManager.coffee | 9 +++- .../app/coffee/HttpController.coffee | 1 + .../app/coffee/UpdateCompressor.coffee | 7 ++- .../track-changes/app/coffee/mongojs.coffee | 2 +- services/track-changes/package.json | 3 +- .../coffee/AppendingUpdatesTests.coffee | 30 +++++++++--- .../HistoryManager/HistoryManagerTests.coffee | 5 +- .../UpdateCompressorTests.coffee | 49 +++++++++++++++++-- 9 files changed, 91 insertions(+), 19 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index e08a8a366a..ba2c2470ce 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -6,11 +6,13 @@ HttpController = require "./app/js/HttpController" express = require "express" app = express() +app.use express.logger() + app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" - req.send 500 + res.send 500 port = Settings.internal?.history?.port or 3014 host = Settings.internal?.history?.host or "localhost" diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 89319ca9e7..67fb7c789f 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -12,6 +12,7 @@ module.exports = HistoryManager = MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> return callback(error) if error? + logger.log doc_id: doc_id, "popped last update" # Ensure that raw updates start where lastCompressedUpdate left off if lastCompressedUpdate? @@ -30,25 +31,31 @@ module.exports = HistoryManager = REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (doc_id, callback = (error) ->) -> + logger.log "processUncompressedUpdates" RedisManager.getOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? length = rawUpdates.length + logger.log doc_id: doc_id, length: length, "got raw updates from redis" HistoryManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> return callback(error) if error? + logger.log doc_id: doc_id, "compressed and saved doc updates" RedisManager.deleteOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error) -> return callback(error) if error? if length == HistoryManager.REDIS_READ_BATCH_SIZE # There might be more updates + logger.log doc_id: doc_id, "continuing processing updates" setTimeout () -> HistoryManager.processUncompressedUpdates doc_id, callback , 0 else + logger.log doc_id: doc_id, "all raw updates processed" callback() processUncompressedUpdatesWithLock: (doc_id, callback = (error) ->) -> LockManager.runWithLock( "HistoryLock:#{doc_id}", - HistoryManager.processUncompressedUpdates, + (releaseLock) -> + HistoryManager.processUncompressedUpdates doc_id, releaseLock callback ) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index c82f923550..1fd1926317 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -7,4 +7,5 @@ module.exports = HttpController = logger.log doc_id: doc_id, "compressing doc history" HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) -> return next(error) if error? + logger.log "done http request" res.send 204 diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 5a69cb6e6b..e5830bde5a 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -26,6 +26,7 @@ module.exports = UpdateCompressor = start_ts: update.meta.start_ts or update.meta.ts end_ts: update.meta.end_ts or update.meta.ts user_id: update.meta.user_id + v: update.v return normalizedUpdates compressRawUpdates: (lastPreviousUpdate, rawUpdates) -> @@ -56,12 +57,14 @@ module.exports = UpdateCompressor = user_id: firstUpdate.meta.user_id or null start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts + v: firstUpdate.v secondUpdate = op: secondUpdate.op meta: user_id: secondUpdate.meta.user_id or null start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts + v: secondUpdate.v if firstUpdate.meta.user_id != secondUpdate.meta.user_id return [firstUpdate, secondUpdate] @@ -81,6 +84,7 @@ module.exports = UpdateCompressor = op: p: firstOp.p i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) + v: secondUpdate.v ] # Two deletes else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) @@ -92,6 +96,7 @@ module.exports = UpdateCompressor = op: p: secondOp.p d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) + v: secondUpdate.v ] # An insert and then a delete else if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) @@ -99,7 +104,6 @@ module.exports = UpdateCompressor = insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) if insertedText == secondOp.d insert = strRemove(firstOp.i, offset, secondOp.d.length) - return [] if insert == "" return [ meta: start_ts: firstUpdate.meta.start_ts @@ -108,6 +112,7 @@ module.exports = UpdateCompressor = op: p: firstOp.p i: insert + v: secondUpdate.v ] else # This shouldn't be possible! diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index 667f03fcd5..ff56198fa7 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" -db = mongojs.connect(Settings.mongo.url, ["docHistory", "docOps"]) +db = mongojs.connect(Settings.mongo.url, ["docHistory"]) module.exports = db: db ObjectId: mongojs.ObjectId diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 9f1cf78669..23447e6102 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -10,6 +10,7 @@ "mongojs": "~0.9.11", "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master", "logger": "git+ssh://git@bitbucket.org:sharelatex/logger-sharelatex.git#bunyan", - "request": "~2.33.0" + "request": "~2.33.0", + "redis": "~0.10.1" } } diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 1b6f335914..9e07310ad9 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -1,11 +1,13 @@ sinon = require "sinon" chai = require("chai") chai.should() +expect = chai.expect mongojs = require "../../../app/js/mongojs" db = mongojs.db ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" request = require "request" +rclient = require("redis").createClient() # Only works locally for now describe "Appending doc ops to the history", -> describe "when the history does not exist yet", -> @@ -15,27 +17,41 @@ describe "Appending doc ops to the history", -> updates = [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } + v: 3 }, { op: [{ i: "o", p: 4 }] meta: { ts: Date.now(), user_id: @user_id } + v: 4 }, { op: [{ i: "o", p: 5 }] meta: { ts: Date.now(), user_id: @user_id } + v: 5 }] - @version = 3 + + rclient.rpush "UncompressedHistoryOps:#{@doc_id}", (JSON.stringify(u) for u in updates)... request.post { - url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" - json: - version: @version - docOps: updates + url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/flush" }, (@error, @response, @body) => - done() + db.docHistory + .find(doc_id: ObjectId(@doc_id)) + .sort("meta.end_ts": -1) + .toArray (error, updates) => + @update = updates[0] + done() it "should return a successful response", -> @response.statusCode.should.equal 204 + it "should insert the compressed op into mongo", -> + expect(@update.op).to.deep.equal { + p: 3, i: "foo" + } + it "should insert the correct version number into mongo", -> + expect(@update.v).to.equal 5 + + ### describe "when the history has already been started", -> beforeEach (done) -> @doc_id = ObjectId().toString() @@ -112,4 +128,4 @@ describe "Appending doc ops to the history", -> it "should return a successful response", -> @response.statusCode.should.equal 204 - +### diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index 82d61cd21d..7b20efeb63 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -176,15 +176,14 @@ describe "HistoryManager", -> describe "processCompressedUpdatesWithLock", -> beforeEach -> - @HistoryManager.processUncompressedUpdates = sinon.stub() + @HistoryManager.processUncompressedUpdates = sinon.stub().callsArg(2) @LockManager.runWithLock = sinon.stub().callsArg(2) @HistoryManager.processUncompressedUpdatesWithLock @doc_id, @callback it "should run processUncompressedUpdates with the lock", -> @LockManager.runWithLock .calledWith( - "HistoryLock:#{@doc_id}", - @HistoryManager.processUncompressedUpdates + "HistoryLock:#{@doc_id}" ) .should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index 21cb02b13d..a4de76c5ac 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -18,19 +18,24 @@ describe "UpdateCompressor", -> expect(@UpdateCompressor.convertRawUpdatesToCompressedFormat [{ op: [ @op1 = { p: 0, i: "Foo" }, @op2 = { p: 6, i: "bar"} ] meta: { ts: @ts1, user_id: @user_id } + v: 42 }, { op: [ @op3 = { p: 10, i: "baz" } ] meta: { ts: @ts2, user_id: @other_user_id } + v: 43 }]) .to.deep.equal [{ op: @op1, - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + v: 42 }, { op: @op2, - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + v: 42 }, { op: @op3, - meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id } + meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id }, + v: 43 }] describe "compress", -> @@ -39,42 +44,52 @@ describe "UpdateCompressor", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 6, i: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, i: "foobar" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 }] it "should insert one insert inside the other", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 5, i: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, i: "fobaro" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 }] it "should not append separated inserts", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 9, i: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, i: "foo" } meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 9, i: "bar" } meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 }] describe "delete - delete", -> @@ -82,42 +97,52 @@ describe "UpdateCompressor", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, d: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 3, d: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, d: "foobar" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 }] it "should insert one delete inside the other", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, d: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 1, d: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 1, d: "bafoor" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 }] it "should not append separated deletes", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, d: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 9, d: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, d: "foo" } meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 9, d: "bar" } meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 }] describe "insert - delete", -> @@ -125,52 +150,68 @@ describe "UpdateCompressor", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 5, d: "o" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, i: "fo" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 }] it "should remove part of an insert from the middle", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "fobaro" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 5, d: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, i: "foo" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 }] it "should cancel out two opposite updates", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 3, d: "foo" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) - .to.deep.equal [] + .to.deep.equal [ + op: { p: 3, i: "" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 + ] it "should not combine separated updates", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, i: "foo" } meta: ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 9, d: "bar" } meta: ts: @ts2, user_id: @user_id + v: 43 }]) .to.deep.equal [{ op: { p: 3, i: "foo" } meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 }, { op: { p: 9, d: "bar" } meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 }] From 80af34895b0c9abc69da8f5b1e9d7e099e5e81d4 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 12:38:47 +0000 Subject: [PATCH 025/549] Refactor and improve acceptance tests --- .../coffee/AppendingUpdatesTests.coffee | 119 +++++++++--------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 9e07310ad9..e00b169782 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -9,12 +9,25 @@ Settings = require "settings-sharelatex" request = require "request" rclient = require("redis").createClient() # Only works locally for now +flushAndGetCompressedUpdates = (doc_id, callback = (error, updates) ->) -> + request.post { + url: "http://localhost:#{Settings.port}/doc/#{doc_id}/flush" + }, (error, response, body) => + response.statusCode.should.equal 204 + db.docHistory + .find(doc_id: ObjectId(doc_id)) + .sort("meta.end_ts": 1) + .toArray callback + +pushRawUpdates = (doc_id, updates, callback = (error) ->) -> + rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback + describe "Appending doc ops to the history", -> describe "when the history does not exist yet", -> before (done) -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - updates = [{ + pushRawUpdates @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } v: 3 @@ -26,106 +39,96 @@ describe "Appending doc ops to the history", -> op: [{ i: "o", p: 5 }] meta: { ts: Date.now(), user_id: @user_id } v: 5 - }] - - rclient.rpush "UncompressedHistoryOps:#{@doc_id}", (JSON.stringify(u) for u in updates)... - - request.post { - url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/flush" - }, (@error, @response, @body) => - db.docHistory - .find(doc_id: ObjectId(@doc_id)) - .sort("meta.end_ts": -1) - .toArray (error, updates) => - @update = updates[0] - done() - - it "should return a successful response", -> - @response.statusCode.should.equal 204 + }], (error) => + throw error if error? + flushAndGetCompressedUpdates @doc_id, (error, @updates) => + throw error if error? + done() it "should insert the compressed op into mongo", -> - expect(@update.op).to.deep.equal { + expect(@updates[0].op).to.deep.equal { p: 3, i: "foo" } it "should insert the correct version number into mongo", -> - expect(@update.v).to.equal 5 + expect(@updates[0].v).to.equal 5 - ### describe "when the history has already been started", -> beforeEach (done) -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - updates = [{ + pushRawUpdates @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } + v: 3 }, { op: [{ i: "o", p: 4 }] meta: { ts: Date.now(), user_id: @user_id } + v: 4 }, { op: [{ i: "o", p: 5 }] meta: { ts: Date.now(), user_id: @user_id } - }] - @version = 3 - - request.post { - url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" - json: - version: @version - docOps: updates - }, (@error, @response, @body) => - done() + v: 5 + }], (error) => + throw error if error? + flushAndGetCompressedUpdates @doc_id, (error, updates) => + throw error if error? + done() describe "when the updates are recent and from the same user", -> beforeEach (done) -> - updates = [{ + pushRawUpdates @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now(), user_id: @user_id } + v: 6 }, { op: [{ i: "a", p: 7 }] meta: { ts: Date.now(), user_id: @user_id } + v: 7 }, { op: [{ i: "r", p: 8 }] meta: { ts: Date.now(), user_id: @user_id } - }] - @version = 6 + v: 8 + }], (error) => + throw error if error? + flushAndGetCompressedUpdates @doc_id, (error, @updates) => + throw error if error? + done() - request.post { - url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" - json: - version: @version - docOps: updates - }, (@error, @response, @body) => - done() + it "should combine all the updates into one", -> + expect(@updates[0].op).to.deep.equal { + p: 3, i: "foobar" + } - it "should return a successful response", -> - @response.statusCode.should.equal 204 + it "should insert the correct version number into mongo", -> + expect(@updates[0].v).to.equal 8 describe "when the updates are far apart", -> beforeEach (done) -> oneDay = 24 * 60 * 60 * 1000 - updates = [{ + pushRawUpdates @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now() + oneDay, user_id: @user_id } + v: 6 }, { op: [{ i: "a", p: 7 }] meta: { ts: Date.now() + oneDay, user_id: @user_id } + v: 7 }, { op: [{ i: "r", p: 8 }] meta: { ts: Date.now() + oneDay, user_id: @user_id } - }] - @version = 6 + v: 8 + }], (error) => + throw error if error? + flushAndGetCompressedUpdates @doc_id, (error, @updates) => + throw error if error? + done() - request.post { - url: "http://localhost:#{Settings.port}/doc/#{@doc_id}/history" - json: - version: @version - docOps: updates - }, (@error, @response, @body) => - done() - - it "should return a successful response", -> - @response.statusCode.should.equal 204 - -### + it "should keep the updates separate", -> + expect(@updates[0].op).to.deep.equal { + p: 3, i: "foo" + } + expect(@updates[1].op).to.deep.equal { + p: 6, i: "bar" + } From a21f8a8004dc5d91d0b8640e040e3ce61169e94d Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 12:44:13 +0000 Subject: [PATCH 026/549] Add acceptance test for batch processing --- .../coffee/AppendingUpdatesTests.coffee | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index e00b169782..a1cd7c89b3 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -132,3 +132,30 @@ describe "Appending doc ops to the history", -> expect(@updates[1].op).to.deep.equal { p: 6, i: "bar" } + + describe "when the updates need processing in batches", -> + before (done) -> + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + updates = [] + @expectedOp = { p:0, i: "" } + for i in [0..250] + updates.push { + op: [{i: "a", p: 0}] + meta: { ts: Date.now(), user_id: @user_id } + v: i + } + @expectedOp.i = "a" + @expectedOp.i + + pushRawUpdates @doc_id, updates, (error) => + throw error if error? + flushAndGetCompressedUpdates @doc_id, (error, @updates) => + throw error if error? + done() + + it "should concat the compressed op into mongo", -> + expect(@updates[0].op).to.deep.equal @expectedOp + + it "should insert the correct version number into mongo", -> + expect(@updates[0].v).to.equal 250 + From ad18c1d5a5c0ae16ead60325a8f493d730405539 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:17:15 +0000 Subject: [PATCH 027/549] Add Gruntfile --- services/track-changes/Gruntfile.coffee | 111 ++++++++++++++++++ services/track-changes/app.coffee | 10 +- services/track-changes/compressHistory.coffee | 20 ---- .../config/settings.development.coffee | 7 ++ .../config/settings.testing.coffee | 4 - services/track-changes/package.json | 22 +++- .../coffee/AppendingUpdatesTests.coffee | 2 +- 7 files changed, 140 insertions(+), 36 deletions(-) create mode 100644 services/track-changes/Gruntfile.coffee delete mode 100644 services/track-changes/compressHistory.coffee create mode 100755 services/track-changes/config/settings.development.coffee delete mode 100755 services/track-changes/config/settings.testing.coffee diff --git a/services/track-changes/Gruntfile.coffee b/services/track-changes/Gruntfile.coffee new file mode 100644 index 0000000000..eda137d969 --- /dev/null +++ b/services/track-changes/Gruntfile.coffee @@ -0,0 +1,111 @@ +module.exports = (grunt) -> + grunt.loadNpmTasks 'grunt-contrib-coffee' + grunt.loadNpmTasks 'grunt-contrib-clean' + grunt.loadNpmTasks 'grunt-mocha-test' + grunt.loadNpmTasks 'grunt-available-tasks' + grunt.loadNpmTasks 'grunt-execute' + grunt.loadNpmTasks 'grunt-bunyan' + + grunt.initConfig + execute: + app: + src: "app.js" + + bunyan: + strict: false + + coffee: + app_dir: + expand: true, + flatten: false, + cwd: 'app/coffee', + src: ['**/*.coffee'], + dest: 'app/js/', + ext: '.js' + + app: + src: 'app.coffee' + dest: 'app.js' + + acceptance_tests: + expand: true, + flatten: false, + cwd: 'test/acceptance/coffee', + src: ['**/*.coffee'], + dest: 'test/acceptance/js/', + ext: '.js' + + unit_tests: + expand: true, + flatten: false, + cwd: 'test/unit/coffee', + src: ['**/*.coffee'], + dest: 'test/unit/js/', + ext: '.js' + + clean: + app: ["app/js"] + acceptance_tests: ["test/acceptance/js"] + + mochaTest: + unit: + src: ['test/unit/js/**/*.js'] + options: + reporter: grunt.option('reporter') or 'spec' + grep: grunt.option("grep") + acceptance: + src: ['test/acceptance/js/**/*.js'] + options: + reporter: grunt.option('reporter') or 'spec' + grep: grunt.option("grep") + timeout: 10000 + + availabletasks: + tasks: + options: + filter: 'exclude', + tasks: [ + 'coffee' + 'clean' + 'mochaTest' + 'availabletasks' + 'execute' + 'bunyan' + ] + groups: + "Compile tasks": [ + "compile:server" + "compile:tests" + "compile" + "compile:unit_tests" + "compile:acceptance_tests" + "install" + ] + "Test tasks": [ + "test:unit" + "test:acceptance" + ] + "Run tasks": [ + "run" + "default" + ] + "Misc": [ + "help" + ] + + grunt.registerTask 'help', 'Display this help list', 'availabletasks' + + grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir'] + grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['coffee:unit_tests'] + grunt.registerTask 'compile:acceptance_tests', 'Compile the acceptance tests', ['clean:acceptance_tests', 'coffee:acceptance_tests'] + grunt.registerTask 'compile:tests', 'Compile all the tests', ['compile:acceptance_tests', 'compile:unit_tests'] + grunt.registerTask 'compile', 'Compiles everything need to run track-changes-sharelatex', ['compile:server'] + + grunt.registerTask 'install', "Compile everything when installing as an npm module", ['compile'] + + grunt.registerTask 'test:unit', 'Run the unit tests (use --grep= for individual tests)', ['compile:server', 'compile:unit_tests', 'mochaTest:unit'] + grunt.registerTask 'test:acceptance', 'Run the acceptance tests (use --grep= for individual tests)', ['compile:acceptance_tests', 'mochaTest:acceptance'] + + grunt.registerTask 'run', "Compile and run the track-changes-sharelatex server", ['compile', 'bunyan', 'execute'] + grunt.registerTask 'default', 'run' + diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index ba2c2470ce..7334c95682 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" logger = require "logger-sharelatex" -logger.initialize("history") +logger.initialize("track-changes") HttpController = require "./app/js/HttpController" express = require "express" @@ -14,11 +14,11 @@ app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" res.send 500 -port = Settings.internal?.history?.port or 3014 -host = Settings.internal?.history?.host or "localhost" +port = Settings.internal?.trackchanges?.port or 3014 +host = Settings.internal?.trackchanges?.host or "localhost" app.listen port, host, (error) -> if error? - logger.error err: error, "could not start history server" + logger.error err: error, "could not start track-changes server" else - logger.log "history api listening on http://#{host}:#{port}" + logger.log "track changes api listening on http://#{host}:#{port}" diff --git a/services/track-changes/compressHistory.coffee b/services/track-changes/compressHistory.coffee deleted file mode 100644 index 1ce1dad1be..0000000000 --- a/services/track-changes/compressHistory.coffee +++ /dev/null @@ -1,20 +0,0 @@ -{db, ObjectId} = require "./app/coffee/mongojs" -ConversionManager = require "./app/coffee/ConversionManager" -async = require "async" - -db.docOps.find { }, { doc_id: true }, (error, docs) -> - throw error if error? - jobs = [] - for doc in docs - do (doc) -> - jobs.push (callback) -> - doc_id = doc.doc_id.toString() - ConversionManager.convertOldRawUpdates doc_id, (error) -> - return callback(error) if error? - console.log doc_id, "DONE" - callback() - async.series jobs, (error) -> - throw error if error? - process.exit() - - diff --git a/services/track-changes/config/settings.development.coffee b/services/track-changes/config/settings.development.coffee new file mode 100755 index 0000000000..ee758aac0e --- /dev/null +++ b/services/track-changes/config/settings.development.coffee @@ -0,0 +1,7 @@ +module.exports = + mongo: + url: 'mongodb://127.0.0.1/sharelatex' + internal: + trackchanges: + port: 3014 + host: "localhost" diff --git a/services/track-changes/config/settings.testing.coffee b/services/track-changes/config/settings.testing.coffee deleted file mode 100755 index 0e4e181dfd..0000000000 --- a/services/track-changes/config/settings.testing.coffee +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = - mongo: - url: 'mongodb://127.0.0.1/sharelatexTesting' - port: 3014 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 23447e6102..0c2e13c3cb 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -2,15 +2,25 @@ "name": "history-sharelatex", "version": "0.0.1", "dependencies": { - "async": "", - "chai": "", + "async": "~0.2.10", "express": "3.3.5", - "sandboxed-module": "", - "sinon": "", "mongojs": "~0.9.11", - "settings": "git+ssh://git@bitbucket.org:sharelatex/settings-sharelatex.git#master", - "logger": "git+ssh://git@bitbucket.org:sharelatex/logger-sharelatex.git#bunyan", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "request": "~2.33.0", "redis": "~0.10.1" + }, + "devDependencies": { + "chai": "~1.9.0", + "sinon": "~1.8.2", + "sandboxed-module": "~0.3.0", + "grunt-execute": "~0.1.5", + "grunt-contrib-clean": "~0.5.0", + "grunt-mocha-test": "~0.9.3", + "grunt": "~0.4.2", + "grunt-available-tasks": "~0.4.2", + "grunt-contrib-coffee": "~0.10.1", + "bunyan": "~0.22.1", + "grunt-bunyan": "~0.5.0" } } diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index a1cd7c89b3..6867dd901b 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -11,7 +11,7 @@ rclient = require("redis").createClient() # Only works locally for now flushAndGetCompressedUpdates = (doc_id, callback = (error, updates) ->) -> request.post { - url: "http://localhost:#{Settings.port}/doc/#{doc_id}/flush" + url: "http://localhost:3014/doc/#{doc_id}/flush" }, (error, response, body) => response.statusCode.should.equal 204 db.docHistory From 834f41eea11cc780e0a7eac1ccc58b2903edbbb6 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:17:26 +0000 Subject: [PATCH 028/549] Remove Rakefile --- services/track-changes/rakefile.rb | 118 ----------------------------- 1 file changed, 118 deletions(-) delete mode 100644 services/track-changes/rakefile.rb diff --git a/services/track-changes/rakefile.rb b/services/track-changes/rakefile.rb deleted file mode 100644 index a3b3cd6ecd..0000000000 --- a/services/track-changes/rakefile.rb +++ /dev/null @@ -1,118 +0,0 @@ -require 'fileutils' - -siteurl = "https://www.sharelatex.com" - -desc "Compile JavaScirpt into CoffeeScript" -namespace 'setup' do - - desc "installes npm packages json and global stuff like less" - task :installDependencys do - sh %{npm install} - sh %{npm install -g coffee-script} - sh %{git submodule init} - sh %{git submodule update} - end -end - - -namespace 'run' do - desc "compiles and runs the javascirpt version of the app" - task :app => ["compile:app"] do - sh %{node app.js | bunyan} do |ok, res| - if ! ok - raise "error compiling app folder tests : #{res}" - end - puts 'finished app compile' - end - end -end - -namespace 'compile' do - desc "compiles main app folder" - task :app do - puts "Compiling app folder to JS" - FileUtils.rm_rf "app/js" - sh %{coffee -c -o app/js/ app/coffee/} do |ok, res| - if ! ok - raise "error compiling app folder tests : #{res}" - end - puts 'finished app compile' - end - sh %{coffee -c app.coffee} do |ok, res| - if ! ok - raise "error compiling root app file: #{res}" - end - puts 'finished root app file compile' - end - end - - desc "compiles unit tests" - task :unittests => ["compile:app"] do - puts "Compiling Unit Tests to JS" - `coffee -c -o test/unit/js/ test/unit/coffee/` - end - - desc "compiles acceptance tests" - task :acceptancetests => ["compile:app"] do - puts "Compiling Acceptance Tests to JS" - sh %{coffee -c -o test/acceptance/js/ test/acceptance/coffee/} do |ok, res| - if ! ok - raise "error compiling acceptance tests: #{res}" - end - end - end -end - -namespace 'test' do - - desc "runs all test" - task :all => ["test:unit", "test:acceptance"] do - puts "testing everything" - end - - desc "Run Acceptance Tests" - task :acceptance => ["compile:acceptancetests"]do - puts "Running Acceptance Tests" - feature = ENV['feature'] - if feature.nil? - featureFlags = "" - else - featureFlags = "-g \"#{feature}\"" - end - sh %{mocha -R spec #{featureFlags} test/acceptance/js/*} do |ok, res| - if ! ok - raise "error running acceptance tests: #{res}" - end - end - end - - desc "run unit tests" - task :unit => ["compile:unittests"]do - puts "Running Unit Tests" - featurePath = ENV['feature'] - puts featurePath - if featurePath.nil? - featurePath = '' - elsif featurePath.include? '/' - elsif !featurePath.include? '/' - featurePath +='/' - else - featurePath = '' - end - - sh %{mocha -R spec test/unit/js/#{featurePath}* --ignore-leaks} do |ok, res| - if ! ok - raise "error running unit tests : #{res}" - end - end - end -end - - -namespace 'deploy' do - desc "safley deploys app" - task :live do - sh %{git push origin} - sh %{cap live deploy} - end -end From 1e7fc3dc29e04736f383a774302789fffab07777 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:27:09 +0000 Subject: [PATCH 029/549] Add Travis CI --- services/track-changes/.travis.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 services/track-changes/.travis.yml diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml new file mode 100644 index 0000000000..29f5884d60 --- /dev/null +++ b/services/track-changes/.travis.yml @@ -0,0 +1,18 @@ +language: node_js + +node_js: + - "0.10" + +before_install: + - npm install -g grunt-cli + +install: + - npm install + - grunt install + +script: + - grunt test:unit + +services: + - redis-server + - mongodb From 6e9485c0c458595cb01c8286e13a3e45024aa923 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:29:14 +0000 Subject: [PATCH 030/549] Add README and LICENSE --- services/track-changes/LICENSE | 662 +++++++++++++++++++++++++++++++ services/track-changes/README.md | 13 + 2 files changed, 675 insertions(+) create mode 100644 services/track-changes/LICENSE create mode 100644 services/track-changes/README.md diff --git a/services/track-changes/LICENSE b/services/track-changes/LICENSE new file mode 100644 index 0000000000..ac8619dcb9 --- /dev/null +++ b/services/track-changes/LICENSE @@ -0,0 +1,662 @@ + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/services/track-changes/README.md b/services/track-changes/README.md new file mode 100644 index 0000000000..65cf6fe2ff --- /dev/null +++ b/services/track-changes/README.md @@ -0,0 +1,13 @@ +track-changes-sharelatex +======================== + +An API for converting raw editor updates into a compressed and browseable history. + +[![Build Status](https://travis-ci.org/sharelatex/track-changes-sharelatex.png?branch=master)](https://travis-ci.org/sharelatex/track-changes-sharelatex) + +License +------- + +The code in this repository is released under the GNU AFFERO GENERAL PUBLIC LICENSE, version 3. A copy can be found in the `LICENSE` file. + +Copyright (c) ShareLaTeX, 2014. From cef17a7fbc42096dfd3b2887e40059bc108813d6 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:36:53 +0000 Subject: [PATCH 031/549] Experiment with acceptance tests in Travis --- services/track-changes/.travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml index 29f5884d60..8c6c2c3354 100644 --- a/services/track-changes/.travis.yml +++ b/services/track-changes/.travis.yml @@ -10,8 +10,12 @@ install: - npm install - grunt install +before_script: + - grunt run + script: - grunt test:unit + - grunt test:acceptance services: - redis-server From 53db11466b4555c48b588ce7a1dff946a80a8d84 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:39:58 +0000 Subject: [PATCH 032/549] Fix YAML --- services/track-changes/.travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml index 8c6c2c3354..06259acd5a 100644 --- a/services/track-changes/.travis.yml +++ b/services/track-changes/.travis.yml @@ -11,7 +11,7 @@ install: - grunt install before_script: - - grunt run + - grunt run script: - grunt test:unit From 3570e8f4087a36cf965251e2200d1ea3048b0f67 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 16:44:13 +0000 Subject: [PATCH 033/549] Use grunt forever to run service daemonized for acceptance testing on travis --- services/track-changes/.gitignore | 1 + services/track-changes/.travis.yml | 2 +- services/track-changes/Gruntfile.coffee | 6 ++++++ services/track-changes/package.json | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index c39cd12b4d..5178f6093f 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -4,3 +4,4 @@ app/js app.js test/unit/js test/acceptance/js +forever/ diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml index 06259acd5a..6adc08643a 100644 --- a/services/track-changes/.travis.yml +++ b/services/track-changes/.travis.yml @@ -11,7 +11,7 @@ install: - grunt install before_script: - - grunt run + - grunt forever:app:start script: - grunt test:unit diff --git a/services/track-changes/Gruntfile.coffee b/services/track-changes/Gruntfile.coffee index eda137d969..2488d2e8f3 100644 --- a/services/track-changes/Gruntfile.coffee +++ b/services/track-changes/Gruntfile.coffee @@ -5,8 +5,14 @@ module.exports = (grunt) -> grunt.loadNpmTasks 'grunt-available-tasks' grunt.loadNpmTasks 'grunt-execute' grunt.loadNpmTasks 'grunt-bunyan' + grunt.loadNpmTasks 'grunt-forever' grunt.initConfig + forever: + app: + options: + index: "app.js" + execute: app: src: "app.js" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 0c2e13c3cb..fdde105959 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -21,6 +21,7 @@ "grunt-available-tasks": "~0.4.2", "grunt-contrib-coffee": "~0.10.1", "bunyan": "~0.22.1", - "grunt-bunyan": "~0.5.0" + "grunt-bunyan": "~0.5.0", + "grunt-forever": "~0.4.2" } } From d1e18d82aa2c237678123b886e8b219876c2139f Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 26 Feb 2014 17:35:10 +0000 Subject: [PATCH 034/549] Add simple benchmark script --- services/track-changes/benchmark.coffee | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 services/track-changes/benchmark.coffee diff --git a/services/track-changes/benchmark.coffee b/services/track-changes/benchmark.coffee new file mode 100644 index 0000000000..7ed9cd81e7 --- /dev/null +++ b/services/track-changes/benchmark.coffee @@ -0,0 +1,56 @@ +request = require "request" +rclient = require("redis").createClient() +async = require "async" +{ObjectId} = require("./app/js/mongojs") + +NO_OF_DOCS = 100 +NO_OF_UPDATES = 200 + +user_id = ObjectId().toString() + +updates = for i in [1..NO_OF_UPDATES] + { + op: { i: "a", p: 0 } + v: i + meta: ts: new Date(), user_id: user_id + } +jsonUpdates = (JSON.stringify(u) for u in updates) + +doc_ids = (ObjectId().toString() for i in [1..NO_OF_DOCS]) + +populateRedis = (callback = (error) ->) -> + console.log "Populating Redis queues..." + + jobs = [] + for doc_id in doc_ids + do (doc_id) -> + jobs.push (callback) -> + rclient.rpush "UncompressedHistoryOps:#{doc_id}", jsonUpdates..., callback + async.series jobs, (error) -> + return callback(error) if error? + console.log "Done." + callback() + +flushDocs = (callback = (error) ->) -> + console.log "Flushing docs..." + inProgress = 0 + jobs = [] + for doc_id in doc_ids + do (doc_id) -> + jobs.push (callback) -> + inProgress = inProgress + 1 + request.post "http://localhost:3014/doc/#{doc_id}/flush", (error) -> + inProgress = inProgress - 1 + console.log Date.now(), "In progress: #{inProgress}" + callback(error) + async.parallel jobs, (error) -> + return callback(error) if error? + console.log "Done." + callback() + +populateRedis (error) -> + throw error if error? + flushDocs (error) -> + throw error if error? + process.exit(0) + From 76cdd5cf9851eba44cfc57ee09c6c4d37c6f8615 Mon Sep 17 00:00:00 2001 From: James Allen Date: Sat, 1 Mar 2014 11:42:31 +0000 Subject: [PATCH 035/549] Delete only the updates that were processed, not a full batch --- services/track-changes/app/coffee/HistoryManager.coffee | 2 +- .../test/unit/coffee/HistoryManager/HistoryManagerTests.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 67fb7c789f..41feff05c3 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -39,7 +39,7 @@ module.exports = HistoryManager = HistoryManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> return callback(error) if error? logger.log doc_id: doc_id, "compressed and saved doc updates" - RedisManager.deleteOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error) -> + RedisManager.deleteOldestRawUpdates doc_id, length, (error) -> return callback(error) if error? if length == HistoryManager.REDIS_READ_BATCH_SIZE # There might be more updates diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index 7b20efeb63..88efcfe606 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -132,7 +132,7 @@ describe "HistoryManager", -> it "should delete the batch of uncompressed updates that was just processed", -> @RedisManager.deleteOldestRawUpdates - .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) + .calledWith(@doc_id, @updates.length) .should.equal true it "should call the callback", -> From e0402692cf5c87d87895fb0e1fd83bb97bf2c627 Mon Sep 17 00:00:00 2001 From: James Allen Date: Sat, 1 Mar 2014 13:31:34 +0000 Subject: [PATCH 036/549] Add in diff generating functions --- services/track-changes/Gruntfile.coffee | 4 +- .../app/coffee/DiffGenerator.coffee | 168 +++++++++++++ .../DiffGenerator/DiffGeneratorTests.coffee | 238 ++++++++++++++++++ 3 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 services/track-changes/app/coffee/DiffGenerator.coffee create mode 100644 services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee diff --git a/services/track-changes/Gruntfile.coffee b/services/track-changes/Gruntfile.coffee index 2488d2e8f3..ccd1654e2d 100644 --- a/services/track-changes/Gruntfile.coffee +++ b/services/track-changes/Gruntfile.coffee @@ -55,12 +55,12 @@ module.exports = (grunt) -> mochaTest: unit: - src: ['test/unit/js/**/*.js'] + src: ["test/unit/js/#{grunt.option("feature") or "**"}/*.js"] options: reporter: grunt.option('reporter') or 'spec' grep: grunt.option("grep") acceptance: - src: ['test/acceptance/js/**/*.js'] + src: ["test/acceptance/js/**/*.js"] options: reporter: grunt.option('reporter') or 'spec' grep: grunt.option("grep") diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee new file mode 100644 index 0000000000..9ea4682e31 --- /dev/null +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -0,0 +1,168 @@ +ConsistencyError = (message) -> + error = new Error(message) + error.name = "ConsistencyError" + error.__proto__ = ConsistencyError.prototype + return error +ConsistencyError.prototype.__proto__ = Error.prototype + +module.exports = DiffGenerator = + ConsistencyError: ConsistencyError + + rewindUpdate: (content, update) -> + op = update.op + if op.i? + textToBeRemoved = content.slice(op.p, op.p + op.i.length) + if op.i != textToBeRemoved + throw new ConsistencyError( + "Inserted content, '#{op.i}', does not match text to be removed, '#{textToBeRemoved}'" + ) + + return content.slice(0, op.p) + content.slice(op.p + op.i.length) + + else if op.d? + return content.slice(0, op.p) + op.d + content.slice(op.p) + + rewindUpdates: (content, updates) -> + for update in updates.reverse() + content = DiffGenerator.rewindUpdate(content, update) + return content + + buildDiff: (initialContent, updates) -> + + applyUpdateToDiff: (diff, update) -> + position = 0 + op = update.op + + remainingDiff = diff.slice() + {consumedDiff, remainingDiff} = DiffGenerator._consumeToOffset(remainingDiff, op.p) + newDiff = consumedDiff + + if op.i? + newDiff.push + i: op.i + meta: update.meta + else if op.d? + {consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteUpdate remainingDiff, update + newDiff.push(consumedDiff...) + + newDiff.push(remainingDiff...) + + return newDiff + + + _consumeToOffset: (remainingDiff, totalOffset) -> + consumedDiff = [] + position = 0 + while part = remainingDiff.shift() + length = DiffGenerator._getLengthOfDiffPart part + if part.d? + consumedDiff.push part + else if position + length >= totalOffset + partOffset = totalOffset - position + if partOffset > 0 + consumedDiff.push DiffGenerator._slicePart part, 0, partOffset + if partOffset < length + remainingDiff.unshift DiffGenerator._slicePart part, partOffset + return { + consumedDiff: consumedDiff + remainingDiff: remainingDiff + } + else + position += length + consumedDiff.push part + throw new Error("Ran out of diff to consume. Offset is too small") + + _consumeDiffAffectedByDeleteUpdate: (remainingDiff, deleteUpdate) -> + consumedDiff = [] + remainingUpdate = deleteUpdate + while remainingUpdate + {newPart, remainingDiff, remainingUpdate} = DiffGenerator._consumeDeletedPart remainingDiff, remainingUpdate + consumedDiff.push newPart if newPart? + return { + consumedDiff: consumedDiff + remainingDiff: remainingDiff + } + + _consumeDeletedPart: (remainingDiff, deleteUpdate) -> + part = remainingDiff.shift() + partLength = DiffGenerator._getLengthOfDiffPart part + op = deleteUpdate.op + + if part.d? + # Skip existing deletes + remainingUpdate = deleteUpdate + newPart = part + + else if partLength > op.d.length + # Only the first bit of the part has been deleted + remainingPart = DiffGenerator._slicePart part, op.d.length + remainingDiff.unshift remainingPart + + deletedContent = DiffGenerator._getContentOfPart(part).slice(0, op.d.length) + if deletedContent != op.d + throw new ConsistencyError("deleted content, '#{deletedContent}', does not match delete op, '#{op.d}'") + + if part.u? + newPart = + d: op.d + meta: deleteUpdate.meta + else if part.i? + newPart = null + + remainingUpdate = null + + else if partLength == op.d.length + # The entire part has been deleted, but it is the last part + + deletedContent = DiffGenerator._getContentOfPart(part) + if deletedContent != op.d + throw new ConsistencyError("deleted content, '#{deletedContent}', does not match delete op, '#{op.d}'") + + if part.u? + newPart = + d: op.d + meta: deleteUpdate.meta + else if part.i? + newPart = null + + remainingUpdate = null + + else if partLength < op.d.length + # The entire part has been deleted and there is more + + deletedContent = DiffGenerator._getContentOfPart(part) + opContent = op.d.slice(0, deletedContent.length) + if deletedContent != opContent + throw new ConsistencyError("deleted content, '#{deletedContent}', does not match delete op, '#{opContent}'") + + if part.u + newPart = + d: part.u + meta: deleteUpdate.meta + else if part.i? + newPart = null + + remainingUpdate = + op: { p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)) } + meta: deleteUpdate.meta + + return { + newPart: newPart + remainingDiff: remainingDiff + remainingUpdate: remainingUpdate + } + + _slicePart: (basePart, from, to) -> + if basePart.u? + part = { u: basePart.u.slice(from, to) } + else if basePart.i? + part = { i: basePart.i.slice(from, to) } + if basePart.meta? + part.meta = basePart.meta + return part + + _getLengthOfDiffPart: (part) -> + (part.u or part.d or part.i).length + + _getContentOfPart: (part) -> + part.u or part.d or part.i diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee new file mode 100644 index 0000000000..47c4561074 --- /dev/null +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -0,0 +1,238 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/DiffGenerator.js" +SandboxedModule = require('sandboxed-module') + +describe "DiffGenerator", -> + beforeEach -> + @DiffGenerator = SandboxedModule.require modulePath + @ts = Date.now() + @user_id = "mock-user-id" + @meta = { + start_ts: @ts, end_ts: @ts, user_id: @user_id + } + + describe "rewindUpdate", -> + describe "rewinding an insert", -> + it "should undo the insert", -> + content = "hello world" + update = + op: { p: 6, i: "wo" } + rewoundContent = @DiffGenerator.rewindUpdate content, update + rewoundContent.should.equal "hello rld" + + describe "rewinding a delete", -> + it "should undo the delete", -> + content = "hello rld" + update = + op: { p: 6, d: "wo" } + rewoundContent = @DiffGenerator.rewindUpdate content, update + rewoundContent.should.equal "hello world" + + describe "with an inconsistent update", -> + it "should throw an error", -> + content = "hello world" + update = + op: { p: 6, i: "foo" } + expect( () => + @DiffGenerator.rewindUpdate content, update + ).to.throw(@DiffGenerator.ConsistencyError) + + describe "rewindUpdates", -> + it "should rewind updates in reverse", -> + content = "aaabbbccc" + updates = [ + { op: { p: 3, i: "bbb" } }, + { op: { p: 6, i: "ccc" } } + ] + rewoundContent = @DiffGenerator.rewindUpdates content, updates + rewoundContent.should.equal "aaa" + + describe "applyUpdateToDiff", -> + describe "an insert", -> + it "should insert into the middle of (u)nchanged text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foobar" } ], + { op: { p: 3, i: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "foo" } + { i: "baz", meta: @meta } + { u: "bar" } + ]) + + it "should insert into the start of (u)changed text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foobar" } ], + { op: { p: 0, i: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { i: "baz", meta: @meta } + { u: "foobar" } + ]) + + it "should insert into the end of (u)changed text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foobar" } ], + { op: { p: 6, i: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "foobar" } + { i: "baz", meta: @meta } + ]) + + it "should insert into the middle of (i)inserted text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { i: "foobar", meta: @meta } ], + { op: { p: 3, i: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { i: "foo", meta: @meta } + { i: "baz", meta: @meta } + { i: "bar", meta: @meta } + ]) + + it "should not count deletes in the running length total", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ + { d: "deleted", meta: @meta } + { u: "foobar" } + ], + { op: { p: 3, i: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { d: "deleted", meta: @meta } + { u: "foo" } + { i: "baz", meta: @meta } + { u: "bar" } + ]) + + describe "a delete", -> + describe "deleting unchanged text", -> + it "should delete from the middle of (u)nchanged text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foobazbar" } ], + { op: { p: 3, d: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "foo" } + { d: "baz", meta: @meta } + { u: "bar" } + ]) + + it "should delete from the start of (u)nchanged text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foobazbar" } ], + { op: { p: 0, d: "foo" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { d: "foo", meta: @meta } + { u: "bazbar" } + ]) + + it "should delete from the end of (u)nchanged text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foobazbar" } ], + { op: { p: 6, d: "bar" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "foobaz" } + { d: "bar", meta: @meta } + ]) + + it "should delete across multiple (u)changed text parts", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { u: "baz" }, { u: "bar" } ], + { op: { p: 2, d: "obazb" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "fo" } + { d: "o", meta: @meta } + { d: "baz", meta: @meta } + { d: "b", meta: @meta } + { u: "ar" } + ]) + + describe "deleting inserts", -> + it "should delete from the middle of (i)nserted text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { i: "foobazbar", meta: @meta } ], + { op: { p: 3, d: "baz" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { i: "foo", meta: @meta } + { i: "bar", meta: @meta } + ]) + + it "should delete from the start of (u)nchanged text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { i: "foobazbar", meta: @meta } ], + { op: { p: 0, d: "foo" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { i: "bazbar", meta: @meta } + ]) + + it "should delete from the end of (u)nchanged text", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { i: "foobazbar", meta: @meta } ], + { op: { p: 6, d: "bar" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { i: "foobaz", meta: @meta } + ]) + + it "should delete across multiple (u)changed and (i)nserted text parts", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { i: "baz", meta: @meta }, { u: "bar" } ], + { op: { p: 2, d: "obazb" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "fo" } + { d: "o", meta: @meta } + { d: "b", meta: @meta } + { u: "ar" } + ]) + + describe "deleting over existing deletes", -> + it "should delete across multiple (u)changed and (d)deleted text parts", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { d: "baz", meta: @meta }, { u: "bar" } ], + { op: { p: 2, d: "ob" }, meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "fo" } + { d: "o", meta: @meta } + { d: "baz", meta: @meta } + { d: "b", meta: @meta } + { u: "ar" } + ]) + + describe "deleting when the text doesn't match", -> + it "should throw an error when deleting from the middle of (u)nchanged text", -> + expect( + () => @DiffGenerator.applyUpdateToDiff( + [ { u: "foobazbar" } ], + { op: { p: 3, d: "xxx" }, meta: @meta } + ) + ).to.throw(@DiffGenerator.ConsistencyError) + + it "should throw an error when deleting from the start of (u)nchanged text", -> + expect( + () => @DiffGenerator.applyUpdateToDiff( + [ { u: "foobazbar" } ], + { op: { p: 0, d: "xxx" }, meta: @meta } + ) + ).to.throw(@DiffGenerator.ConsistencyError) + + it "should throw an error when deleting from the end of (u)nchanged text", -> + expect( + () => @DiffGenerator.applyUpdateToDiff( + [ { u: "foobazbar" } ], + { op: { p: 6, d: "xxx" }, meta: @meta } + ) + ).to.throw(@DiffGenerator.ConsistencyError) + + From c15df4c2aa43fc251fa0664e5cf600b2ba762c6a Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 3 Mar 2014 13:14:01 +0000 Subject: [PATCH 037/549] Add in /status end point --- services/track-changes/app.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 7334c95682..fdbabacd46 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -10,6 +10,9 @@ app.use express.logger() app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock +app.get "/status", (req, res, next) -> + res.send "track-changes is alive" + app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" res.send 500 From ba2c4768d06d361365a1968b26b960ea1269246d Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 3 Mar 2014 13:31:10 +0000 Subject: [PATCH 038/549] Use port 3015 by default --- services/track-changes/app.coffee | 2 +- services/track-changes/config/settings.development.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index fdbabacd46..83c768f889 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -17,7 +17,7 @@ app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" res.send 500 -port = Settings.internal?.trackchanges?.port or 3014 +port = Settings.internal?.trackchanges?.port or 3015 host = Settings.internal?.trackchanges?.host or "localhost" app.listen port, host, (error) -> if error? diff --git a/services/track-changes/config/settings.development.coffee b/services/track-changes/config/settings.development.coffee index ee758aac0e..56d0a90eb8 100755 --- a/services/track-changes/config/settings.development.coffee +++ b/services/track-changes/config/settings.development.coffee @@ -3,5 +3,5 @@ module.exports = url: 'mongodb://127.0.0.1/sharelatex' internal: trackchanges: - port: 3014 + port: 3015 host: "localhost" From 8d044b7f13380c5e9f0f09f1908a6f1398172047 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 3 Mar 2014 13:36:05 +0000 Subject: [PATCH 039/549] Fix acceptance tests --- .../test/acceptance/coffee/AppendingUpdatesTests.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 6867dd901b..31c4a012eb 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -11,7 +11,7 @@ rclient = require("redis").createClient() # Only works locally for now flushAndGetCompressedUpdates = (doc_id, callback = (error, updates) ->) -> request.post { - url: "http://localhost:3014/doc/#{doc_id}/flush" + url: "http://localhost:3015/doc/#{doc_id}/flush" }, (error, response, body) => response.statusCode.should.equal 204 db.docHistory From 20d70859aa79b21b8a432d38ad1f113e434b4fc9 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 3 Mar 2014 17:39:59 +0000 Subject: [PATCH 040/549] Create buildDiff function --- .../app/coffee/DiffGenerator.coffee | 4 +++ .../DiffGenerator/DiffGeneratorTests.coffee | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index 9ea4682e31..fdec79d6f4 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -28,6 +28,10 @@ module.exports = DiffGenerator = return content buildDiff: (initialContent, updates) -> + diff = [ u: initialContent ] + for update in updates + diff = DiffGenerator.applyUpdateToDiff diff, update + return diff applyUpdateToDiff: (diff, update) -> position = 0 diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee index 47c4561074..18b2e637e1 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -50,6 +50,34 @@ describe "DiffGenerator", -> rewoundContent = @DiffGenerator.rewindUpdates content, updates rewoundContent.should.equal "aaa" + describe "buildDiff", -> + beforeEach -> + @diff = [ u: "mock-diff" ] + @content = "Hello world" + @updates = [ + { i: "mock-update-1" } + { i: "mock-update-2" } + { i: "mock-update-3" } + ] + @DiffGenerator.applyUpdateToDiff = sinon.stub().returns(@diff) + @result = @DiffGenerator.buildDiff(@content, @updates) + + it "should return the diff", -> + @result.should.deep.equal @diff + + it "should build the content into an initial diff", -> + @DiffGenerator.applyUpdateToDiff + .calledWith([{ + u: @content + }], @updates[0]) + .should.equal true + + it "should apply each update", -> + for update in @updates + @DiffGenerator.applyUpdateToDiff + .calledWith(sinon.match.any, update) + .should.equal true + describe "applyUpdateToDiff", -> describe "an insert", -> it "should insert into the middle of (u)nchanged text", -> From 1d1dcdfa2f3347c706ae69965d2b3add3f07b6a5 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 4 Mar 2014 13:02:48 +0000 Subject: [PATCH 041/549] Add in methods for retrieving updates and doc version --- services/track-changes/app.coffee | 2 + .../app/coffee/DiffManager.coffee | 8 +++ .../app/coffee/DocumentUpdaterManager.coffee | 21 ++++++++ .../app/coffee/MongoManager.coffee | 16 +++++- .../DocumentUpdaterManagerTests.coffee | 52 +++++++++++++++++++ .../MongoManager/MongoManagerTests.coffee | 30 +++++++++++ 6 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 services/track-changes/app/coffee/DiffManager.coffee create mode 100644 services/track-changes/app/coffee/DocumentUpdaterManager.coffee create mode 100644 services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 83c768f889..68e11cc486 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -2,6 +2,8 @@ Settings = require "settings-sharelatex" logger = require "logger-sharelatex" logger.initialize("track-changes") +require("./app/js/MongoManager").ensureIndices() + HttpController = require "./app/js/HttpController" express = require "express" app = express() diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee new file mode 100644 index 0000000000..f1186293e8 --- /dev/null +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -0,0 +1,8 @@ +module.exports = DiffManager = + getDiff: (doc_id, fromDate, toDate, callback = (error, diff) ->) -> + # Flush diff + # Get doc content and version + # Get updates from Mongo + # Check version matches + # Build diff + # Return diff \ No newline at end of file diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee new file mode 100644 index 0000000000..0b02a9a16c --- /dev/null +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee @@ -0,0 +1,21 @@ +request = require "request" +logger = require "logger-sharelatex" +Settings = require "settings-sharelatex" + +module.exports = DocumentUpdaterManager = + getDocument: (project_id, doc_id, callback = (error, content, version) ->) -> + url = "#{Settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}" + logger.log project_id:project_id, doc_id: doc_id, "getting doc from document updater" + request.get url, (error, res, body)-> + if error? + return callback(error) + if res.statusCode >= 200 and res.statusCode < 300 + try + body = JSON.parse(body) + catch error + return callback(error) + callback null, body.lines, body.version + else + error = new Error("doc updater returned a non-success status code: #{res.statusCode}") + logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" + callback error \ No newline at end of file diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index dc17f12d76..b194188992 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -37,4 +37,18 @@ module.exports = MongoManager = op: update.op meta: update.meta v: update.v - }, callback \ No newline at end of file + }, callback + + getUpdatesBetweenDates:(doc_id, fromDate, toDate, callback = (error, updates) ->) -> + db.docHistory + .find({ + doc_id: ObjectId(doc_id.toString()) + "meta.start_ts" : { $gte: fromDate } + "meta.end_ts" : { $lte: toDate } + }) + .sort( "meta.end_ts": -1 ) + .toArray callback + + ensureIndices: (callback = (error) ->) -> + db.docHistory.ensureIndex { doc_id: 1, "meta.start_ts": 1, "meta.end_ts": 1 }, callback + diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee new file mode 100644 index 0000000000..5941eefd8d --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee @@ -0,0 +1,52 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/DocumentUpdaterManager.js" +SandboxedModule = require('sandboxed-module') + +describe "DocumentUpdaterManager", -> + beforeEach -> + @DocumentUpdaterManager = SandboxedModule.require modulePath, requires: + "request": @request = {} + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + 'settings-sharelatex': @settings = + apis : documentupdater: url : "http://example.com" + @callback = sinon.stub() + @lines = ["one", "two", "three"] + @version = 42 + + describe "getDocument", -> + describe "successfully", -> + beforeEach -> + @body = JSON.stringify + lines: @lines + version: @version + ops: [] + @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) + @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + + it 'should get the document from the document updater', -> + url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}" + @request.get.calledWith(url).should.equal true + + it "should call the callback with the lines and version", -> + @callback.calledWith(null, @lines, @version, @ops).should.equal true + + describe "when the document updater API returns an error", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) + @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + + it "should return an error to the callback", -> + @callback.calledWith(@error).should.equal true + + describe "when the document updater returns a failure error code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") + @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + + it "should return the callback with an error", -> + @callback + .calledWith(new Error("doc updater returned failure status code: 500")) + .should.equal true \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 0aa43677e4..ab9fb73ddf 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -131,3 +131,33 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true + + describe "getUpdatesBetweenDates", -> + beforeEach -> + @updates = ["mock-update"] + @db.docHistory = {} + @db.docHistory.find = sinon.stub().returns @db.docHistory + @db.docHistory.sort = sinon.stub().returns @db.docHistory + @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates) + + @from = new Date(Date.now()) + @to = new Date(Date.now() + 100000) + + @MongoManager.getUpdatesBetweenDates @doc_id, @from, @to, @callback + + it "should find the updates for the doc", -> + @db.docHistory.find + .calledWith({ + doc_id: ObjectId(@doc_id) + "meta.start_ts": { $gte: @from } + "meta.end_ts": { $lte: @to } + }) + .should.equal true + + it "should sort in descending timestamp order", -> + @db.docHistory.sort + .calledWith("meta.end_ts": -1) + .should.equal true + + it "should call the call back with the updates", -> + @callback.calledWith(null, @updates).should.equal true From 8b71d222d42c7dbcacb265737d563ffc39bcc250 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 4 Mar 2014 14:05:17 +0000 Subject: [PATCH 042/549] Create DiffManager.getDiff --- .../app/coffee/DiffManager.coffee | 41 +++++-- .../app/coffee/MongoManager.coffee | 12 +- .../DiffManager/DiffManagerTests.coffee | 110 ++++++++++++++++++ .../MongoManager/MongoManagerTests.coffee | 48 +++++--- 4 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index f1186293e8..4c45661aa3 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -1,8 +1,35 @@ +HistoryManager = require "./HistoryManager" +DocumentUpdaterManager = require "./DocumentUpdaterManager" +MongoManager = require "./MongoManager" +DiffGenerator = require "./DiffGenerator" + module.exports = DiffManager = - getDiff: (doc_id, fromDate, toDate, callback = (error, diff) ->) -> - # Flush diff - # Get doc content and version - # Get updates from Mongo - # Check version matches - # Build diff - # Return diff \ No newline at end of file + getLatestDocAndUpdates: (project_id, doc_id, fromDate, toDate, callback = (error, lines, version, updates) ->) -> + HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) -> + return callback(error) if error? + DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) -> + return callback(error) if error? + MongoManager.getUpdatesBetweenDates doc_id, fromDate, toDate, (error, updates) -> + return callback(error) if error? + callback(null, lines, version, updates) + + getDiff: (project_id, doc_id, fromDate, toDate, callback = (error, diff) ->) -> + DiffManager.getLatestDocAndUpdates project_id, doc_id, fromDate, null, (error, lines, version, updates) -> + return callback(error) if error? + lastUpdate = updates[updates.length - 1] + if lastUpdate? and lastUpdate.v != version + return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") + + + updatesToApply = [] + for update in updates + if update.meta.end_ts <= toDate + updatesToApply.push update + + try + startingContent = DiffGenerator.rewindUpdates lines.join("\n"), updates + diff = DiffGenerator.buildDiff startingContent, updatesToApply + catch e + return callback(e) + + callback(null, diff) \ No newline at end of file diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index b194188992..1f884d0b67 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -40,12 +40,14 @@ module.exports = MongoManager = }, callback getUpdatesBetweenDates:(doc_id, fromDate, toDate, callback = (error, updates) ->) -> + query = + doc_id: ObjectId(doc_id.toString()) + if fromDate? + query["meta.start_ts"] = { $gte: fromDate } + if toDate? + query["meta.end_ts"] = { $lte: toDate } db.docHistory - .find({ - doc_id: ObjectId(doc_id.toString()) - "meta.start_ts" : { $gte: fromDate } - "meta.end_ts" : { $lte: toDate } - }) + .find( query ) .sort( "meta.end_ts": -1 ) .toArray callback diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee new file mode 100644 index 0000000000..332a73120d --- /dev/null +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -0,0 +1,110 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/DiffManager.js" +SandboxedModule = require('sandboxed-module') + +describe "DiffManager", -> + beforeEach -> + @DiffManager = SandboxedModule.require modulePath, requires: + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "./HistoryManager": @HistoryManager = {} + "./DocumentUpdaterManager": @DocumentUpdaterManager = {} + "./MongoManager": @MongoManager = {} + "./DiffGenerator": @DiffGenerator = {} + @callback = sinon.stub() + @from = new Date() + @to = new Date(Date.now() + 10000) + @project_id = "mock-project-id" + @doc_id = "mock-doc-id" + + describe "getLatestDocAndUpdates", -> + beforeEach -> + @lines = [ "hello", "world" ] + @version = 42 + @updates = [ "mock-update-1", "mock-update-2" ] + + @HistoryManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) + @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @lines, @version) + @MongoManager.getUpdatesBetweenDates = sinon.stub().callsArgWith(3, null, @updates) + @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback + + it "should ensure the latest updates have been compressed", -> + @HistoryManager.processUncompressedUpdatesWithLock + .calledWith(@doc_id) + .should.equal true + + it "should get the latest version of the doc", -> + @DocumentUpdaterManager.getDocument + .calledWith(@project_id, @doc_id) + .should.equal true + + it "should get the requested updates from Mongo", -> + @MongoManager.getUpdatesBetweenDates + .calledWith(@doc_id, @from, @to) + .should.equal true + + it "should call the callback with the lines, version and updates", -> + @callback.calledWith(null, @lines, @version, @updates).should.equal true + + describe "getDiff", -> + beforeEach -> + @lines = [ "hello", "world" ] + @version = 42 + @updates = [ + { op: "mock-1", v: 41, meta: { end_ts: new Date(@to.getTime() - 10)} } + { op: "mock-2", v: 42, meta: { end_ts: new Date(@to.getTime() + 10)} } + ] + @diffed_updates = @updates.slice(0,1) + @rewound_content = "rewound-content" + @diff = [ u: "mock-diff" ] + + describe "with matching versions", -> + beforeEach -> + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) + @DiffGenerator.rewindUpdates = sinon.stub().returns(@rewound_content) + @DiffGenerator.buildDiff = sinon.stub().returns(@diff) + @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback + + it "should get the latest doc and version with all recent updates", -> + @DiffManager.getLatestDocAndUpdates + .calledWith(@project_id, @doc_id, @from, null) + .should.equal true + + it "should rewind the diff", -> + @DiffGenerator.rewindUpdates + .calledWith(@lines.join("\n"), @updates) + .should.equal true + + it "should generate the diff", -> + @DiffGenerator.buildDiff + .calledWith(@rewound_content, @diffed_updates) + .should.equal true + + it "should call the callback with the diff", -> + @callback.calledWith(null, @diff).should.equal true + + describe "with mismatching versions", -> + beforeEach -> + @version = 42 + @updates = [ { op: "mock-1", v: 39 }, { op: "mock-1", v: 40 } ] + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) + @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback + + it "should call the callback with an error", -> + @callback + .calledWith(new Error("latest update version, 40, does not match doc version, 42")) + .should.equal true + + describe "when the updates are inconsistent", -> + beforeEach -> + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) + @DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!")) + @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback + + it "should call the callback with an error", -> + @callback + .calledWith(@error) + .should.equal true + diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index ab9fb73ddf..21f57c6477 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -143,21 +143,39 @@ describe "MongoManager", -> @from = new Date(Date.now()) @to = new Date(Date.now() + 100000) - @MongoManager.getUpdatesBetweenDates @doc_id, @from, @to, @callback + describe "with a toDate", -> + beforeEach -> + @MongoManager.getUpdatesBetweenDates @doc_id, @from, @to, @callback - it "should find the updates for the doc", -> - @db.docHistory.find - .calledWith({ - doc_id: ObjectId(@doc_id) - "meta.start_ts": { $gte: @from } - "meta.end_ts": { $lte: @to } - }) - .should.equal true + it "should find the all updates between the to and from date", -> + @db.docHistory.find + .calledWith({ + doc_id: ObjectId(@doc_id) + "meta.start_ts": { $gte: @from } + "meta.end_ts": { $lte: @to } + }) + .should.equal true - it "should sort in descending timestamp order", -> - @db.docHistory.sort - .calledWith("meta.end_ts": -1) - .should.equal true + it "should sort in descending timestamp order", -> + @db.docHistory.sort + .calledWith("meta.end_ts": -1) + .should.equal true + + it "should call the call back with the updates", -> + @callback.calledWith(null, @updates).should.equal true + + describe "without a todo date", -> + beforeEach -> + @MongoManager.getUpdatesBetweenDates @doc_id, @from, null, @callback + + it "should find the all updates after the from date", -> + @db.docHistory.find + .calledWith({ + doc_id: ObjectId(@doc_id) + "meta.start_ts": { $gte: @from } + }) + .should.equal true + + it "should call the call back with the updates", -> + @callback.calledWith(null, @updates).should.equal true - it "should call the call back with the updates", -> - @callback.calledWith(null, @updates).should.equal true From e4d8cc7a11052b1b0e4621afab207f93acf41a93 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 4 Mar 2014 15:27:03 +0000 Subject: [PATCH 043/549] Add in acceptance tests for getting a diff --- services/track-changes/app.coffee | 2 + .../app/coffee/DiffManager.coffee | 15 ++-- .../app/coffee/HttpController.coffee | 21 +++++- .../config/settings.development.coffee | 3 + .../coffee/AppendingUpdatesTests.coffee | 34 +++------ .../coffee/GettingADiffTests.coffee | 70 +++++++++++++++++++ .../coffee/helpers/MockDocUpdaterApi.coffee | 24 +++++++ .../coffee/helpers/TrackChangesClient.coffee | 24 +++++++ .../DiffManager/DiffManagerTests.coffee | 12 ++-- .../HttpController/HttpControllerTests.coffee | 29 +++++++- 10 files changed, 200 insertions(+), 34 deletions(-) create mode 100644 services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee create mode 100644 services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee create mode 100644 services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 68e11cc486..c8c0be3c56 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -12,6 +12,8 @@ app.use express.logger() app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock +app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff + app.get "/status", (req, res, next) -> res.send "track-changes is alive" diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 4c45661aa3..eddb461c53 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -2,6 +2,7 @@ HistoryManager = require "./HistoryManager" DocumentUpdaterManager = require "./DocumentUpdaterManager" MongoManager = require "./MongoManager" DiffGenerator = require "./DiffGenerator" +logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromDate, toDate, callback = (error, lines, version, updates) ->) -> @@ -14,20 +15,26 @@ module.exports = DiffManager = callback(null, lines, version, updates) getDiff: (project_id, doc_id, fromDate, toDate, callback = (error, diff) ->) -> + logger.log project_id: project_id, doc_id: doc_id, from: fromDate, to: toDate, "getting diff" DiffManager.getLatestDocAndUpdates project_id, doc_id, fromDate, null, (error, lines, version, updates) -> return callback(error) if error? - lastUpdate = updates[updates.length - 1] + + logger.log lines: lines, version: version, updates: updates, "got doc and updates" + + lastUpdate = updates[0] if lastUpdate? and lastUpdate.v != version return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") - updatesToApply = [] - for update in updates - if update.meta.end_ts <= toDate + for update in updates.reverse() + if update.meta.start_ts <= toDate updatesToApply.push update + logger.log project_id: project_id, doc_id: doc_id, updatesToApply: updatesToApply, "got updates to apply" + try startingContent = DiffGenerator.rewindUpdates lines.join("\n"), updates + logger.log project_id: project_id, doc_id: doc_id, startingContent: startingContent, "rewound doc" diff = DiffGenerator.buildDiff startingContent, updatesToApply catch e return callback(e) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 1fd1926317..5b742e8891 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -1,4 +1,5 @@ HistoryManager = require "./HistoryManager" +DiffManager = require "./DiffManager" logger = require "logger-sharelatex" module.exports = HttpController = @@ -7,5 +8,23 @@ module.exports = HttpController = logger.log doc_id: doc_id, "compressing doc history" HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) -> return next(error) if error? - logger.log "done http request" res.send 204 + + getDiff: (req, res, next = (error) ->) -> + doc_id = req.params.doc_id + project_id = req.params.project_id + + if req.query.from? + from = parseInt(req.query.from, 10) + else + from = null + if req.query.to? + to = parseInt(req.query.to, 10) + else + to = null + + logger.log project_id, doc_id: doc_id, from: from, to: to, "getting diff" + DiffManager.getDiff project_id, doc_id, from, to, (error, diff) -> + return next(error) if error? + res.send JSON.stringify(diff: diff) + diff --git a/services/track-changes/config/settings.development.coffee b/services/track-changes/config/settings.development.coffee index 56d0a90eb8..d3cf2978ec 100755 --- a/services/track-changes/config/settings.development.coffee +++ b/services/track-changes/config/settings.development.coffee @@ -5,3 +5,6 @@ module.exports = trackchanges: port: 3015 host: "localhost" + apis: + documentupdater: + url: "http://localhost:3003" diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 31c4a012eb..031659f24b 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -3,31 +3,19 @@ chai = require("chai") chai.should() expect = chai.expect mongojs = require "../../../app/js/mongojs" -db = mongojs.db ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" request = require "request" rclient = require("redis").createClient() # Only works locally for now -flushAndGetCompressedUpdates = (doc_id, callback = (error, updates) ->) -> - request.post { - url: "http://localhost:3015/doc/#{doc_id}/flush" - }, (error, response, body) => - response.statusCode.should.equal 204 - db.docHistory - .find(doc_id: ObjectId(doc_id)) - .sort("meta.end_ts": 1) - .toArray callback - -pushRawUpdates = (doc_id, updates, callback = (error) ->) -> - rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback +TrackChangesClient = require "./helpers/TrackChangesClient" describe "Appending doc ops to the history", -> describe "when the history does not exist yet", -> before (done) -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } v: 3 @@ -41,7 +29,7 @@ describe "Appending doc ops to the history", -> v: 5 }], (error) => throw error if error? - flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => throw error if error? done() @@ -57,7 +45,7 @@ describe "Appending doc ops to the history", -> beforeEach (done) -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } v: 3 @@ -71,13 +59,13 @@ describe "Appending doc ops to the history", -> v: 5 }], (error) => throw error if error? - flushAndGetCompressedUpdates @doc_id, (error, updates) => + TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, updates) => throw error if error? done() describe "when the updates are recent and from the same user", -> beforeEach (done) -> - pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now(), user_id: @user_id } v: 6 @@ -91,7 +79,7 @@ describe "Appending doc ops to the history", -> v: 8 }], (error) => throw error if error? - flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => throw error if error? done() @@ -107,7 +95,7 @@ describe "Appending doc ops to the history", -> describe "when the updates are far apart", -> beforeEach (done) -> oneDay = 24 * 60 * 60 * 1000 - pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now() + oneDay, user_id: @user_id } v: 6 @@ -121,7 +109,7 @@ describe "Appending doc ops to the history", -> v: 8 }], (error) => throw error if error? - flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => throw error if error? done() @@ -147,9 +135,9 @@ describe "Appending doc ops to the history", -> } @expectedOp.i = "a" + @expectedOp.i - pushRawUpdates @doc_id, updates, (error) => + TrackChangesClient.pushRawUpdates @doc_id, updates, (error) => throw error if error? - flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => throw error if error? done() diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee new file mode 100644 index 0000000000..02d83f0044 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -0,0 +1,70 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +expect = chai.expect +mongojs = require "../../../app/js/mongojs" +db = mongojs.db +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" + +TrackChangesClient = require "./helpers/TrackChangesClient" +MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" + +describe "Getting a diff", -> + before (done) -> + sinon.spy MockDocUpdaterApi, "getDoc" + + @now = Date.now() + @from = @now - 100000000 + @to = @now + @user_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + + twoMinutes = 2 * 60 * 1000 + + @updates = [{ + op: [{ i: "one ", p: 0 }] + meta: { ts: @from - twoMinutes, user_id: @user_id } + v: 3 + }, { + op: [{ i: "two ", p: 4 }] + meta: { ts: @from + twoMinutes, user_id: @user_id } + v: 4 + }, { + op: [{ i: "three ", p: 8 }] + meta: { ts: @to - twoMinutes, user_id: @user_id } + v: 5 + }, { + op: [{ i: "four", p: 14 }] + meta: { ts: @to + twoMinutes, user_id: @user_id } + v: 6 + }] + @lines = ["one two three four"] + @expected_diff = [ + { u: "one " } + { i: "two ", meta: { start_ts: @from + twoMinutes, end_ts: @from + twoMinutes, user_id: @user_id } } + { i: "three ", meta: { start_ts: @to - twoMinutes, end_ts: @to - twoMinutes, user_id: @user_id } } + ] + + MockDocUpdaterApi.docs[@doc_id] = + lines: @lines + version: 6 + + TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + throw error if error? + TrackChangesClient.getDiff @project_id, @doc_id, @from, @to, (error, diff) => + throw error if error? + @diff = diff.diff + done() + + after () -> + MockDocUpdaterApi.getDoc.restore() + + it "should return the diff", -> + expect(@diff).to.deep.equal @expected_diff + + it "should get the doc from the doc updater", -> + MockDocUpdaterApi.getDoc + .calledWith(@project_id, @doc_id) + .should.equal true diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee new file mode 100644 index 0000000000..a64098503c --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -0,0 +1,24 @@ +express = require("express") +app = express() + +module.exports = MockDocUpdaterApi = + docs: {} + + getDoc: (project_id, doc_id, callback = (error) ->) -> + callback null, @docs[doc_id] + + run: () -> + app.get "/project/:project_id/doc/:doc_id", (req, res, next) => + @getDoc req.params.project_id, req.params.doc_id, (error, doc) -> + if error? + res.send 500 + if !doc? + res.send 404 + else + res.send JSON.stringify doc + + app.listen 3003, (error) -> + throw error if error? + +MockDocUpdaterApi.run() + diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee new file mode 100644 index 0000000000..52a5552dd6 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -0,0 +1,24 @@ +request = require "request" +rclient = require("redis").createClient() # Only works locally for now +{db, ObjectId} = require "../../../../app/js/mongojs" + +module.exports = TrackChangesClient = + flushAndGetCompressedUpdates: (doc_id, callback = (error, updates) ->) -> + request.post { + url: "http://localhost:3015/doc/#{doc_id}/flush" + }, (error, response, body) => + response.statusCode.should.equal 204 + db.docHistory + .find(doc_id: ObjectId(doc_id)) + .sort("meta.end_ts": 1) + .toArray callback + + pushRawUpdates: (doc_id, updates, callback = (error) ->) -> + rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback + + getDiff: (project_id, doc_id, from, to, callback = (error, diff) ->) -> + request.get { + url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/diff?from=#{from}&to=#{to}" + }, (error, response, body) => + response.statusCode.should.equal 200 + callback null, JSON.parse(body) \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 332a73120d..391ad138cb 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -53,10 +53,12 @@ describe "DiffManager", -> @lines = [ "hello", "world" ] @version = 42 @updates = [ - { op: "mock-1", v: 41, meta: { end_ts: new Date(@to.getTime() - 10)} } - { op: "mock-2", v: 42, meta: { end_ts: new Date(@to.getTime() + 10)} } + { op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} } + { op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} } + { op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} } + { op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} } ] - @diffed_updates = @updates.slice(0,1) + @diffed_updates = @updates.slice(2) @rewound_content = "rewound-content" @diff = [ u: "mock-diff" ] @@ -79,7 +81,7 @@ describe "DiffManager", -> it "should generate the diff", -> @DiffGenerator.buildDiff - .calledWith(@rewound_content, @diffed_updates) + .calledWith(@rewound_content, @diffed_updates.reverse()) .should.equal true it "should call the callback with the diff", -> @@ -88,7 +90,7 @@ describe "DiffManager", -> describe "with mismatching versions", -> beforeEach -> @version = 42 - @updates = [ { op: "mock-1", v: 39 }, { op: "mock-1", v: 40 } ] + @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index a0d7c23166..89e03ff6ea 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -10,7 +10,9 @@ describe "HttpController", -> @HttpController = SandboxedModule.require modulePath, requires: "logger-sharelatex": { log: sinon.stub() } "./HistoryManager": @HistoryManager = {} + "./DiffManager": @DiffManager = {} @doc_id = "doc-id-123" + @project_id = "project-id-123" @version = 42 @next = sinon.stub() @@ -30,4 +32,29 @@ describe "HttpController", -> .should.equal true it "should return a success code", -> - @res.send.calledWith(204).should.equal true \ No newline at end of file + @res.send.calledWith(204).should.equal true + + describe "getDiff", -> + beforeEach -> + @from = Date.now() - 10000 + @to = Date.now() + @req = + params: + doc_id: @doc_id + project_id: @project_id + query: + from: @from.toString() + to: @to.toString() + @res = + send: sinon.stub() + @diff = [ u: "mock-diff" ] + @DiffManager.getDiff = sinon.stub().callsArgWith(4, null, @diff) + @HttpController.getDiff @req, @res, @next + + it "should get the diff", -> + @DiffManager.getDiff + .calledWith(@project_id, @doc_id, parseInt(@from, 10), parseInt(@to, 10)) + .should.equal true + + it "should return the diff", -> + @res.send.calledWith(JSON.stringify(diff: @diff)).should.equal true From 188d620ce1c4b48b08ebe9509f423091107690a1 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 5 Mar 2014 13:22:38 +0000 Subject: [PATCH 044/549] Log out and put the last update back when an error occurs --- services/track-changes/app/coffee/HistoryManager.coffee | 8 +++++++- .../unit/coffee/HistoryManager/HistoryManagerTests.coffee | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/HistoryManager.coffee index 41feff05c3..276d31ad1c 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/HistoryManager.coffee @@ -21,7 +21,13 @@ module.exports = HistoryManager = rawUpdates.shift() if rawUpdates[0]? and rawUpdates[0].v != lastCompressedUpdate.v + 1 - return callback new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") + error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") + logger.error err: error, doc_id: doc_id, "inconsistent doc versions" + # Push the update back into Mongo - catching errors at this + # point is useless, we're already bailing + MongoManager.insertCompressedUpdates doc_id, [lastCompressedUpdate], () -> + return callback error + return compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates MongoManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee index 88efcfe606..e58c2fc44c 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee @@ -12,7 +12,7 @@ describe "HistoryManager", -> "./MongoManager" : @MongoManager = {} "./RedisManager" : @RedisManager = {} "./LockManager" : @LockManager = {} - "logger-sharelatex": { log: sinon.stub() } + "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } @doc_id = "doc-id-123" @callback = sinon.stub() @@ -111,6 +111,12 @@ describe "HistoryManager", -> .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) .should.equal true + it "should put the popped update back into mongo", -> + @MongoManager.insertCompressedUpdates.calledOnce.should.equal true + @MongoManager.insertCompressedUpdates + .calledWith(@doc_id, [@lastCompressedUpdate]) + .should.equal true + describe "processUncompressedUpdates", -> describe "when there is fewer than one batch to send", -> beforeEach -> From a46963a349d4ab0f60ee8769b3b442b3d7a33563 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 5 Mar 2014 15:06:46 +0000 Subject: [PATCH 045/549] Refactor arguments to MongoManager.getUpdatesBetweenDates --- services/track-changes/app/coffee/DiffManager.coffee | 2 +- .../track-changes/app/coffee/HttpController.coffee | 5 +++++ services/track-changes/app/coffee/MongoManager.coffee | 10 +++++----- .../unit/coffee/DiffManager/DiffManagerTests.coffee | 4 ++-- .../unit/coffee/MongoManager/MongoManagerTests.coffee | 10 +++++----- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index eddb461c53..ab6ea41b4b 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -10,7 +10,7 @@ module.exports = DiffManager = return callback(error) if error? DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) -> return callback(error) if error? - MongoManager.getUpdatesBetweenDates doc_id, fromDate, toDate, (error, updates) -> + MongoManager.getUpdatesBetweenDates doc_id, from: fromDate, to: toDate, (error, updates) -> return callback(error) if error? callback(null, lines, version, updates) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 5b742e8891..b33eea596e 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -28,3 +28,8 @@ module.exports = HttpController = return next(error) if error? res.send JSON.stringify(diff: diff) + getUpdates: (req, res, next = (error) ->) -> + doc_id = req.params.doc_id + project_id = req.params.project_id + + diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1f884d0b67..6a9f10fe73 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -39,13 +39,13 @@ module.exports = MongoManager = v: update.v }, callback - getUpdatesBetweenDates:(doc_id, fromDate, toDate, callback = (error, updates) ->) -> + getUpdatesBetweenDates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) - if fromDate? - query["meta.start_ts"] = { $gte: fromDate } - if toDate? - query["meta.end_ts"] = { $lte: toDate } + if options.from? + query["meta.end_ts"] = { $gte: options.from } + if options.to? + query["meta.start_ts"] = { $lte: options.to } db.docHistory .find( query ) .sort( "meta.end_ts": -1 ) diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 391ad138cb..b057425ff5 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -27,7 +27,7 @@ describe "DiffManager", -> @HistoryManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @lines, @version) - @MongoManager.getUpdatesBetweenDates = sinon.stub().callsArgWith(3, null, @updates) + @MongoManager.getUpdatesBetweenDates = sinon.stub().callsArgWith(2, null, @updates) @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback it "should ensure the latest updates have been compressed", -> @@ -42,7 +42,7 @@ describe "DiffManager", -> it "should get the requested updates from Mongo", -> @MongoManager.getUpdatesBetweenDates - .calledWith(@doc_id, @from, @to) + .calledWith(@doc_id, from: @from, to: @to) .should.equal true it "should call the callback with the lines, version and updates", -> diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 21f57c6477..9a0d83169a 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -145,14 +145,14 @@ describe "MongoManager", -> describe "with a toDate", -> beforeEach -> - @MongoManager.getUpdatesBetweenDates @doc_id, @from, @to, @callback + @MongoManager.getUpdatesBetweenDates @doc_id, from: @from, to: @to, @callback it "should find the all updates between the to and from date", -> @db.docHistory.find .calledWith({ doc_id: ObjectId(@doc_id) - "meta.start_ts": { $gte: @from } - "meta.end_ts": { $lte: @to } + "meta.end_ts": { $gte: @from } + "meta.start_ts": { $lte: @to } }) .should.equal true @@ -166,13 +166,13 @@ describe "MongoManager", -> describe "without a todo date", -> beforeEach -> - @MongoManager.getUpdatesBetweenDates @doc_id, @from, null, @callback + @MongoManager.getUpdatesBetweenDates @doc_id, from: @from, @callback it "should find the all updates after the from date", -> @db.docHistory.find .calledWith({ doc_id: ObjectId(@doc_id) - "meta.start_ts": { $gte: @from } + "meta.end_ts": { $gte: @from } }) .should.equal true From 3660253fd41a76830393a49263821fc8010dfbcc Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 5 Mar 2014 15:59:40 +0000 Subject: [PATCH 046/549] Add in /updates end point to get updates --- services/track-changes/app.coffee | 2 + .../app/coffee/DiffManager.coffee | 9 +-- .../app/coffee/HttpController.coffee | 19 +++++- .../app/coffee/MongoManager.coffee | 11 +++- ...ryManager.coffee => UpdatesManager.coffee} | 17 +++-- .../coffee/GettingUpdatesTests.coffee | 58 +++++++++++++++++ .../coffee/helpers/TrackChangesClient.coffee | 7 ++ .../DiffManager/DiffManagerTests.coffee | 15 ++--- .../HttpController/HttpControllerTests.coffee | 40 +++++++++++- .../MongoManager/MongoManagerTests.coffee | 21 +++++- .../UpdatesManagerTests.coffee} | 64 +++++++++++++------ 11 files changed, 208 insertions(+), 55 deletions(-) rename services/track-changes/app/coffee/{HistoryManager.coffee => UpdatesManager.coffee} (81%) create mode 100644 services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee rename services/track-changes/test/unit/coffee/{HistoryManager/HistoryManagerTests.coffee => UpdatesManager/UpdatesManagerTests.coffee} (77%) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index c8c0be3c56..ac6215e20d 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -14,6 +14,8 @@ app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff +app.get "/project/:project_id/doc/:doc_id/updates", HttpController.getUpdates + app.get "/status", (req, res, next) -> res.send "track-changes is alive" diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index ab6ea41b4b..1128533028 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -1,18 +1,15 @@ -HistoryManager = require "./HistoryManager" +UpdatesManager = require "./UpdatesManager" DocumentUpdaterManager = require "./DocumentUpdaterManager" -MongoManager = require "./MongoManager" DiffGenerator = require "./DiffGenerator" logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromDate, toDate, callback = (error, lines, version, updates) ->) -> - HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) -> + UpdatesManager.getUpdates doc_id, from: fromDate, to: toDate, (error, updates) -> return callback(error) if error? DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) -> return callback(error) if error? - MongoManager.getUpdatesBetweenDates doc_id, from: fromDate, to: toDate, (error, updates) -> - return callback(error) if error? - callback(null, lines, version, updates) + callback(null, lines, version, updates) getDiff: (project_id, doc_id, fromDate, toDate, callback = (error, diff) ->) -> logger.log project_id: project_id, doc_id: doc_id, from: fromDate, to: toDate, "getting diff" diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index b33eea596e..af98464424 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -1,4 +1,4 @@ -HistoryManager = require "./HistoryManager" +UpdatesManager = require "./UpdatesManager" DiffManager = require "./DiffManager" logger = require "logger-sharelatex" @@ -6,7 +6,7 @@ module.exports = HttpController = flushUpdatesWithLock: (req, res, next = (error) ->) -> doc_id = req.params.doc_id logger.log doc_id: doc_id, "compressing doc history" - HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) -> + UpdatesManager.processUncompressedUpdatesWithLock doc_id, (error) -> return next(error) if error? res.send 204 @@ -31,5 +31,18 @@ module.exports = HttpController = getUpdates: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id - + + if req.query.to? + to = parseInt(req.query.to, 10) + if req.query.limit? + limit = parseInt(req.query.limit, 10) + + UpdatesManager.getUpdates doc_id, to: to, limit: limit, (error, updates) -> + return next(error) if error? + formattedUpdates = for update in updates + { + meta: update.meta + } + res.send JSON.stringify updates: formattedUpdates + diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 6a9f10fe73..817f3cac7a 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -39,17 +39,22 @@ module.exports = MongoManager = v: update.v }, callback - getUpdatesBetweenDates:(doc_id, options = {}, callback = (error, updates) ->) -> + getUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) if options.from? query["meta.end_ts"] = { $gte: options.from } if options.to? query["meta.start_ts"] = { $lte: options.to } - db.docHistory + + cursor = db.docHistory .find( query ) .sort( "meta.end_ts": -1 ) - .toArray callback + + if options.limit? + cursor.limit(options.limit) + + cursor.toArray callback ensureIndices: (callback = (error) ->) -> db.docHistory.ensureIndex { doc_id: 1, "meta.start_ts": 1, "meta.end_ts": 1 }, callback diff --git a/services/track-changes/app/coffee/HistoryManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee similarity index 81% rename from services/track-changes/app/coffee/HistoryManager.coffee rename to services/track-changes/app/coffee/UpdatesManager.coffee index 276d31ad1c..158688d03b 100644 --- a/services/track-changes/app/coffee/HistoryManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -4,7 +4,7 @@ UpdateCompressor = require "./UpdateCompressor" LockManager = require "./LockManager" logger = require "logger-sharelatex" -module.exports = HistoryManager = +module.exports = UpdatesManager = compressAndSaveRawUpdates: (doc_id, rawUpdates, callback = (error) ->) -> length = rawUpdates.length if length == 0 @@ -38,20 +38,20 @@ module.exports = HistoryManager = REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (doc_id, callback = (error) ->) -> logger.log "processUncompressedUpdates" - RedisManager.getOldestRawUpdates doc_id, HistoryManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> + RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? length = rawUpdates.length logger.log doc_id: doc_id, length: length, "got raw updates from redis" - HistoryManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> + UpdatesManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> return callback(error) if error? logger.log doc_id: doc_id, "compressed and saved doc updates" RedisManager.deleteOldestRawUpdates doc_id, length, (error) -> return callback(error) if error? - if length == HistoryManager.REDIS_READ_BATCH_SIZE + if length == UpdatesManager.REDIS_READ_BATCH_SIZE # There might be more updates logger.log doc_id: doc_id, "continuing processing updates" setTimeout () -> - HistoryManager.processUncompressedUpdates doc_id, callback + UpdatesManager.processUncompressedUpdates doc_id, callback , 0 else logger.log doc_id: doc_id, "all raw updates processed" @@ -61,7 +61,12 @@ module.exports = HistoryManager = LockManager.runWithLock( "HistoryLock:#{doc_id}", (releaseLock) -> - HistoryManager.processUncompressedUpdates doc_id, releaseLock + UpdatesManager.processUncompressedUpdates doc_id, releaseLock callback ) + getUpdates: (doc_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.processUncompressedUpdatesWithLock doc_id, (error) -> + return callback(error) if error? + MongoManager.getUpdates doc_id, options, callback + diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee new file mode 100644 index 0000000000..6e93f79973 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -0,0 +1,58 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +expect = chai.expect +mongojs = require "../../../app/js/mongojs" +db = mongojs.db +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" + +TrackChangesClient = require "./helpers/TrackChangesClient" + +describe "Getting updates", -> + before (done) -> + @now = Date.now() + @to = @now + @user_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + + @minutes = 60 * 1000 + + @updates = [{ + op: [{ i: "one ", p: 0 }] + meta: { ts: @to - 4 * @minutes, user_id: @user_id } + v: 3 + }, { + op: [{ i: "two ", p: 4 }] + meta: { ts: @to - 2 * @minutes, user_id: @user_id } + v: 4 + }, { + op: [{ i: "three ", p: 8 }] + meta: { ts: @to, user_id: @user_id } + v: 5 + }, { + op: [{ i: "four", p: 14 }] + meta: { ts: @to + 2 * @minutes, user_id: @user_id } + v: 6 + }] + + TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + throw error if error? + TrackChangesClient.getUpdates @project_id, @doc_id, { to: @to, limit: 2 }, (error, body) => + throw error if error? + @updates = body.updates + done() + + it "should return the diff", -> + expect(@updates).to.deep.equal [{ + meta: + start_ts: @to + end_ts: @to + user_id: @user_id + }, { + meta: + start_ts: @to - 2 * @minutes + end_ts: @to - 2 * @minutes + user_id: @user_id + }] diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 52a5552dd6..d2b31f623d 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -19,6 +19,13 @@ module.exports = TrackChangesClient = getDiff: (project_id, doc_id, from, to, callback = (error, diff) ->) -> request.get { url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/diff?from=#{from}&to=#{to}" + }, (error, response, body) => + response.statusCode.should.equal 200 + callback null, JSON.parse(body) + + getUpdates: (project_id, doc_id, options, callback = (error, body) ->) -> + request.get { + url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/updates?to=#{options.to}&limit=#{options.limit}" }, (error, response, body) => response.statusCode.should.equal 200 callback null, JSON.parse(body) \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index b057425ff5..ef4c2d9327 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -9,9 +9,8 @@ describe "DiffManager", -> beforeEach -> @DiffManager = SandboxedModule.require modulePath, requires: "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } - "./HistoryManager": @HistoryManager = {} + "./UpdatesManager": @UpdatesManager = {} "./DocumentUpdaterManager": @DocumentUpdaterManager = {} - "./MongoManager": @MongoManager = {} "./DiffGenerator": @DiffGenerator = {} @callback = sinon.stub() @from = new Date() @@ -25,23 +24,17 @@ describe "DiffManager", -> @version = 42 @updates = [ "mock-update-1", "mock-update-2" ] - @HistoryManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @lines, @version) - @MongoManager.getUpdatesBetweenDates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback - it "should ensure the latest updates have been compressed", -> - @HistoryManager.processUncompressedUpdatesWithLock - .calledWith(@doc_id) - .should.equal true - it "should get the latest version of the doc", -> @DocumentUpdaterManager.getDocument .calledWith(@project_id, @doc_id) .should.equal true - it "should get the requested updates from Mongo", -> - @MongoManager.getUpdatesBetweenDates + it "should get the latest updates", -> + @UpdatesManager.getUpdates .calledWith(@doc_id, from: @from, to: @to) .should.equal true diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 89e03ff6ea..aaea318ff4 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -9,7 +9,7 @@ describe "HttpController", -> beforeEach -> @HttpController = SandboxedModule.require modulePath, requires: "logger-sharelatex": { log: sinon.stub() } - "./HistoryManager": @HistoryManager = {} + "./UpdatesManager": @UpdatesManager = {} "./DiffManager": @DiffManager = {} @doc_id = "doc-id-123" @project_id = "project-id-123" @@ -23,11 +23,11 @@ describe "HttpController", -> doc_id: @doc_id @res = send: sinon.stub() - @HistoryManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) @HttpController.flushUpdatesWithLock @req, @res, @next it "should process the updates", -> - @HistoryManager.processUncompressedUpdatesWithLock + @UpdatesManager.processUncompressedUpdatesWithLock .calledWith(@doc_id) .should.equal true @@ -58,3 +58,37 @@ describe "HttpController", -> it "should return the diff", -> @res.send.calledWith(JSON.stringify(diff: @diff)).should.equal true + + describe "getUpdates", -> + beforeEach -> + @to = Date.now() + @limit = 10 + @req = + params: + doc_id: @doc_id + project_id: @project_id + query: + to: @to.toString() + limit: @limit.toString() + @res = + send: sinon.stub() + @rawUpdates = [{ + v: @v = 42 + op: @op = "mock-op" + meta: @meta = "mock-meta" + doc_id: @doc_id + }] + @UpdatesManager.getUpdates = sinon.stub().callsArgWith(2, null, @rawUpdates) + @HttpController.getUpdates @req, @res, @next + + it "should get the updates", -> + @UpdatesManager.getUpdates + .calledWith(@doc_id, to: @to, limit: @limit) + .should.equal true + + it "should return the updates", -> + updates = for update in @rawUpdates + { + meta: @meta + } + @res.send.calledWith(JSON.stringify(updates: updates)).should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 9a0d83169a..e56d5fc858 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -132,12 +132,13 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "getUpdatesBetweenDates", -> + describe "getUpdates", -> beforeEach -> @updates = ["mock-update"] @db.docHistory = {} @db.docHistory.find = sinon.stub().returns @db.docHistory @db.docHistory.sort = sinon.stub().returns @db.docHistory + @db.docHistory.limit = sinon.stub().returns @db.docHistory @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates) @from = new Date(Date.now()) @@ -145,7 +146,7 @@ describe "MongoManager", -> describe "with a toDate", -> beforeEach -> - @MongoManager.getUpdatesBetweenDates @doc_id, from: @from, to: @to, @callback + @MongoManager.getUpdates @doc_id, from: @from, to: @to, @callback it "should find the all updates between the to and from date", -> @db.docHistory.find @@ -161,12 +162,16 @@ describe "MongoManager", -> .calledWith("meta.end_ts": -1) .should.equal true + it "should not limit the results", -> + @db.docHistory.limit + .called.should.equal false + it "should call the call back with the updates", -> @callback.calledWith(null, @updates).should.equal true describe "without a todo date", -> beforeEach -> - @MongoManager.getUpdatesBetweenDates @doc_id, from: @from, @callback + @MongoManager.getUpdates @doc_id, from: @from, @callback it "should find the all updates after the from date", -> @db.docHistory.find @@ -179,3 +184,13 @@ describe "MongoManager", -> it "should call the call back with the updates", -> @callback.calledWith(null, @updates).should.equal true + describe "with a limit", -> + beforeEach -> + @MongoManager.getUpdates @doc_id, from: @from, limit: @limit = 10, @callback + + it "should limit the results", -> + @db.docHistory.limit + .calledWith(@limit) + .should.equal true + + diff --git a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee similarity index 77% rename from services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee rename to services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index e58c2fc44c..7dfc96169e 100644 --- a/services/track-changes/test/unit/coffee/HistoryManager/HistoryManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -2,12 +2,12 @@ sinon = require('sinon') chai = require('chai') should = chai.should() expect = chai.expect -modulePath = "../../../../app/js/HistoryManager.js" +modulePath = "../../../../app/js/UpdatesManager.js" SandboxedModule = require('sandboxed-module') -describe "HistoryManager", -> +describe "UpdatesManager", -> beforeEach -> - @HistoryManager = SandboxedModule.require modulePath, requires: + @UpdatesManager = SandboxedModule.require modulePath, requires: "./UpdateCompressor": @UpdateCompressor = {} "./MongoManager" : @MongoManager = {} "./RedisManager" : @RedisManager = {} @@ -21,7 +21,7 @@ describe "HistoryManager", -> beforeEach -> @MongoManager.popLastCompressedUpdate = sinon.stub() @MongoManager.insertCompressedUpdates = sinon.stub() - @HistoryManager.compressAndSaveRawUpdates @doc_id, [], @callback + @UpdatesManager.compressAndSaveRawUpdates @doc_id, [], @callback it "should not need to access the database", -> @MongoManager.popLastCompressedUpdate.called.should.equal false @@ -38,7 +38,7 @@ describe "HistoryManager", -> @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> @MongoManager.popLastCompressedUpdate @@ -70,7 +70,7 @@ describe "HistoryManager", -> describe "when the raw ops start where the existing history ends", -> beforeEach -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> @MongoManager.popLastCompressedUpdate @@ -94,7 +94,7 @@ describe "HistoryManager", -> beforeEach -> @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should only compress the more recent raw ops", -> @UpdateCompressor.compressRawUpdates @@ -104,7 +104,7 @@ describe "HistoryManager", -> describe "when the raw ops do not follow from the last compressed op version", -> beforeEach -> @rawUpdates = [{ v: 13, op: "mock-op-13" }] - @HistoryManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback it "should call the callback with an error", -> @callback @@ -122,17 +122,17 @@ describe "HistoryManager", -> beforeEach -> @updates = ["mock-update"] @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) - @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) - @HistoryManager.processUncompressedUpdates @doc_id, @callback + @UpdatesManager.processUncompressedUpdates @doc_id, @callback it "should get the oldest updates", -> @RedisManager.getOldestRawUpdates - .calledWith(@doc_id, @HistoryManager.REDIS_READ_BATCH_SIZE) + .calledWith(@doc_id, @UpdatesManager.REDIS_READ_BATCH_SIZE) .should.equal true it "should compress and save the updates", -> - @HistoryManager.compressAndSaveRawUpdates + @UpdatesManager.compressAndSaveRawUpdates .calledWith(@doc_id, @updates) .should.equal true @@ -146,7 +146,7 @@ describe "HistoryManager", -> describe "when there are multiple batches to send", -> beforeEach (done) -> - @HistoryManager.REDIS_READ_BATCH_SIZE = 2 + @UpdatesManager.REDIS_READ_BATCH_SIZE = 2 @updates = ["mock-update-0", "mock-update-1", "mock-update-2", "mock-update-3", "mock-update-4"] @redisArray = @updates.slice() @RedisManager.getOldestRawUpdates = (doc_id, batchSize, callback = (error, updates) ->) => @@ -154,9 +154,9 @@ describe "HistoryManager", -> @redisArray = @redisArray.slice(batchSize) callback null, updates sinon.spy @RedisManager, "getOldestRawUpdates" - @HistoryManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) - @HistoryManager.processUncompressedUpdates @doc_id, (args...) => + @UpdatesManager.processUncompressedUpdates @doc_id, (args...) => @callback(args...) done() @@ -164,13 +164,13 @@ describe "HistoryManager", -> @RedisManager.getOldestRawUpdates.callCount.should.equal 3 it "should compress and save the updates in batches", -> - @HistoryManager.compressAndSaveRawUpdates + @UpdatesManager.compressAndSaveRawUpdates .calledWith(@doc_id, @updates.slice(0,2)) .should.equal true - @HistoryManager.compressAndSaveRawUpdates + @UpdatesManager.compressAndSaveRawUpdates .calledWith(@doc_id, @updates.slice(2,4)) .should.equal true - @HistoryManager.compressAndSaveRawUpdates + @UpdatesManager.compressAndSaveRawUpdates .calledWith(@doc_id, @updates.slice(4,5)) .should.equal true @@ -182,9 +182,9 @@ describe "HistoryManager", -> describe "processCompressedUpdatesWithLock", -> beforeEach -> - @HistoryManager.processUncompressedUpdates = sinon.stub().callsArg(2) + @UpdatesManager.processUncompressedUpdates = sinon.stub().callsArg(2) @LockManager.runWithLock = sinon.stub().callsArg(2) - @HistoryManager.processUncompressedUpdatesWithLock @doc_id, @callback + @UpdatesManager.processUncompressedUpdatesWithLock @doc_id, @callback it "should run processUncompressedUpdates with the lock", -> @LockManager.runWithLock @@ -195,3 +195,27 @@ describe "HistoryManager", -> it "should call the callback", -> @callback.called.should.equal true + + describe "getUpdates", -> + beforeEach -> + @updates = ["mock-updates"] + @options = { to: "mock-to", limit: "mock-limit" } + @MongoManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) + @UpdatesManager.getUpdates @doc_id, @options, @callback + + it "should process outstanding updates", -> + @UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(@doc_id) + .should.equal true + + it "should get the updates from the database", -> + @MongoManager.getUpdates + .calledWith(@doc_id, @options) + .should.equal true + + it "should return the updates", -> + @callback + .calledWith(null, @updates) + .should.equal true + From 2e1307bd82c38adea122ef00ba600a9f0faeaddc Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 5 Mar 2014 16:31:38 +0000 Subject: [PATCH 047/549] Doc is always one version ahead of latest op version --- services/track-changes/app/coffee/DiffManager.coffee | 2 +- .../test/acceptance/coffee/GettingADiffTests.coffee | 2 +- .../test/unit/coffee/DiffManager/DiffManagerTests.coffee | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 1128533028..cf1283f193 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -19,7 +19,7 @@ module.exports = DiffManager = logger.log lines: lines, version: version, updates: updates, "got doc and updates" lastUpdate = updates[0] - if lastUpdate? and lastUpdate.v != version + if lastUpdate? and lastUpdate.v != version - 1 return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") updatesToApply = [] diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index 02d83f0044..d321954c09 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -49,7 +49,7 @@ describe "Getting a diff", -> MockDocUpdaterApi.docs[@doc_id] = lines: @lines - version: 6 + version: 7 TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => throw error if error? diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index ef4c2d9327..dbe02b8b4d 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -44,7 +44,9 @@ describe "DiffManager", -> describe "getDiff", -> beforeEach -> @lines = [ "hello", "world" ] - @version = 42 + # Op versions are the version they were applied to, so doc is always one version + # ahead.s + @version = 43 @updates = [ { op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} } { op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} } @@ -82,7 +84,7 @@ describe "DiffManager", -> describe "with mismatching versions", -> beforeEach -> - @version = 42 + @version = 50 @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback From aadce232a14912be40ea1ea8f0039a7916f4271b Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 6 Mar 2014 10:45:51 +0000 Subject: [PATCH 048/549] Use version numbers for sorting and querying, not dates --- .../app/coffee/DiffManager.coffee | 12 +++++------ .../app/coffee/MongoManager.coffee | 10 +++++---- .../coffee/GettingADiffTests.coffee | 6 +++--- .../coffee/GettingUpdatesTests.coffee | 4 ++-- .../DiffManager/DiffManagerTests.coffee | 10 +++++---- .../MongoManager/MongoManagerTests.coffee | 21 +++++++++---------- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index cf1283f193..4294e48d11 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -4,16 +4,16 @@ DiffGenerator = require "./DiffGenerator" logger = require "logger-sharelatex" module.exports = DiffManager = - getLatestDocAndUpdates: (project_id, doc_id, fromDate, toDate, callback = (error, lines, version, updates) ->) -> - UpdatesManager.getUpdates doc_id, from: fromDate, to: toDate, (error, updates) -> + getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, lines, version, updates) ->) -> + UpdatesManager.getUpdates doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) -> return callback(error) if error? callback(null, lines, version, updates) - getDiff: (project_id, doc_id, fromDate, toDate, callback = (error, diff) ->) -> - logger.log project_id: project_id, doc_id: doc_id, from: fromDate, to: toDate, "getting diff" - DiffManager.getLatestDocAndUpdates project_id, doc_id, fromDate, null, (error, lines, version, updates) -> + getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) -> + logger.log project_id: project_id, doc_id: doc_id, from: fromVersion, to: toVersion, "getting diff" + DiffManager.getLatestDocAndUpdates project_id, doc_id, fromVersion, null, (error, lines, version, updates) -> return callback(error) if error? logger.log lines: lines, version: version, updates: updates, "got doc and updates" @@ -24,7 +24,7 @@ module.exports = DiffManager = updatesToApply = [] for update in updates.reverse() - if update.meta.start_ts <= toDate + if update.v <= toVersion updatesToApply.push update logger.log project_id: project_id, doc_id: doc_id, updatesToApply: updatesToApply, "got updates to apply" diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 817f3cac7a..1bf203be18 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -43,13 +43,15 @@ module.exports = MongoManager = query = doc_id: ObjectId(doc_id.toString()) if options.from? - query["meta.end_ts"] = { $gte: options.from } + query["v"] ||= {} + query["v"]["$gte"] = options.from if options.to? - query["meta.start_ts"] = { $lte: options.to } + query["v"] ||= {} + query["v"]["$lte"] = options.to cursor = db.docHistory .find( query ) - .sort( "meta.end_ts": -1 ) + .sort( v: -1 ) if options.limit? cursor.limit(options.limit) @@ -57,5 +59,5 @@ module.exports = MongoManager = cursor.toArray callback ensureIndices: (callback = (error) ->) -> - db.docHistory.ensureIndex { doc_id: 1, "meta.start_ts": 1, "meta.end_ts": 1 }, callback + db.docHistory.ensureIndex { doc_id: 1, v: 1 }, callback diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index d321954c09..8284b425aa 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -30,11 +30,11 @@ describe "Getting a diff", -> }, { op: [{ i: "two ", p: 4 }] meta: { ts: @from + twoMinutes, user_id: @user_id } - v: 4 + v: @fromVersion = 4 }, { op: [{ i: "three ", p: 8 }] meta: { ts: @to - twoMinutes, user_id: @user_id } - v: 5 + v: @toVersion = 5 }, { op: [{ i: "four", p: 14 }] meta: { ts: @to + twoMinutes, user_id: @user_id } @@ -53,7 +53,7 @@ describe "Getting a diff", -> TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => throw error if error? - TrackChangesClient.getDiff @project_id, @doc_id, @from, @to, (error, diff) => + TrackChangesClient.getDiff @project_id, @doc_id, @fromVersion, @toVersion, (error, diff) => throw error if error? @diff = diff.diff done() diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 6e93f79973..2f4048c5cc 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -30,7 +30,7 @@ describe "Getting updates", -> }, { op: [{ i: "three ", p: 8 }] meta: { ts: @to, user_id: @user_id } - v: 5 + v: @toVersion = 5 }, { op: [{ i: "four", p: 14 }] meta: { ts: @to + 2 * @minutes, user_id: @user_id } @@ -39,7 +39,7 @@ describe "Getting updates", -> TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => throw error if error? - TrackChangesClient.getUpdates @project_id, @doc_id, { to: @to, limit: 2 }, (error, body) => + TrackChangesClient.getUpdates @project_id, @doc_id, { to: @toVersion, limit: 2 }, (error, body) => throw error if error? @updates = body.updates done() diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index dbe02b8b4d..e12d4b2085 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -53,6 +53,8 @@ describe "DiffManager", -> { op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} } { op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} } ] + @fromVersion = 39 + @toVersion = 40 @diffed_updates = @updates.slice(2) @rewound_content = "rewound-content" @diff = [ u: "mock-diff" ] @@ -62,11 +64,11 @@ describe "DiffManager", -> @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) @DiffGenerator.rewindUpdates = sinon.stub().returns(@rewound_content) @DiffGenerator.buildDiff = sinon.stub().returns(@diff) - @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback + @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback it "should get the latest doc and version with all recent updates", -> @DiffManager.getLatestDocAndUpdates - .calledWith(@project_id, @doc_id, @from, null) + .calledWith(@project_id, @doc_id, @fromVersion, null) .should.equal true it "should rewind the diff", -> @@ -87,7 +89,7 @@ describe "DiffManager", -> @version = 50 @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) - @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback + @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback it "should call the callback with an error", -> @callback @@ -98,7 +100,7 @@ describe "DiffManager", -> beforeEach -> @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) @DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!")) - @DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback + @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback it "should call the callback with an error", -> @callback diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index e56d5fc858..bbae536533 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -141,25 +141,24 @@ describe "MongoManager", -> @db.docHistory.limit = sinon.stub().returns @db.docHistory @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates) - @from = new Date(Date.now()) - @to = new Date(Date.now() + 100000) + @from = 42 + @to = 55 - describe "with a toDate", -> + describe "with a to version", -> beforeEach -> @MongoManager.getUpdates @doc_id, from: @from, to: @to, @callback - it "should find the all updates between the to and from date", -> + it "should find the all updates between the to and from versions", -> @db.docHistory.find .calledWith({ doc_id: ObjectId(@doc_id) - "meta.end_ts": { $gte: @from } - "meta.start_ts": { $lte: @to } + v: { $gte: @from, $lte: @to } }) .should.equal true - it "should sort in descending timestamp order", -> + it "should sort in descending version order", -> @db.docHistory.sort - .calledWith("meta.end_ts": -1) + .calledWith("v": -1) .should.equal true it "should not limit the results", -> @@ -169,15 +168,15 @@ describe "MongoManager", -> it "should call the call back with the updates", -> @callback.calledWith(null, @updates).should.equal true - describe "without a todo date", -> + describe "without a to version", -> beforeEach -> @MongoManager.getUpdates @doc_id, from: @from, @callback - it "should find the all updates after the from date", -> + it "should find the all updates after the from version", -> @db.docHistory.find .calledWith({ doc_id: ObjectId(@doc_id) - "meta.end_ts": { $gte: @from } + v: { $gte: @from } }) .should.equal true From 5fc139db00bc2fbd2b4ed93da9f3eab830b22286 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 6 Mar 2014 11:00:49 +0000 Subject: [PATCH 049/549] Return version numbers of updates --- services/track-changes/app/coffee/HttpController.coffee | 1 + .../test/acceptance/coffee/GettingUpdatesTests.coffee | 2 ++ .../test/unit/coffee/HttpController/HttpControllerTests.coffee | 1 + 3 files changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index af98464424..6f8a428a6e 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -42,6 +42,7 @@ module.exports = HttpController = formattedUpdates = for update in updates { meta: update.meta + v: update.v } res.send JSON.stringify updates: formattedUpdates diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 2f4048c5cc..751b098cb7 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -50,9 +50,11 @@ describe "Getting updates", -> start_ts: @to end_ts: @to user_id: @user_id + v: 5 }, { meta: start_ts: @to - 2 * @minutes end_ts: @to - 2 * @minutes user_id: @user_id + v: 4 }] diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index aaea318ff4..667e40193a 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -90,5 +90,6 @@ describe "HttpController", -> updates = for update in @rawUpdates { meta: @meta + v: @v } @res.send.calledWith(JSON.stringify(updates: updates)).should.equal true From b45db3aa2b5e486d7280f7b1851eb7aedb2fa10b Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 6 Mar 2014 18:04:00 +0000 Subject: [PATCH 050/549] Return user details like email and name in updates --- .../app/coffee/DiffManager.coffee | 2 +- .../app/coffee/HttpController.coffee | 4 +- .../app/coffee/UpdatesManager.coffee | 30 +++++++ .../app/coffee/WebApiManager.coffee | 32 +++++++ .../config/settings.development.coffee | 4 + .../coffee/GettingADiffTests.coffee | 13 ++- .../coffee/GettingUpdatesTests.coffee | 22 ++++- .../coffee/helpers/MockWebApi.coffee | 24 ++++++ .../DiffManager/DiffManagerTests.coffee | 4 +- .../HttpController/HttpControllerTests.coffee | 6 +- .../UpdatesManager/UpdatesManagerTests.coffee | 85 +++++++++++++++++++ .../WebApiManager/WebApiManagerTests.coffee | 71 ++++++++++++++++ 12 files changed, 283 insertions(+), 14 deletions(-) create mode 100644 services/track-changes/app/coffee/WebApiManager.coffee create mode 100644 services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee create mode 100644 services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 4294e48d11..e517a5ef4e 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -5,7 +5,7 @@ logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, lines, version, updates) ->) -> - UpdatesManager.getUpdates doc_id, from: fromVersion, to: toVersion, (error, updates) -> + UpdatesManager.getUpdatesWithUserInfo doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) -> return callback(error) if error? diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 6f8a428a6e..5d7bcf6c72 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -37,7 +37,7 @@ module.exports = HttpController = if req.query.limit? limit = parseInt(req.query.limit, 10) - UpdatesManager.getUpdates doc_id, to: to, limit: limit, (error, updates) -> + UpdatesManager.getUpdatesWithUserInfo doc_id, to: to, limit: limit, (error, updates) -> return next(error) if error? formattedUpdates = for update in updates { @@ -45,5 +45,3 @@ module.exports = HttpController = v: update.v } res.send JSON.stringify updates: formattedUpdates - - diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 158688d03b..ee9bb12655 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -2,7 +2,9 @@ MongoManager = require "./MongoManager" RedisManager = require "./RedisManager" UpdateCompressor = require "./UpdateCompressor" LockManager = require "./LockManager" +WebApiManager = require "./WebApiManager" logger = require "logger-sharelatex" +async = require "async" module.exports = UpdatesManager = compressAndSaveRawUpdates: (doc_id, rawUpdates, callback = (error) ->) -> @@ -70,3 +72,31 @@ module.exports = UpdatesManager = return callback(error) if error? MongoManager.getUpdates doc_id, options, callback + getUpdatesWithUserInfo: (doc_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.getUpdates doc_id, options, (error, updates) -> + return callback(error) if error? + UpdatesManager.fillUserInfo updates, (error, updates) -> + return callback(error) if error? + callback null, updates + + fillUserInfo: (updates, callback = (error, updates) ->) -> + users = {} + for update in updates + users[update.meta.user_id] = true + + jobs = [] + for user_id, _ of users + do (user_id) -> + jobs.push (callback) -> + WebApiManager.getUserInfo user_id, (error, userInfo) -> + return callback(error) if error? + users[user_id] = userInfo + callback() + + async.series jobs, (error) -> + return callback(error) if error? + for update in updates + user_id = update.meta.user_id + delete update.meta.user_id + update.meta.user = users[user_id] + callback null, updates diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee new file mode 100644 index 0000000000..fc4eda9729 --- /dev/null +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -0,0 +1,32 @@ +request = require "request" +logger = require "logger-sharelatex" +Settings = require "settings-sharelatex" + +module.exports = WebApiManager = + getUserInfo: (user_id, callback = (error, userInfo) ->) -> + url = "#{Settings.apis.web.url}/user/#{user_id}/personal_info" + logger.log user_id: user_id, "getting user info from web" + request.get { + url: url + auth: + user: Settings.apis.web.user + pass: Settings.apis.web.pass + sendImmediately: true + }, (error, res, body)-> + if error? + return callback(error) + if res.statusCode >= 200 and res.statusCode < 300 + try + user = JSON.parse(body) + catch error + return callback(error) + callback null, { + id: user.id + email: user.email + first_name: user.first_name + last_name: user.last_name + } + else + error = new Error("web returned a non-success status code: #{res.statusCode}") + logger.error err: error, user_id: user_id, url: url, "error accessing web" + callback error \ No newline at end of file diff --git a/services/track-changes/config/settings.development.coffee b/services/track-changes/config/settings.development.coffee index d3cf2978ec..f79f657fa6 100755 --- a/services/track-changes/config/settings.development.coffee +++ b/services/track-changes/config/settings.development.coffee @@ -8,3 +8,7 @@ module.exports = apis: documentupdater: url: "http://localhost:3003" + web: + url: "http://localhost:3000" + user: "sharelatex" + pass: "password" diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index 8284b425aa..ed0e0e42d6 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -9,6 +9,7 @@ Settings = require "settings-sharelatex" TrackChangesClient = require "./helpers/TrackChangesClient" MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" +MockWebApi = require "./helpers/MockWebApi" describe "Getting a diff", -> before (done) -> @@ -21,6 +22,13 @@ describe "Getting a diff", -> @doc_id = ObjectId().toString() @project_id = ObjectId().toString() + MockWebApi.users[@user_id] = @user = + email: "user@sharelatex.com" + first_name: "Leo" + last_name: "Lion" + id: @user_id + sinon.spy MockWebApi, "getUser" + twoMinutes = 2 * 60 * 1000 @updates = [{ @@ -43,8 +51,8 @@ describe "Getting a diff", -> @lines = ["one two three four"] @expected_diff = [ { u: "one " } - { i: "two ", meta: { start_ts: @from + twoMinutes, end_ts: @from + twoMinutes, user_id: @user_id } } - { i: "three ", meta: { start_ts: @to - twoMinutes, end_ts: @to - twoMinutes, user_id: @user_id } } + { i: "two ", meta: { start_ts: @from + twoMinutes, end_ts: @from + twoMinutes, user: @user } } + { i: "three ", meta: { start_ts: @to - twoMinutes, end_ts: @to - twoMinutes, user: @user } } ] MockDocUpdaterApi.docs[@doc_id] = @@ -60,6 +68,7 @@ describe "Getting a diff", -> after () -> MockDocUpdaterApi.getDoc.restore() + MockWebApi.getUser.restore() it "should return the diff", -> expect(@diff).to.deep.equal @expected_diff diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 751b098cb7..6720548b26 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -8,6 +8,7 @@ ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" TrackChangesClient = require "./helpers/TrackChangesClient" +MockWebApi = require "./helpers/MockWebApi" describe "Getting updates", -> before (done) -> @@ -19,6 +20,13 @@ describe "Getting updates", -> @minutes = 60 * 1000 + MockWebApi.users[@user_id] = @user = + email: "user@sharelatex.com" + first_name: "Leo" + last_name: "Lion" + id: @user_id + sinon.spy MockWebApi, "getUser" + @updates = [{ op: [{ i: "one ", p: 0 }] meta: { ts: @to - 4 * @minutes, user_id: @user_id } @@ -44,17 +52,25 @@ describe "Getting updates", -> @updates = body.updates done() - it "should return the diff", -> + after: () -> + MockWebApi.getUser.restore() + + it "should fetch the user details from the web api", -> + MockWebApi.getUser + .calledWith(@user_id) + .should.equal true + + it "should return the updates", -> expect(@updates).to.deep.equal [{ meta: start_ts: @to end_ts: @to - user_id: @user_id + user: @user v: 5 }, { meta: start_ts: @to - 2 * @minutes end_ts: @to - 2 * @minutes - user_id: @user_id + user: @user v: 4 }] diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee new file mode 100644 index 0000000000..04ee281541 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee @@ -0,0 +1,24 @@ +express = require("express") +app = express() + +module.exports = MockWebApi = + users: {} + + getUser: (user_id, callback = (error) ->) -> + callback null, @users[user_id] + + run: () -> + app.get "/user/:user_id/personal_info", (req, res, next) => + @getUser req.params.user_id, (error, user) -> + if error? + res.send 500 + if !user? + res.send 404 + else + res.send JSON.stringify user + + app.listen 3000, (error) -> + throw error if error? + +MockWebApi.run() + diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index e12d4b2085..7b1ce33af1 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -25,7 +25,7 @@ describe "DiffManager", -> @updates = [ "mock-update-1", "mock-update-2" ] @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @lines, @version) - @UpdatesManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback it "should get the latest version of the doc", -> @@ -34,7 +34,7 @@ describe "DiffManager", -> .should.equal true it "should get the latest updates", -> - @UpdatesManager.getUpdates + @UpdatesManager.getUpdatesWithUserInfo .calledWith(@doc_id, from: @from, to: @to) .should.equal true diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 667e40193a..8e57ea2bd3 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -78,15 +78,15 @@ describe "HttpController", -> meta: @meta = "mock-meta" doc_id: @doc_id }] - @UpdatesManager.getUpdates = sinon.stub().callsArgWith(2, null, @rawUpdates) + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @rawUpdates) @HttpController.getUpdates @req, @res, @next it "should get the updates", -> - @UpdatesManager.getUpdates + @UpdatesManager.getUpdatesWithUserInfo .calledWith(@doc_id, to: @to, limit: @limit) .should.equal true - it "should return the updates", -> + it "should return the formatted updates", -> updates = for update in @rawUpdates { meta: @meta diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 7dfc96169e..fd2056e38d 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -12,6 +12,7 @@ describe "UpdatesManager", -> "./MongoManager" : @MongoManager = {} "./RedisManager" : @RedisManager = {} "./LockManager" : @LockManager = {} + "./WebApiManager": @WebApiManager = {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } @doc_id = "doc-id-123" @callback = sinon.stub() @@ -219,3 +220,87 @@ describe "UpdatesManager", -> .calledWith(null, @updates) .should.equal true + describe "getUpdatesWithUserInfo", -> + beforeEach -> + @updates = ["mock-updates"] + @options = { to: "mock-to", limit: "mock-limit" } + @updatesWithUserInfo = ["updates-with-user-info"] + @UpdatesManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) + @UpdatesManager.getUpdatesWithUserInfo @doc_id, @options, @callback + + it "should get the updates", -> + @UpdatesManager.getUpdates + .calledWith(@doc_id, @options) + .should.equal true + + it "should file the updates with the user info", -> + @UpdatesManager.fillUserInfo + .calledWith(@updates) + .should.equal true + + it "shoudl return the updates with the filled details", -> + @callback.calledWith(null, @updatesWithUserInfo).should.equal true + + describe "fillUserInfo", -> + beforeEach (done) -> + @user_id_1 = "user-id-1" + @user_id_2 = "user-id-2" + @updates = [{ + meta: + user_id: @user_id_1 + op: "mock-op-1" + }, { + meta: + user_id: @user_id_1 + op: "mock-op-2" + }, { + meta: + user_id: @user_id_2 + op: "mock-op-3" + }] + @user_info = + "user-id-1": { + email: "user1@sharelatex.com" + } + "user-id-2": { + email: "user2@sharelatex.com" + } + @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + callback null, @user_info[user_id] + sinon.spy @WebApiManager, "getUserInfo" + + @UpdatesManager.fillUserInfo @updates, (error, @results) => + done() + + it "should only call getUserInfo once for each user_id", -> + @WebApiManager.getUserInfo.calledTwice.should.equal true + @WebApiManager.getUserInfo + .calledWith(@user_id_1) + .should.equal true + @WebApiManager.getUserInfo + .calledWith(@user_id_2) + .should.equal true + + it "should return the updates with the user info filled", -> + expect(@results).to.deep.equal [{ + meta: + user: + email: "user1@sharelatex.com" + op: "mock-op-1" + }, { + meta: + user: + email: "user1@sharelatex.com" + op: "mock-op-2" + }, { + meta: + user: + email: "user2@sharelatex.com" + op: "mock-op-3" + }] + + + + + diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee new file mode 100644 index 0000000000..589dfeb7e5 --- /dev/null +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee @@ -0,0 +1,71 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/WebApiManager.js" +SandboxedModule = require('sandboxed-module') + +describe "WebApiManager", -> + beforeEach -> + @WebApiManager = SandboxedModule.require modulePath, requires: + "request": @request = {} + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + 'settings-sharelatex': @settings = + apis: + web: + url: "http://example.com" + user: "sharelatex" + pass: "password" + @callback = sinon.stub() + @user_id = "mock-user-id" + @user_info = + email: "leo@sharelatex.com" + id: @user_id + first_name: "Leo" + last_nane: "Lion" + extra_param: "blah" + + describe "getUserInfo", -> + describe "successfully", -> + beforeEach -> + @body = JSON.stringify @user_info + @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) + @WebApiManager.getUserInfo @user_id, @callback + + it 'should get the user from the web api', -> + url = + @request.get + .calledWith({ + url: "#{@settings.apis.web.url}/user/#{@user_id}/personal_info" + auth: + user: @settings.apis.web.user + pass: @settings.apis.web.pass + sendImmediately: true + }) + .should.equal true + + it "should call the callback with only the email, id and names", -> + @callback.calledWith(null, { + id: @user_id + email: @user_info.email + first_name: @user_info.first_name + last_name: @user_info.last_name + }).should.equal true + + describe "when the web API returns an error", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) + @WebApiManager.getUserInfo @user_id, @callback + + it "should return an error to the callback", -> + @callback.calledWith(@error).should.equal true + + describe "when the web returns a failure error code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") + @WebApiManager.getUserInfo @user_id, @callback + + it "should return the callback with an error", -> + @callback + .calledWith(new Error("web returned failure status code: 500")) + .should.equal true \ No newline at end of file From 866084ee6b7f0a112539fc545e160742bfd167fa Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 7 Mar 2014 14:02:16 +0000 Subject: [PATCH 051/549] Pop last version, not last timestamp --- services/track-changes/app/coffee/MongoManager.coffee | 2 +- .../test/unit/coffee/MongoManager/MongoManagerTests.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1bf203be18..9285b59c3a 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -5,7 +5,7 @@ module.exports = MongoManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> db.docHistory .find(doc_id: ObjectId(doc_id.toString())) - .sort( "meta.end_ts": -1) + .sort( v: -1 ) .limit(1) .toArray (error, compressedUpdates) -> return callback(error) if error? diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index bbae536533..cd8caec27b 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -34,9 +34,9 @@ describe "MongoManager", -> .calledWith(1) .should.equal true - it "should sort in descending timestamp order", -> + it "should sort in descending version order", -> @db.docHistory.sort - .calledWith("meta.end_ts": -1) + .calledWith(v: -1) .should.equal true it "should call the call back with the update", -> From 7eb8699b9397338ef8902ceda9052145c183810e Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 10 Mar 2014 16:03:03 +0000 Subject: [PATCH 052/549] Refactor diff manager --- .../app/coffee/DiffManager.coffee | 38 +++++---- .../app/coffee/DocumentUpdaterManager.coffee | 2 +- .../app/coffee/UpdatesManager.coffee | 3 - .../DiffManager/DiffManagerTests.coffee | 79 ++++++++++++++----- .../DocumentUpdaterManagerTests.coffee | 4 +- 5 files changed, 84 insertions(+), 42 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index e517a5ef4e..c791fe1e09 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -4,36 +4,42 @@ DiffGenerator = require "./DiffGenerator" logger = require "logger-sharelatex" module.exports = DiffManager = - getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, lines, version, updates) ->) -> + getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> UpdatesManager.getUpdatesWithUserInfo doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? - DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) -> + DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? - callback(null, lines, version, updates) + callback(null, content, version, updates) getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) -> logger.log project_id: project_id, doc_id: doc_id, from: fromVersion, to: toVersion, "getting diff" - DiffManager.getLatestDocAndUpdates project_id, doc_id, fromVersion, null, (error, lines, version, updates) -> + DiffManager.getDocumentBeforeVersion project_id, doc_id, fromVersion, (error, startingContent, updates) -> return callback(error) if error? - logger.log lines: lines, version: version, updates: updates, "got doc and updates" + updatesToApply = [] + for update in updates.slice().reverse() + if update.v <= toVersion + updatesToApply.push update + + try + diff = DiffGenerator.buildDiff startingContent, updatesToApply + catch e + return callback(e) + + callback(null, diff) + + getDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) -> + logger.log project_id: project_id, doc_id: doc_id, version: version, "getting document before version" + DiffManager.getLatestDocAndUpdates project_id, doc_id, version, null, (error, content, version, updates) -> + return callback(error) if error? lastUpdate = updates[0] if lastUpdate? and lastUpdate.v != version - 1 return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") - updatesToApply = [] - for update in updates.reverse() - if update.v <= toVersion - updatesToApply.push update - - logger.log project_id: project_id, doc_id: doc_id, updatesToApply: updatesToApply, "got updates to apply" - try - startingContent = DiffGenerator.rewindUpdates lines.join("\n"), updates - logger.log project_id: project_id, doc_id: doc_id, startingContent: startingContent, "rewound doc" - diff = DiffGenerator.buildDiff startingContent, updatesToApply + startingContent = DiffGenerator.rewindUpdates content, updates.slice().reverse() catch e return callback(e) - callback(null, diff) \ No newline at end of file + callback(null, startingContent, updates) \ No newline at end of file diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee index 0b02a9a16c..e88f0f3520 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee @@ -14,7 +14,7 @@ module.exports = DocumentUpdaterManager = body = JSON.parse(body) catch error return callback(error) - callback null, body.lines, body.version + callback null, body.lines.join("\n"), body.version else error = new Error("doc updater returned a non-success status code: #{res.statusCode}") logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index ee9bb12655..1014001835 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -14,7 +14,6 @@ module.exports = UpdatesManager = MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> return callback(error) if error? - logger.log doc_id: doc_id, "popped last update" # Ensure that raw updates start where lastCompressedUpdate left off if lastCompressedUpdate? @@ -39,11 +38,9 @@ module.exports = UpdatesManager = REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (doc_id, callback = (error) ->) -> - logger.log "processUncompressedUpdates" RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? length = rawUpdates.length - logger.log doc_id: doc_id, length: length, "got raw updates from redis" UpdatesManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> return callback(error) if error? logger.log doc_id: doc_id, "compressed and saved doc updates" diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 7b1ce33af1..57c367db2a 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -20,11 +20,11 @@ describe "DiffManager", -> describe "getLatestDocAndUpdates", -> beforeEach -> - @lines = [ "hello", "world" ] + @content = "hello world" @version = 42 @updates = [ "mock-update-1", "mock-update-2" ] - @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @lines, @version) + @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @content, @version) @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback @@ -38,12 +38,12 @@ describe "DiffManager", -> .calledWith(@doc_id, from: @from, to: @to) .should.equal true - it "should call the callback with the lines, version and updates", -> - @callback.calledWith(null, @lines, @version, @updates).should.equal true + it "should call the callback with the content, version and updates", -> + @callback.calledWith(null, @content, @version, @updates).should.equal true describe "getDiff", -> beforeEach -> - @lines = [ "hello", "world" ] + @content = "hello world" # Op versions are the version they were applied to, so doc is always one version # ahead.s @version = 43 @@ -61,11 +61,56 @@ describe "DiffManager", -> describe "with matching versions", -> beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) - @DiffGenerator.rewindUpdates = sinon.stub().returns(@rewound_content) + @DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, @rewound_content, @updates) @DiffGenerator.buildDiff = sinon.stub().returns(@diff) @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback + it "should get the latest doc and version with all recent updates", -> + @DiffManager.getDocumentBeforeVersion + .calledWith(@project_id, @doc_id, @fromVersion) + .should.equal true + + it "should generate the diff", -> + @DiffGenerator.buildDiff + .calledWith(@rewound_content, @diffed_updates.slice().reverse()) + .should.equal true + + it "should call the callback with the diff", -> + @callback.calledWith(null, @diff).should.equal true + + describe "when the updates are inconsistent", -> + beforeEach -> + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffGenerator.buildDiff = sinon.stub().throws(@error = new Error("inconsistent!")) + @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback + + it "should call the callback with an error", -> + @callback + .calledWith(@error) + .should.equal true + + describe "getDocumentBeforeVersion", -> + beforeEach -> + @content = "hello world" + # Op versions are the version they were applied to, so doc is always one version + # ahead.s + @version = 43 + @updates = [ + { op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} } + { op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} } + { op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} } + { op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} } + ] + @fromVersion = 39 + @rewound_content = "rewound-content" + @diff = [ u: "mock-diff" ] + + describe "with matching versions", -> + beforeEach -> + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffGenerator.rewindUpdates = sinon.stub().returns(@rewound_content) + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback + it "should get the latest doc and version with all recent updates", -> @DiffManager.getLatestDocAndUpdates .calledWith(@project_id, @doc_id, @fromVersion, null) @@ -73,23 +118,18 @@ describe "DiffManager", -> it "should rewind the diff", -> @DiffGenerator.rewindUpdates - .calledWith(@lines.join("\n"), @updates) + .calledWith(@content, @updates.slice().reverse()) .should.equal true - it "should generate the diff", -> - @DiffGenerator.buildDiff - .calledWith(@rewound_content, @diffed_updates.reverse()) - .should.equal true - - it "should call the callback with the diff", -> - @callback.calledWith(null, @diff).should.equal true + it "should call the callback with the rewound document and updates", -> + @callback.calledWith(null, @rewound_content, @updates).should.equal true describe "with mismatching versions", -> beforeEach -> @version = 50 @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) - @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback it "should call the callback with an error", -> @callback @@ -98,12 +138,11 @@ describe "DiffManager", -> describe "when the updates are inconsistent", -> beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates) + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) @DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!")) - @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback it "should call the callback with an error", -> @callback .calledWith(@error) .should.equal true - diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee index 5941eefd8d..642909a787 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee @@ -30,8 +30,8 @@ describe "DocumentUpdaterManager", -> url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}" @request.get.calledWith(url).should.equal true - it "should call the callback with the lines and version", -> - @callback.calledWith(null, @lines, @version, @ops).should.equal true + it "should call the callback with the content and version", -> + @callback.calledWith(null, @lines.join("\n"), @version, @ops).should.equal true describe "when the document updater API returns an error", -> beforeEach -> From 83b2aa3082866cb0769ec94f5c4d55a358f377e2 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 10 Mar 2014 16:58:26 +0000 Subject: [PATCH 053/549] add in restore end point --- services/track-changes/app.coffee | 2 + .../app/coffee/DocumentUpdaterManager.coffee | 17 +++++ .../app/coffee/HttpController.coffee | 8 +++ .../app/coffee/RestoreManager.coffee | 12 ++++ .../coffee/RestoringVersions.coffee | 67 +++++++++++++++++++ .../coffee/helpers/MockDocUpdaterApi.coffee | 12 ++++ .../coffee/helpers/TrackChangesClient.coffee | 9 ++- .../DocumentUpdaterManagerTests.coffee | 39 +++++++++++ .../HttpController/HttpControllerTests.coffee | 30 +++++++-- .../RestoreManager/RestoreManagerTests.coffee | 38 +++++++++++ 10 files changed, 229 insertions(+), 5 deletions(-) create mode 100644 services/track-changes/app/coffee/RestoreManager.coffee create mode 100644 services/track-changes/test/acceptance/coffee/RestoringVersions.coffee create mode 100644 services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index ac6215e20d..7c47607aeb 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -16,6 +16,8 @@ app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff app.get "/project/:project_id/doc/:doc_id/updates", HttpController.getUpdates +app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore + app.get "/status", (req, res, next) -> res.send "track-changes is alive" diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee index e88f0f3520..6dea2b3f14 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee @@ -15,6 +15,23 @@ module.exports = DocumentUpdaterManager = catch error return callback(error) callback null, body.lines.join("\n"), body.version + else + error = new Error("doc updater returned a non-success status code: #{res.statusCode}") + logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" + callback error + + setDocument: (project_id, doc_id, content, callback = (error) ->) -> + url = "#{Settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}" + logger.log project_id:project_id, doc_id: doc_id, "setting doc in document updater" + request.post { + url: url + json: + lines: content.split("\n") + }, (error, res, body)-> + if error? + return callback(error) + if res.statusCode >= 200 and res.statusCode < 300 + callback null else error = new Error("doc updater returned a non-success status code: #{res.statusCode}") logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 5d7bcf6c72..f3878ab7cd 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -1,5 +1,6 @@ UpdatesManager = require "./UpdatesManager" DiffManager = require "./DiffManager" +RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" module.exports = HttpController = @@ -45,3 +46,10 @@ module.exports = HttpController = v: update.v } res.send JSON.stringify updates: formattedUpdates + + restore: (req, res, next = (error) ->) -> + {doc_id, project_id, version} = req.params + version = parseInt(version, 10) + RestoreManager.restoreToBeforeVersion project_id, doc_id, version, (error) -> + return next(error) if error? + res.send 204 diff --git a/services/track-changes/app/coffee/RestoreManager.coffee b/services/track-changes/app/coffee/RestoreManager.coffee new file mode 100644 index 0000000000..6c5ccef6fb --- /dev/null +++ b/services/track-changes/app/coffee/RestoreManager.coffee @@ -0,0 +1,12 @@ +DocumentUpdaterManager = require "./DocumentUpdaterManager" +DiffManager = require "./DiffManager" +logger = require "logger-sharelatex" + +module.exports = RestoreManager = + restoreToBeforeVersion: (project_id, doc_id, version, callback = (error) ->) -> + logger.log project_id: project_id, doc_id: doc_id, version: version, "restoring document" + DiffManager.getDocumentBeforeVersion project_id, doc_id, version, (error, content) -> + return callback(error) if error? + DocumentUpdaterManager.setDocument project_id, doc_id, content, (error) -> + return callback(error) if error? + callback() diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee new file mode 100644 index 0000000000..21666bab3d --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -0,0 +1,67 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +expect = chai.expect +mongojs = require "../../../app/js/mongojs" +db = mongojs.db +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" + +TrackChangesClient = require "./helpers/TrackChangesClient" +MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" +MockWebApi = require "./helpers/MockWebApi" + +describe "Restoring a version", -> + before (done) -> + sinon.spy MockDocUpdaterApi, "setDoc" + + @now = Date.now() + @user_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + + minutes = 60 * 1000 + + @updates = [{ + op: [{ i: "one ", p: 0 }] + meta: { ts: @now - 6 * minutes, user_id: @user_id } + v: 3 + }, { + op: [{ i: "two ", p: 4 }] + meta: { ts: @now - 4 * minutes, user_id: @user_id } + v: 4 + }, { + op: [{ i: "three ", p: 8 }] + meta: { ts: @now - 2 * minutes, user_id: @user_id } + v: 5 + }, { + op: [{ i: "four", p: 14 }] + meta: { ts: @now, user_id: @user_id } + v: 6 + }] + @lines = ["one two three four"] + @restored_lines = ["one two "] + @beforeVersion = 5 + + MockWebApi.users[@user_id] = @user = + email: "user@sharelatex.com" + first_name: "Leo" + last_name: "Lion" + id: @user_id + MockDocUpdaterApi.docs[@doc_id] = + lines: @lines + version: 7 + + TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + throw error if error? + TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, (error) => + throw error if error? + done() + + after () -> + MockDocUpdaterApi.setDoc.restore() + + it "should set the doc in the doc updater", -> + MockDocUpdaterApi.setDoc + .calledWith(@project_id, @doc_id, @restored_lines) + .should.equal true diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee index a64098503c..fcddace406 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -7,6 +7,11 @@ module.exports = MockDocUpdaterApi = getDoc: (project_id, doc_id, callback = (error) ->) -> callback null, @docs[doc_id] + setDoc: (project_id, doc_id, lines, callback = (error) ->) -> + @docs[doc_id] ||= {} + @docs[doc_id].lines = lines + callback() + run: () -> app.get "/project/:project_id/doc/:doc_id", (req, res, next) => @getDoc req.params.project_id, req.params.doc_id, (error, doc) -> @@ -17,6 +22,13 @@ module.exports = MockDocUpdaterApi = else res.send JSON.stringify doc + app.post "/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => + @setDoc req.params.project_id, req.params.doc_id, req.body.lines, (errr, doc) -> + if error? + res.send 500 + else + res.send 204 + app.listen 3003, (error) -> throw error if error? diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index d2b31f623d..ab85b67171 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -28,4 +28,11 @@ module.exports = TrackChangesClient = url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/updates?to=#{options.to}&limit=#{options.limit}" }, (error, response, body) => response.statusCode.should.equal 200 - callback null, JSON.parse(body) \ No newline at end of file + callback null, JSON.parse(body) + + restoreDoc: (project_id, doc_id, version, callback = (error) ->) -> + request.post { + url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/version/#{version}/restore" + }, (error, response, body) => + response.statusCode.should.equal 204 + callback null \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee index 642909a787..25c61f6279 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee @@ -46,6 +46,45 @@ describe "DocumentUpdaterManager", -> @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + it "should return the callback with an error", -> + @callback + .calledWith(new Error("doc updater returned failure status code: 500")) + .should.equal true + + describe "setDocument", -> + beforeEach -> + @content = "mock content" + + describe "successfully", -> + beforeEach -> + @request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}) + @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @callback + + it 'should set the document in the document updater', -> + url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}" + @request.post + .calledWith({ + url: url + json: + lines: @content.split("\n") + }).should.equal true + + it "should call the callback", -> + @callback.calledWith(null).should.equal true + + describe "when the document updater API returns an error", -> + beforeEach -> + @request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) + @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @callback + + it "should return an error to the callback", -> + @callback.calledWith(@error).should.equal true + + describe "when the document updater returns a failure error code", -> + beforeEach -> + @request.post = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") + @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @callback + it "should return the callback with an error", -> @callback .calledWith(new Error("doc updater returned failure status code: 500")) diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 8e57ea2bd3..791bbd1d04 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -11,9 +11,9 @@ describe "HttpController", -> "logger-sharelatex": { log: sinon.stub() } "./UpdatesManager": @UpdatesManager = {} "./DiffManager": @DiffManager = {} + "./RestoreManager": @RestoreManager = {} @doc_id = "doc-id-123" @project_id = "project-id-123" - @version = 42 @next = sinon.stub() describe "flushUpdatesWithLock", -> @@ -36,8 +36,8 @@ describe "HttpController", -> describe "getDiff", -> beforeEach -> - @from = Date.now() - 10000 - @to = Date.now() + @from = 42 + @to = 45 @req = params: doc_id: @doc_id @@ -61,7 +61,7 @@ describe "HttpController", -> describe "getUpdates", -> beforeEach -> - @to = Date.now() + @to = 42 @limit = 10 @req = params: @@ -93,3 +93,25 @@ describe "HttpController", -> v: @v } @res.send.calledWith(JSON.stringify(updates: updates)).should.equal true + + describe "RestoreManager", -> + beforeEach -> + @version = "42" + @req = + params: + doc_id: @doc_id + project_id: @project_id + version: @version + @res = + send: sinon.stub() + + @RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(3) + @HttpController.restore @req, @res, @next + + it "should restore the document", -> + @RestoreManager.restoreToBeforeVersion + .calledWith(@project_id, @doc_id, parseInt(@version, 10)) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true diff --git a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee new file mode 100644 index 0000000000..36088d4405 --- /dev/null +++ b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee @@ -0,0 +1,38 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/RestoreManager.js" +SandboxedModule = require('sandboxed-module') + +describe "RestoreManager", -> + beforeEach -> + @RestoreManager = SandboxedModule.require modulePath, requires: + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "./DocumentUpdaterManager": @DocumentUpdaterManager = {} + "./DiffManager": @DiffManager = {} + @callback = sinon.stub() + @project_id = "mock-project-id" + @doc_id = "mock-doc-id" + @version = 42 + + describe "restoreToBeforeVersion", -> + beforeEach -> + @content = "mock content" + @DocumentUpdaterManager.setDocument = sinon.stub().callsArg(3) + @DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, @content) + @RestoreManager.restoreToBeforeVersion @project_id, @doc_id, @version, @callback + + it "should get the content before the requested version", -> + @DiffManager.getDocumentBeforeVersion + .calledWith(@project_id, @doc_id, @version) + .should.equal true + + it "should set the document in the document updater", -> + @DocumentUpdaterManager.setDocument + .calledWith(@project_id, @doc_id, @content) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + \ No newline at end of file From 3d1d962501e281bdfa67fcc376c5087833eeed85 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Mar 2014 11:45:25 +0000 Subject: [PATCH 054/549] Don't try to fetch user when id doesn't exist --- .../app/coffee/UpdatesManager.coffee | 12 +- .../coffee/GettingUpdatesTests.coffee | 3 +- .../UpdatesManager/UpdatesManagerTests.coffee | 138 +++++++++++------- 3 files changed, 94 insertions(+), 59 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 1014001835..7261b3f341 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -79,7 +79,8 @@ module.exports = UpdatesManager = fillUserInfo: (updates, callback = (error, updates) ->) -> users = {} for update in updates - users[update.meta.user_id] = true + if UpdatesManager._validUserId(update.meta.user_id) + users[update.meta.user_id] = true jobs = [] for user_id, _ of users @@ -95,5 +96,12 @@ module.exports = UpdatesManager = for update in updates user_id = update.meta.user_id delete update.meta.user_id - update.meta.user = users[user_id] + if UpdatesManager._validUserId(user_id) + update.meta.user = users[user_id] callback null, updates + + _validUserId: (user_id) -> + if !user_id? + return false + else + return !!user_id.match(/^[a-f0-9]{24}$/) diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 6720548b26..85dc9a1bd6 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -33,7 +33,7 @@ describe "Getting updates", -> v: 3 }, { op: [{ i: "two ", p: 4 }] - meta: { ts: @to - 2 * @minutes, user_id: @user_id } + meta: { ts: @to - 2 * @minutes } v: 4 }, { op: [{ i: "three ", p: 8 }] @@ -71,6 +71,5 @@ describe "Getting updates", -> meta: start_ts: @to - 2 * @minutes end_ts: @to - 2 * @minutes - user: @user v: 4 }] diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index fd2056e38d..67fc7b0d14 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -243,64 +243,92 @@ describe "UpdatesManager", -> @callback.calledWith(null, @updatesWithUserInfo).should.equal true describe "fillUserInfo", -> - beforeEach (done) -> - @user_id_1 = "user-id-1" - @user_id_2 = "user-id-2" - @updates = [{ - meta: - user_id: @user_id_1 - op: "mock-op-1" - }, { - meta: - user_id: @user_id_1 - op: "mock-op-2" - }, { - meta: - user_id: @user_id_2 - op: "mock-op-3" - }] - @user_info = - "user-id-1": { - email: "user1@sharelatex.com" - } - "user-id-2": { - email: "user2@sharelatex.com" - } - @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - callback null, @user_info[user_id] - sinon.spy @WebApiManager, "getUserInfo" + describe "with valid users", -> + beforeEach (done) -> + {ObjectId} = require "mongojs" + @user_id_1 = ObjectId().toString() + @user_id_2 = ObjectId().toString() + @updates = [{ + meta: + user_id: @user_id_1 + op: "mock-op-1" + }, { + meta: + user_id: @user_id_1 + op: "mock-op-2" + }, { + meta: + user_id: @user_id_2 + op: "mock-op-3" + }] + @user_info = {} + @user_info[@user_id_1] = email: "user1@sharelatex.com" + @user_info[@user_id_2] = email: "user2@sharelatex.com" + + @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + callback null, @user_info[user_id] + sinon.spy @WebApiManager, "getUserInfo" - @UpdatesManager.fillUserInfo @updates, (error, @results) => - done() - - it "should only call getUserInfo once for each user_id", -> - @WebApiManager.getUserInfo.calledTwice.should.equal true - @WebApiManager.getUserInfo - .calledWith(@user_id_1) - .should.equal true - @WebApiManager.getUserInfo - .calledWith(@user_id_2) - .should.equal true - - it "should return the updates with the user info filled", -> - expect(@results).to.deep.equal [{ - meta: - user: - email: "user1@sharelatex.com" - op: "mock-op-1" - }, { - meta: - user: - email: "user1@sharelatex.com" - op: "mock-op-2" - }, { - meta: - user: - email: "user2@sharelatex.com" - op: "mock-op-3" - }] + @UpdatesManager.fillUserInfo @updates, (error, @results) => + done() + it "should only call getUserInfo once for each user_id", -> + @WebApiManager.getUserInfo.calledTwice.should.equal true + @WebApiManager.getUserInfo + .calledWith(@user_id_1) + .should.equal true + @WebApiManager.getUserInfo + .calledWith(@user_id_2) + .should.equal true + it "should return the updates with the user info filled", -> + expect(@results).to.deep.equal [{ + meta: + user: + email: "user1@sharelatex.com" + op: "mock-op-1" + }, { + meta: + user: + email: "user1@sharelatex.com" + op: "mock-op-2" + }, { + meta: + user: + email: "user2@sharelatex.com" + op: "mock-op-3" + }] + + + describe "with invalid user ids", -> + beforeEach (done) -> + @updates = [{ + meta: + user_id: null + op: "mock-op-1" + }, { + meta: + user_id: "anonymous-user" + op: "mock-op-2" + }] + @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + callback null, @user_info[user_id] + sinon.spy @WebApiManager, "getUserInfo" + + @UpdatesManager.fillUserInfo @updates, (error, @results) => + done() + + it "should not call getUserInfo", -> + @WebApiManager.getUserInfo.called.should.equal false + + it "should return the updates without the user info filled", -> + expect(@results).to.deep.equal [{ + meta: {} + op: "mock-op-1" + }, { + meta: {} + op: "mock-op-2" + }] From 064bdc3eea9791712e4c729838de365afb4cb1eb Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Mar 2014 13:01:07 +0000 Subject: [PATCH 055/549] Take user id in from request header and pass to doc updater --- .../app/coffee/DocumentUpdaterManager.coffee | 4 +++- services/track-changes/app/coffee/HttpController.coffee | 3 ++- services/track-changes/app/coffee/RestoreManager.coffee | 6 +++--- .../test/acceptance/coffee/RestoringVersions.coffee | 4 ++-- .../acceptance/coffee/helpers/MockDocUpdaterApi.coffee | 4 ++-- .../acceptance/coffee/helpers/TrackChangesClient.coffee | 4 +++- .../DocumentUpdaterManagerTests.coffee | 9 ++++++--- .../coffee/HttpController/HttpControllerTests.coffee | 7 +++++-- .../coffee/RestoreManager/RestoreManagerTests.coffee | 7 ++++--- 9 files changed, 30 insertions(+), 18 deletions(-) diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee index 6dea2b3f14..edc2f11c68 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee @@ -20,13 +20,15 @@ module.exports = DocumentUpdaterManager = logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" callback error - setDocument: (project_id, doc_id, content, callback = (error) ->) -> + setDocument: (project_id, doc_id, content, user_id, callback = (error) ->) -> url = "#{Settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}" logger.log project_id:project_id, doc_id: doc_id, "setting doc in document updater" request.post { url: url json: lines: content.split("\n") + source: "restore" + user_id: user_id }, (error, res, body)-> if error? return callback(error) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index f3878ab7cd..70b6ca2557 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -49,7 +49,8 @@ module.exports = HttpController = restore: (req, res, next = (error) ->) -> {doc_id, project_id, version} = req.params + user_id = req.headers["x-user-id"] version = parseInt(version, 10) - RestoreManager.restoreToBeforeVersion project_id, doc_id, version, (error) -> + RestoreManager.restoreToBeforeVersion project_id, doc_id, version, user_id, (error) -> return next(error) if error? res.send 204 diff --git a/services/track-changes/app/coffee/RestoreManager.coffee b/services/track-changes/app/coffee/RestoreManager.coffee index 6c5ccef6fb..cfabca2fb7 100644 --- a/services/track-changes/app/coffee/RestoreManager.coffee +++ b/services/track-changes/app/coffee/RestoreManager.coffee @@ -3,10 +3,10 @@ DiffManager = require "./DiffManager" logger = require "logger-sharelatex" module.exports = RestoreManager = - restoreToBeforeVersion: (project_id, doc_id, version, callback = (error) ->) -> - logger.log project_id: project_id, doc_id: doc_id, version: version, "restoring document" + restoreToBeforeVersion: (project_id, doc_id, version, user_id, callback = (error) ->) -> + logger.log project_id: project_id, doc_id: doc_id, version: version, user_id: user_id, "restoring document" DiffManager.getDocumentBeforeVersion project_id, doc_id, version, (error, content) -> return callback(error) if error? - DocumentUpdaterManager.setDocument project_id, doc_id, content, (error) -> + DocumentUpdaterManager.setDocument project_id, doc_id, content, user_id, (error) -> return callback(error) if error? callback() diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index 21666bab3d..03f4c8b8f7 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -54,7 +54,7 @@ describe "Restoring a version", -> TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => throw error if error? - TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, (error) => + TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) => throw error if error? done() @@ -63,5 +63,5 @@ describe "Restoring a version", -> it "should set the doc in the doc updater", -> MockDocUpdaterApi.setDoc - .calledWith(@project_id, @doc_id, @restored_lines) + .calledWith(@project_id, @doc_id, @restored_lines, @user_id) .should.equal true diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee index fcddace406..97b4d93772 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -7,7 +7,7 @@ module.exports = MockDocUpdaterApi = getDoc: (project_id, doc_id, callback = (error) ->) -> callback null, @docs[doc_id] - setDoc: (project_id, doc_id, lines, callback = (error) ->) -> + setDoc: (project_id, doc_id, lines, user_id, callback = (error) ->) -> @docs[doc_id] ||= {} @docs[doc_id].lines = lines callback() @@ -23,7 +23,7 @@ module.exports = MockDocUpdaterApi = res.send JSON.stringify doc app.post "/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => - @setDoc req.params.project_id, req.params.doc_id, req.body.lines, (errr, doc) -> + @setDoc req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, (errr, doc) -> if error? res.send 500 else diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index ab85b67171..6c65941570 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -30,9 +30,11 @@ module.exports = TrackChangesClient = response.statusCode.should.equal 200 callback null, JSON.parse(body) - restoreDoc: (project_id, doc_id, version, callback = (error) ->) -> + restoreDoc: (project_id, doc_id, version, user_id, callback = (error) ->) -> request.post { url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/version/#{version}/restore" + headers: + "X-User-Id": user_id }, (error, response, body) => response.statusCode.should.equal 204 callback null \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee index 25c61f6279..8c460f4757 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee @@ -54,11 +54,12 @@ describe "DocumentUpdaterManager", -> describe "setDocument", -> beforeEach -> @content = "mock content" + @user_id = "user-id-123" describe "successfully", -> beforeEach -> @request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}) - @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @callback + @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @user_id, @callback it 'should set the document in the document updater', -> url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}" @@ -67,6 +68,8 @@ describe "DocumentUpdaterManager", -> url: url json: lines: @content.split("\n") + source: "restore" + user_id: @user_id }).should.equal true it "should call the callback", -> @@ -75,7 +78,7 @@ describe "DocumentUpdaterManager", -> describe "when the document updater API returns an error", -> beforeEach -> @request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) - @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @callback + @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @user_id, @callback it "should return an error to the callback", -> @callback.calledWith(@error).should.equal true @@ -83,7 +86,7 @@ describe "DocumentUpdaterManager", -> describe "when the document updater returns a failure error code", -> beforeEach -> @request.post = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") - @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @callback + @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @user_id, @callback it "should return the callback with an error", -> @callback diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 791bbd1d04..da84db026f 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -15,6 +15,7 @@ describe "HttpController", -> @doc_id = "doc-id-123" @project_id = "project-id-123" @next = sinon.stub() + @user_id = "mock-user-123" describe "flushUpdatesWithLock", -> beforeEach -> @@ -102,15 +103,17 @@ describe "HttpController", -> doc_id: @doc_id project_id: @project_id version: @version + headers: + "x-user-id": @user_id @res = send: sinon.stub() - @RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(3) + @RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(4) @HttpController.restore @req, @res, @next it "should restore the document", -> @RestoreManager.restoreToBeforeVersion - .calledWith(@project_id, @doc_id, parseInt(@version, 10)) + .calledWith(@project_id, @doc_id, parseInt(@version, 10), @user_id) .should.equal true it "should return a success code", -> diff --git a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee index 36088d4405..11941dab91 100644 --- a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee @@ -14,14 +14,15 @@ describe "RestoreManager", -> @callback = sinon.stub() @project_id = "mock-project-id" @doc_id = "mock-doc-id" + @user_id = "mock-user-id" @version = 42 describe "restoreToBeforeVersion", -> beforeEach -> @content = "mock content" - @DocumentUpdaterManager.setDocument = sinon.stub().callsArg(3) + @DocumentUpdaterManager.setDocument = sinon.stub().callsArg(4) @DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, @content) - @RestoreManager.restoreToBeforeVersion @project_id, @doc_id, @version, @callback + @RestoreManager.restoreToBeforeVersion @project_id, @doc_id, @version, @user_id, @callback it "should get the content before the requested version", -> @DiffManager.getDocumentBeforeVersion @@ -30,7 +31,7 @@ describe "RestoreManager", -> it "should set the document in the document updater", -> @DocumentUpdaterManager.setDocument - .calledWith(@project_id, @doc_id, @content) + .calledWith(@project_id, @doc_id, @content, @user_id) .should.equal true it "should call the callback", -> From 4ecb17b16e9041cc651ae5e9882a9ca7fed73583 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Mar 2014 15:24:38 +0000 Subject: [PATCH 056/549] Put multiple ops in one update --- .../app/coffee/DiffGenerator.coffee | 47 ++++++++------- .../app/coffee/UpdateCompressor.coffee | 28 ++++++--- .../coffee/AppendingUpdatesTests.coffee | 52 ++++++++++++---- .../DiffGenerator/DiffGeneratorTests.coffee | 60 ++++++++++--------- .../UpdateCompressorTests.coffee | 29 ++++++++- 5 files changed, 147 insertions(+), 69 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index fdec79d6f4..b8028d9f99 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -9,7 +9,11 @@ module.exports = DiffGenerator = ConsistencyError: ConsistencyError rewindUpdate: (content, update) -> - op = update.op + for op in update.op.slice().reverse() + content = DiffGenerator.rewindOp content, op + return content + + rewindOp: (content, op) -> if op.i? textToBeRemoved = content.slice(op.p, op.p + op.i.length) if op.i != textToBeRemoved @@ -33,9 +37,8 @@ module.exports = DiffGenerator = diff = DiffGenerator.applyUpdateToDiff diff, update return diff - applyUpdateToDiff: (diff, update) -> + applyOpToDiff: (diff, op, meta) -> position = 0 - op = update.op remainingDiff = diff.slice() {consumedDiff, remainingDiff} = DiffGenerator._consumeToOffset(remainingDiff, op.p) @@ -44,15 +47,19 @@ module.exports = DiffGenerator = if op.i? newDiff.push i: op.i - meta: update.meta + meta: meta else if op.d? - {consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteUpdate remainingDiff, update + {consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteOp remainingDiff, op, meta newDiff.push(consumedDiff...) newDiff.push(remainingDiff...) return newDiff + applyUpdateToDiff: (diff, update) -> + for op in update.op + diff = DiffGenerator.applyOpToDiff diff, op, update.meta + return diff _consumeToOffset: (remainingDiff, totalOffset) -> consumedDiff = [] @@ -76,25 +83,24 @@ module.exports = DiffGenerator = consumedDiff.push part throw new Error("Ran out of diff to consume. Offset is too small") - _consumeDiffAffectedByDeleteUpdate: (remainingDiff, deleteUpdate) -> + _consumeDiffAffectedByDeleteOp: (remainingDiff, deleteOp, meta) -> consumedDiff = [] - remainingUpdate = deleteUpdate - while remainingUpdate - {newPart, remainingDiff, remainingUpdate} = DiffGenerator._consumeDeletedPart remainingDiff, remainingUpdate + remainingOp = deleteOp + while remainingOp + {newPart, remainingDiff, remainingOp} = DiffGenerator._consumeDeletedPart remainingDiff, remainingOp, meta consumedDiff.push newPart if newPart? return { consumedDiff: consumedDiff remainingDiff: remainingDiff } - _consumeDeletedPart: (remainingDiff, deleteUpdate) -> + _consumeDeletedPart: (remainingDiff, op, meta) -> part = remainingDiff.shift() partLength = DiffGenerator._getLengthOfDiffPart part - op = deleteUpdate.op if part.d? # Skip existing deletes - remainingUpdate = deleteUpdate + remainingOp = op newPart = part else if partLength > op.d.length @@ -109,11 +115,11 @@ module.exports = DiffGenerator = if part.u? newPart = d: op.d - meta: deleteUpdate.meta + meta: meta else if part.i? newPart = null - remainingUpdate = null + remainingOp = null else if partLength == op.d.length # The entire part has been deleted, but it is the last part @@ -125,11 +131,11 @@ module.exports = DiffGenerator = if part.u? newPart = d: op.d - meta: deleteUpdate.meta + meta: meta else if part.i? newPart = null - remainingUpdate = null + remainingOp = null else if partLength < op.d.length # The entire part has been deleted and there is more @@ -142,18 +148,17 @@ module.exports = DiffGenerator = if part.u newPart = d: part.u - meta: deleteUpdate.meta + meta: meta else if part.i? newPart = null - remainingUpdate = - op: { p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)) } - meta: deleteUpdate.meta + remainingOp = + p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)) return { newPart: newPart remainingDiff: remainingDiff - remainingUpdate: remainingUpdate + remainingOp: remainingOp } _slicePart: (basePart, from, to) -> diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index e5830bde5a..5ba379a8d9 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -16,24 +16,38 @@ module.exports = UpdateCompressor = # op: op2 # meta: { start_ts: ... , end_ts: ..., user_id: ... } # }] - convertRawUpdatesToCompressedFormat: (updates) -> - normalizedUpdates = [] + convertToSingleOpUpdates: (updates) -> + splitUpdates = [] for update in updates for op in update.op - normalizedUpdates.push + splitUpdates.push op: op meta: start_ts: update.meta.start_ts or update.meta.ts end_ts: update.meta.end_ts or update.meta.ts user_id: update.meta.user_id v: update.v - return normalizedUpdates + return splitUpdates + + concatUpdatesWithSameVersion: (updates) -> + concattedUpdates = [] + for update in updates + lastUpdate = concattedUpdates[concattedUpdates.length - 1] + if lastUpdate? and lastUpdate.v == update.v + lastUpdate.op.push update.op + else + concattedUpdates.push + op: [ update.op ] + meta: update.meta + v: update.v + return concattedUpdates compressRawUpdates: (lastPreviousUpdate, rawUpdates) -> - updates = UpdateCompressor.convertRawUpdatesToCompressedFormat(rawUpdates) if lastPreviousUpdate? - updates.unshift(lastPreviousUpdate) - return UpdateCompressor.compressUpdates(updates) + rawUpdates = [lastPreviousUpdate].concat(rawUpdates) + updates = UpdateCompressor.convertToSingleOpUpdates(rawUpdates) + updates = UpdateCompressor.compressUpdates(updates) + return UpdateCompressor.concatUpdatesWithSameVersion(updates) compressUpdates: (updates) -> return [] if updates.length == 0 diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 031659f24b..827d238239 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -34,9 +34,9 @@ describe "Appending doc ops to the history", -> done() it "should insert the compressed op into mongo", -> - expect(@updates[0].op).to.deep.equal { + expect(@updates[0].op).to.deep.equal [{ p: 3, i: "foo" - } + }] it "should insert the correct version number into mongo", -> expect(@updates[0].v).to.equal 5 @@ -84,9 +84,9 @@ describe "Appending doc ops to the history", -> done() it "should combine all the updates into one", -> - expect(@updates[0].op).to.deep.equal { + expect(@updates[0].op).to.deep.equal [{ p: 3, i: "foobar" - } + }] it "should insert the correct version number into mongo", -> expect(@updates[0].v).to.equal 8 @@ -114,26 +114,26 @@ describe "Appending doc ops to the history", -> done() it "should keep the updates separate", -> - expect(@updates[0].op).to.deep.equal { + expect(@updates[0].op).to.deep.equal [{ p: 3, i: "foo" - } - expect(@updates[1].op).to.deep.equal { + }] + expect(@updates[1].op).to.deep.equal [{ p: 6, i: "bar" - } + }] describe "when the updates need processing in batches", -> before (done) -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() updates = [] - @expectedOp = { p:0, i: "" } + @expectedOp = [{ p:0, i: "" }] for i in [0..250] updates.push { op: [{i: "a", p: 0}] meta: { ts: Date.now(), user_id: @user_id } v: i } - @expectedOp.i = "a" + @expectedOp.i + @expectedOp[0].i = "a" + @expectedOp[0].i TrackChangesClient.pushRawUpdates @doc_id, updates, (error) => throw error if error? @@ -147,3 +147,35 @@ describe "Appending doc ops to the history", -> it "should insert the correct version number into mongo", -> expect(@updates[0].v).to.equal 250 + + describe "when there are multiple ops in each update", -> + before (done) -> + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + oneDay = 24 * 60 * 60 * 1000 + TrackChangesClient.pushRawUpdates @doc_id, [{ + op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }, { + op: [{ i: "b", p: 6 }, { i: "a", p: 7 }, { i: "r", p: 8 }] + meta: { ts: Date.now() + oneDay, user_id: @user_id } + v: 4 + }], (error) => + throw error if error? + TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => + throw error if error? + done() + + it "should insert the compressed ops into mongo", -> + expect(@updates[0].op).to.deep.equal [{ + p: 3, i: "foo" + }] + expect(@updates[1].op).to.deep.equal [{ + p: 6, i: "bar" + }] + + it "should insert the correct version numbers into mongo", -> + expect(@updates[0].v).to.equal 3 + expect(@updates[1].v).to.equal 4 + diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee index 18b2e637e1..9fd59d69f5 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -14,38 +14,40 @@ describe "DiffGenerator", -> start_ts: @ts, end_ts: @ts, user_id: @user_id } - describe "rewindUpdate", -> + describe "rewindOp", -> describe "rewinding an insert", -> it "should undo the insert", -> content = "hello world" - update = - op: { p: 6, i: "wo" } - rewoundContent = @DiffGenerator.rewindUpdate content, update + rewoundContent = @DiffGenerator.rewindOp content, { p: 6, i: "wo" } rewoundContent.should.equal "hello rld" describe "rewinding a delete", -> it "should undo the delete", -> content = "hello rld" - update = - op: { p: 6, d: "wo" } - rewoundContent = @DiffGenerator.rewindUpdate content, update + rewoundContent = @DiffGenerator.rewindOp content, { p: 6, d: "wo" } rewoundContent.should.equal "hello world" describe "with an inconsistent update", -> it "should throw an error", -> content = "hello world" - update = - op: { p: 6, i: "foo" } expect( () => - @DiffGenerator.rewindUpdate content, update + @DiffGenerator.rewindOp content, { p: 6, i: "foo" } ).to.throw(@DiffGenerator.ConsistencyError) + describe "rewindUpdate", -> + it "should rewind ops in reverse", -> + content = "aaabbbccc" + update = + op: [{ p: 3, i: "bbb" }, { p: 6, i: "ccc" }] + rewoundContent = @DiffGenerator.rewindUpdate content, update + rewoundContent.should.equal "aaa" + describe "rewindUpdates", -> it "should rewind updates in reverse", -> content = "aaabbbccc" updates = [ - { op: { p: 3, i: "bbb" } }, - { op: { p: 6, i: "ccc" } } + { op: [{ p: 3, i: "bbb" }] }, + { op: [{ p: 6, i: "ccc" }] } ] rewoundContent = @DiffGenerator.rewindUpdates content, updates rewoundContent.should.equal "aaa" @@ -83,7 +85,7 @@ describe "DiffGenerator", -> it "should insert into the middle of (u)nchanged text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foobar" } ], - { op: { p: 3, i: "baz" }, meta: @meta } + { op: [{ p: 3, i: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "foo" } @@ -94,7 +96,7 @@ describe "DiffGenerator", -> it "should insert into the start of (u)changed text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foobar" } ], - { op: { p: 0, i: "baz" }, meta: @meta } + { op: [{ p: 0, i: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { i: "baz", meta: @meta } @@ -104,7 +106,7 @@ describe "DiffGenerator", -> it "should insert into the end of (u)changed text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foobar" } ], - { op: { p: 6, i: "baz" }, meta: @meta } + { op: [{ p: 6, i: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "foobar" } @@ -114,7 +116,7 @@ describe "DiffGenerator", -> it "should insert into the middle of (i)inserted text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { i: "foobar", meta: @meta } ], - { op: { p: 3, i: "baz" }, meta: @meta } + { op: [{ p: 3, i: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { i: "foo", meta: @meta } @@ -128,7 +130,7 @@ describe "DiffGenerator", -> { d: "deleted", meta: @meta } { u: "foobar" } ], - { op: { p: 3, i: "baz" }, meta: @meta } + { op: [{ p: 3, i: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { d: "deleted", meta: @meta } @@ -142,7 +144,7 @@ describe "DiffGenerator", -> it "should delete from the middle of (u)nchanged text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: { p: 3, d: "baz" }, meta: @meta } + { op: [{ p: 3, d: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "foo" } @@ -153,7 +155,7 @@ describe "DiffGenerator", -> it "should delete from the start of (u)nchanged text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: { p: 0, d: "foo" }, meta: @meta } + { op: [{ p: 0, d: "foo" }], meta: @meta } ) expect(diff).to.deep.equal([ { d: "foo", meta: @meta } @@ -163,7 +165,7 @@ describe "DiffGenerator", -> it "should delete from the end of (u)nchanged text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: { p: 6, d: "bar" }, meta: @meta } + { op: [{ p: 6, d: "bar" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "foobaz" } @@ -173,7 +175,7 @@ describe "DiffGenerator", -> it "should delete across multiple (u)changed text parts", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foo" }, { u: "baz" }, { u: "bar" } ], - { op: { p: 2, d: "obazb" }, meta: @meta } + { op: [{ p: 2, d: "obazb" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "fo" } @@ -187,7 +189,7 @@ describe "DiffGenerator", -> it "should delete from the middle of (i)nserted text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { i: "foobazbar", meta: @meta } ], - { op: { p: 3, d: "baz" }, meta: @meta } + { op: [{ p: 3, d: "baz" }], meta: @meta } ) expect(diff).to.deep.equal([ { i: "foo", meta: @meta } @@ -197,7 +199,7 @@ describe "DiffGenerator", -> it "should delete from the start of (u)nchanged text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { i: "foobazbar", meta: @meta } ], - { op: { p: 0, d: "foo" }, meta: @meta } + { op: [{ p: 0, d: "foo" }], meta: @meta } ) expect(diff).to.deep.equal([ { i: "bazbar", meta: @meta } @@ -206,7 +208,7 @@ describe "DiffGenerator", -> it "should delete from the end of (u)nchanged text", -> diff = @DiffGenerator.applyUpdateToDiff( [ { i: "foobazbar", meta: @meta } ], - { op: { p: 6, d: "bar" }, meta: @meta } + { op: [{ p: 6, d: "bar" }], meta: @meta } ) expect(diff).to.deep.equal([ { i: "foobaz", meta: @meta } @@ -215,7 +217,7 @@ describe "DiffGenerator", -> it "should delete across multiple (u)changed and (i)nserted text parts", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foo" }, { i: "baz", meta: @meta }, { u: "bar" } ], - { op: { p: 2, d: "obazb" }, meta: @meta } + { op: [{ p: 2, d: "obazb" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "fo" } @@ -228,7 +230,7 @@ describe "DiffGenerator", -> it "should delete across multiple (u)changed and (d)deleted text parts", -> diff = @DiffGenerator.applyUpdateToDiff( [ { u: "foo" }, { d: "baz", meta: @meta }, { u: "bar" } ], - { op: { p: 2, d: "ob" }, meta: @meta } + { op: [{ p: 2, d: "ob" }], meta: @meta } ) expect(diff).to.deep.equal([ { u: "fo" } @@ -243,7 +245,7 @@ describe "DiffGenerator", -> expect( () => @DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: { p: 3, d: "xxx" }, meta: @meta } + { op: [{ p: 3, d: "xxx" }], meta: @meta } ) ).to.throw(@DiffGenerator.ConsistencyError) @@ -251,7 +253,7 @@ describe "DiffGenerator", -> expect( () => @DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: { p: 0, d: "xxx" }, meta: @meta } + { op: [{ p: 0, d: "xxx" }], meta: @meta } ) ).to.throw(@DiffGenerator.ConsistencyError) @@ -259,7 +261,7 @@ describe "DiffGenerator", -> expect( () => @DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: { p: 6, d: "xxx" }, meta: @meta } + { op: [{ p: 6, d: "xxx" }] , meta: @meta } ) ).to.throw(@DiffGenerator.ConsistencyError) diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index a4de76c5ac..ec41dcca30 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -13,9 +13,9 @@ describe "UpdateCompressor", -> @ts1 = Date.now() @ts2 = Date.now() + 1000 - describe "convertRawUpdatesToCompressedFormat", -> + describe "convertToSingleOpUpdates", -> it "should split grouped updates into individual updates", -> - expect(@UpdateCompressor.convertRawUpdatesToCompressedFormat [{ + expect(@UpdateCompressor.convertToSingleOpUpdates [{ op: [ @op1 = { p: 0, i: "Foo" }, @op2 = { p: 6, i: "bar"} ] meta: { ts: @ts1, user_id: @user_id } v: 42 @@ -38,6 +38,31 @@ describe "UpdateCompressor", -> v: 43 }] + describe "concatUpdatesWithSameVersion", -> + it "should concat updates with the same version", -> + expect(@UpdateCompressor.concatUpdatesWithSameVersion [{ + op: @op1 = { p: 0, i: "Foo" } + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + v: 42 + }, { + op: @op2 = { p: 6, i: "bar" } + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + v: 42 + }, { + op: @op3 = { p: 10, i: "baz" } + meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id } + v: 43 + }]) + .to.deep.equal [{ + op: [ @op1, @op2 ] + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + v: 42 + }, { + op: [ @op3 ] + meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id } + v: 43 + }] + describe "compress", -> describe "insert - insert", -> it "should append one insert to the other", -> From 7fa88f6b415f5871145d519fd506d0353f659eb1 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Mar 2014 17:39:40 +0000 Subject: [PATCH 057/549] Summarise updates when returning them --- .../app/coffee/HttpController.coffee | 40 +++++- .../coffee/GettingUpdatesTests.coffee | 126 +++++++++++------- .../HttpController/HttpControllerTests.coffee | 75 +++++++++-- 3 files changed, 178 insertions(+), 63 deletions(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 70b6ca2557..215d440129 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -40,12 +40,40 @@ module.exports = HttpController = UpdatesManager.getUpdatesWithUserInfo doc_id, to: to, limit: limit, (error, updates) -> return next(error) if error? - formattedUpdates = for update in updates - { - meta: update.meta - v: update.v - } - res.send JSON.stringify updates: formattedUpdates + res.send JSON.stringify updates: HttpController._buildUpdatesView(updates) + + TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 + _buildUpdatesView: (updates) -> + view = [] + for update in updates.slice().reverse() + lastUpdate = view[view.length - 1] + if lastUpdate and update.meta.start_ts - lastUpdate.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + if update.meta.user? + userExists = false + for user in lastUpdate.meta.users + if user.id == update.meta.user.id + userExists = true + break + if !userExists + lastUpdate.meta.users.push update.meta.user + lastUpdate.meta.start_ts = Math.min(lastUpdate.meta.start_ts, update.meta.start_ts) + lastUpdate.meta.end_ts = Math.max(lastUpdate.meta.end_ts, update.meta.end_ts) + lastUpdate.toV = update.v + else + newUpdate = + meta: + users: [] + start_ts: update.meta.start_ts + end_ts: update.meta.end_ts + fromV: update.v + toV: update.v + + if update.meta.user? + newUpdate.meta.users.push update.meta.user + + view.push newUpdate + + return view.reverse() restore: (req, res, next = (error) ->) -> {doc_id, project_id, version} = req.params diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 85dc9a1bd6..99a103bb74 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -11,12 +11,10 @@ TrackChangesClient = require "./helpers/TrackChangesClient" MockWebApi = require "./helpers/MockWebApi" describe "Getting updates", -> - before (done) -> + before -> @now = Date.now() @to = @now @user_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() @minutes = 60 * 1000 @@ -27,49 +25,87 @@ describe "Getting updates", -> id: @user_id sinon.spy MockWebApi, "getUser" - @updates = [{ - op: [{ i: "one ", p: 0 }] - meta: { ts: @to - 4 * @minutes, user_id: @user_id } - v: 3 - }, { - op: [{ i: "two ", p: 4 }] - meta: { ts: @to - 2 * @minutes } - v: 4 - }, { - op: [{ i: "three ", p: 8 }] - meta: { ts: @to, user_id: @user_id } - v: @toVersion = 5 - }, { - op: [{ i: "four", p: 14 }] - meta: { ts: @to + 2 * @minutes, user_id: @user_id } - v: 6 - }] - - TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.getUpdates @project_id, @doc_id, { to: @toVersion, limit: 2 }, (error, body) => - throw error if error? - @updates = body.updates - done() - after: () -> MockWebApi.getUser.restore() - it "should fetch the user details from the web api", -> - MockWebApi.getUser - .calledWith(@user_id) - .should.equal true + describe "getting updates in the middle", -> + before (done) -> + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + @updates = [{ + op: [{ i: "one ", p: 0 }] + meta: { ts: @to - 20 * @minutes, user_id: @user_id } + v: 3 + }, { + op: [{ i: "two ", p: 4 }] + meta: { ts: @to - 10 * @minutes } + v: 4 + }, { + op: [{ i: "three ", p: 8 }] + meta: { ts: @to, user_id: @user_id } + v: @toVersion = 5 + }, { + op: [{ i: "four", p: 14 }] + meta: { ts: @to + 2 * @minutes, user_id: @user_id } + v: 6 + }] - it "should return the updates", -> - expect(@updates).to.deep.equal [{ - meta: - start_ts: @to - end_ts: @to - user: @user - v: 5 - }, { - meta: - start_ts: @to - 2 * @minutes - end_ts: @to - 2 * @minutes - v: 4 - }] + TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + throw error if error? + TrackChangesClient.getUpdates @project_id, @doc_id, { to: @toVersion, limit: 2 }, (error, body) => + throw error if error? + @updates = body.updates + done() + + it "should fetch the user details from the web api", -> + MockWebApi.getUser + .calledWith(@user_id) + .should.equal true + + it "should return the updates", -> + expect(@updates).to.deep.equal [{ + meta: + start_ts: @to + end_ts: @to + users: [@user] + fromV: 5 + toV: 5 + }, { + meta: + users: [] + start_ts: @to - 10 * @minutes + end_ts: @to - 10 * @minutes + toV: 4 + fromV: 4 + }] + + describe "getting updates that should be combined", -> + before (done) -> + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + @updates = [{ + op: [{ i: "two ", p: 4 }] + meta: { ts: @to - 2 * @minutes, user_id: @user_id } + v: 4 + }, { + op: [{ i: "three ", p: 8 }] + meta: { ts: @to, user_id: @user_id } + v: @toVersion = 5 + }] + + TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + throw error if error? + TrackChangesClient.getUpdates @project_id, @doc_id, { to: @toVersion, limit: 2 }, (error, body) => + throw error if error? + @updates = body.updates + done() + + it "should return the updates", -> + expect(@updates).to.deep.equal [{ + meta: + start_ts: @to - 2 * @minutes + end_ts: @to + users: [@user] + fromV: 4 + toV: 5 + }] diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index da84db026f..f6ec9bdee8 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -16,6 +16,7 @@ describe "HttpController", -> @project_id = "project-id-123" @next = sinon.stub() @user_id = "mock-user-123" + @now = Date.now() describe "flushUpdatesWithLock", -> beforeEach -> @@ -73,12 +74,9 @@ describe "HttpController", -> limit: @limit.toString() @res = send: sinon.stub() - @rawUpdates = [{ - v: @v = 42 - op: @op = "mock-op" - meta: @meta = "mock-meta" - doc_id: @doc_id - }] + @rawUpdates = ["raw-updates"] + @updatesView = ["updates-view"] + @HttpController._buildUpdatesView = sinon.stub().returns(@updatesView) @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @rawUpdates) @HttpController.getUpdates @req, @res, @next @@ -87,13 +85,66 @@ describe "HttpController", -> .calledWith(@doc_id, to: @to, limit: @limit) .should.equal true + it "should build the updates view", -> + @HttpController._buildUpdatesView + .calledWith(@rawUpdates) + .should.equal true + it "should return the formatted updates", -> - updates = for update in @rawUpdates - { - meta: @meta - v: @v - } - @res.send.calledWith(JSON.stringify(updates: updates)).should.equal true + @res.send.calledWith(JSON.stringify(updates: @updatesView)).should.equal true + + describe "_buildUpdatesView", -> + it "should concat updates that are close in time", -> + expect(@HttpController._buildUpdatesView [{ + meta: + user: @user_2 = { id: "mock-user-2" } + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + meta: + user: @user_1 = { id: "mock-user-1" } + start_ts: @now + end_ts: @now + 10 + v: 4 + }]).to.deep.equal [{ + meta: + users: [@user_1, @user_2] + start_ts: @now + end_ts: @now + 30 + fromV: 4 + toV: 5 + }] + + it "should leave updates that are far apart in time", -> + oneDay = 1000 * 60 * 60 * 24 + expect(@HttpController._buildUpdatesView [{ + meta: + user: @user_2 = { id: "mock-user-2" } + start_ts: @now + oneDay + end_ts: @now + oneDay + 10 + v: 5 + }, { + meta: + user: @user_1 = { id: "mock-user-2" } + start_ts: @now + end_ts: @now + 10 + v: 4 + }]).to.deep.equal [{ + meta: + users: [@user_2] + start_ts: @now + oneDay + end_ts: @now + oneDay + 10 + fromV: 5 + toV: 5 + }, { + meta: + users: [@user_1] + start_ts: @now + end_ts: @now + 10 + fromV: 4 + toV: 4 + }] describe "RestoreManager", -> beforeEach -> From 575afae0481b0951e0b03849f96978db6744b9ff Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 12 Mar 2014 13:27:18 +0000 Subject: [PATCH 058/549] Compress adjacent inserts and deletes into one diff item --- .../app/coffee/DiffGenerator.coffee | 17 +++++++ .../coffee/GettingADiffTests.coffee | 3 +- .../DiffGenerator/DiffGeneratorTests.coffee | 46 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index b8028d9f99..996c936612 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -35,8 +35,25 @@ module.exports = DiffGenerator = diff = [ u: initialContent ] for update in updates diff = DiffGenerator.applyUpdateToDiff diff, update + diff = DiffGenerator.compressDiff diff return diff + compressDiff: (diff) -> + newDiff = [] + for part in diff + lastPart = newDiff[newDiff.length - 1] + if lastPart? and lastPart.i? and part.i? and lastPart.meta.user_id == part.meta.user_id + lastPart.i += part.i + lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) + lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) + else if lastPart? and lastPart.d? and part.d? and lastPart.meta.user_id == part.meta.user_id + lastPart.d += part.d + lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) + lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) + else + newDiff.push part + return newDiff + applyOpToDiff: (diff, op, meta) -> position = 0 diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index ed0e0e42d6..6fe573c19e 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -51,8 +51,7 @@ describe "Getting a diff", -> @lines = ["one two three four"] @expected_diff = [ { u: "one " } - { i: "two ", meta: { start_ts: @from + twoMinutes, end_ts: @from + twoMinutes, user: @user } } - { i: "three ", meta: { start_ts: @to - twoMinutes, end_ts: @to - twoMinutes, user: @user } } + { i: "two three ", meta: { start_ts: @from + twoMinutes, end_ts: @to - twoMinutes, user: @user } } ] MockDocUpdaterApi.docs[@doc_id] = diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee index 9fd59d69f5..43c86cfb5d 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -10,6 +10,7 @@ describe "DiffGenerator", -> @DiffGenerator = SandboxedModule.require modulePath @ts = Date.now() @user_id = "mock-user-id" + @user_id_2 = "mock-user-id-2" @meta = { start_ts: @ts, end_ts: @ts, user_id: @user_id } @@ -62,6 +63,7 @@ describe "DiffGenerator", -> { i: "mock-update-3" } ] @DiffGenerator.applyUpdateToDiff = sinon.stub().returns(@diff) + @DiffGenerator.compressDiff = sinon.stub().returns(@diff) @result = @DiffGenerator.buildDiff(@content, @updates) it "should return the diff", -> @@ -80,6 +82,50 @@ describe "DiffGenerator", -> .calledWith(sinon.match.any, update) .should.equal true + it "should compress the diff", -> + @DiffGenerator.compressDiff + .calledWith(@diff) + .should.equal true + + describe "compressDiff", -> + describe "with adjacent inserts with the same user_id", -> + it "should create one update with combined meta data and min/max timestamps", -> + diff = @DiffGenerator.compressDiff([ + { i: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} + { i: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id }} + ]) + expect(diff).to.deep.equal([ + { i: "foobar", meta: { start_ts: 5, end_ts: 20, user_id: @user_id }} + ]) + + describe "with adjacent inserts with different user_ids", -> + it "should leave the inserts unchanged", -> + input = [ + { i: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} + { i: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id_2 }} + ] + output = @DiffGenerator.compressDiff(input) + expect(output).to.deep.equal(input) + + describe "with adjacent deletes with the same user_id", -> + it "should create one update with combined meta data and min/max timestamps", -> + diff = @DiffGenerator.compressDiff([ + { d: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} + { d: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id }} + ]) + expect(diff).to.deep.equal([ + { d: "foobar", meta: { start_ts: 5, end_ts: 20, user_id: @user_id }} + ]) + + describe "with adjacent deletes with different user_ids", -> + it "should leave the deletes unchanged", -> + input = [ + { d: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} + { d: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id_2 }} + ] + output = @DiffGenerator.compressDiff(input) + expect(output).to.deep.equal(input) + describe "applyUpdateToDiff", -> describe "an insert", -> it "should insert into the middle of (u)nchanged text", -> From 9977a418c3fdda76e94291abf40df5b56b7d2fa0 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 12 Mar 2014 14:04:33 +0000 Subject: [PATCH 059/549] Compare diffs with user.id, not user_id --- .../app/coffee/DiffGenerator.coffee | 19 ++++++++++-------- .../DiffGenerator/DiffGeneratorTests.coffee | 20 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index 996c936612..c8229b96dc 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -42,14 +42,17 @@ module.exports = DiffGenerator = newDiff = [] for part in diff lastPart = newDiff[newDiff.length - 1] - if lastPart? and lastPart.i? and part.i? and lastPart.meta.user_id == part.meta.user_id - lastPart.i += part.i - lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) - lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) - else if lastPart? and lastPart.d? and part.d? and lastPart.meta.user_id == part.meta.user_id - lastPart.d += part.d - lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) - lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) + if lastPart? and lastPart.meta?.user? and part.meta?.user? + if lastPart.i? and part.i? and lastPart.meta.user.id == part.meta.user.id + lastPart.i += part.i + lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) + lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) + else if lastPart.d? and part.d? and lastPart.meta.user.id == part.meta.user.id + lastPart.d += part.d + lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) + lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) + else + newDiff.push part else newDiff.push part return newDiff diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee index 43c86cfb5d..848086dc2e 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -91,18 +91,18 @@ describe "DiffGenerator", -> describe "with adjacent inserts with the same user_id", -> it "should create one update with combined meta data and min/max timestamps", -> diff = @DiffGenerator.compressDiff([ - { i: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} - { i: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id }} + { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} + { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id } }} ]) expect(diff).to.deep.equal([ - { i: "foobar", meta: { start_ts: 5, end_ts: 20, user_id: @user_id }} + { i: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: @user_id } }} ]) describe "with adjacent inserts with different user_ids", -> it "should leave the inserts unchanged", -> input = [ - { i: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} - { i: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id_2 }} + { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} + { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id_2 } }} ] output = @DiffGenerator.compressDiff(input) expect(output).to.deep.equal(input) @@ -110,18 +110,18 @@ describe "DiffGenerator", -> describe "with adjacent deletes with the same user_id", -> it "should create one update with combined meta data and min/max timestamps", -> diff = @DiffGenerator.compressDiff([ - { d: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} - { d: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id }} + { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} + { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id } }} ]) expect(diff).to.deep.equal([ - { d: "foobar", meta: { start_ts: 5, end_ts: 20, user_id: @user_id }} + { d: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: @user_id } }} ]) describe "with adjacent deletes with different user_ids", -> it "should leave the deletes unchanged", -> input = [ - { d: "foo", meta: { start_ts: 10, end_ts: 20, user_id: @user_id }} - { d: "bar", meta: { start_ts: 5, end_ts: 15, user_id: @user_id_2 }} + { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} + { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id_2 } }} ] output = @DiffGenerator.compressDiff(input) expect(output).to.deep.equal(input) From 540aaf0672eeed268224e912f63d36f147595348 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 13 Mar 2014 14:30:01 +0000 Subject: [PATCH 060/549] Add in some null checks --- services/track-changes/app/coffee/DiffGenerator.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index c8229b96dc..7f6749dbd4 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -191,7 +191,7 @@ module.exports = DiffGenerator = return part _getLengthOfDiffPart: (part) -> - (part.u or part.d or part.i).length + (part.u or part.d or part.i or '').length _getContentOfPart: (part) -> - part.u or part.d or part.i + part.u or part.d or part.i or '' From 22a806a200a59ea3f776e3645a1f13a6587da669 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 17 Mar 2014 14:54:02 +0000 Subject: [PATCH 061/549] Fix issues with consuming delete updates beyond the end of the diff --- .../app/coffee/DiffGenerator.coffee | 13 ++++++----- .../DiffGenerator/DiffGeneratorTests.coffee | 23 +++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index 7f6749dbd4..9593f42f36 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -94,19 +94,20 @@ module.exports = DiffGenerator = consumedDiff.push DiffGenerator._slicePart part, 0, partOffset if partOffset < length remainingDiff.unshift DiffGenerator._slicePart part, partOffset - return { - consumedDiff: consumedDiff - remainingDiff: remainingDiff - } + break else position += length consumedDiff.push part - throw new Error("Ran out of diff to consume. Offset is too small") + + return { + consumedDiff: consumedDiff + remainingDiff: remainingDiff + } _consumeDiffAffectedByDeleteOp: (remainingDiff, deleteOp, meta) -> consumedDiff = [] remainingOp = deleteOp - while remainingOp + while remainingOp and remainingDiff.length > 0 {newPart, remainingDiff, remainingOp} = DiffGenerator._consumeDeletedPart remainingDiff, remainingOp, meta consumedDiff.push newPart if newPart? return { diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee index 848086dc2e..3263d84dda 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -311,4 +311,27 @@ describe "DiffGenerator", -> ) ).to.throw(@DiffGenerator.ConsistencyError) + describe "when the last update in the existing diff is a delete", -> + it "should insert the new update before the delete", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { d: "bar", meta: @meta } ], + { op: [{ p: 3, i: "baz" }], meta: @meta } + ) + expect(diff).to.deep.equal([ + { u: "foo" } + { i: "baz", meta: @meta } + { d: "bar", meta: @meta } + ]) + + describe "when the only update in the existing diff is a delete", -> + it "should insert the new update after the delete", -> + diff = @DiffGenerator.applyUpdateToDiff( + [ { d: "bar", meta: @meta } ], + { op: [{ p: 0, i: "baz" }], meta: @meta } + ) + expect(diff).to.deep.equal([ + { d: "bar", meta: @meta } + { i: "baz", meta: @meta } + ]) + From 81811d7cc513528928180305d1a4466b717f5aa9 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 18 Mar 2014 11:41:48 +0000 Subject: [PATCH 062/549] Move update summarizing logic into UpdatesManager --- .../app/coffee/HttpController.coffee | 36 +-------- .../app/coffee/UpdatesManager.coffee | 39 ++++++++++ .../HttpController/HttpControllerTests.coffee | 68 +--------------- .../UpdatesManager/UpdatesManagerTests.coffee | 78 +++++++++++++++++++ 4 files changed, 123 insertions(+), 98 deletions(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 215d440129..204ba68e48 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -38,42 +38,10 @@ module.exports = HttpController = if req.query.limit? limit = parseInt(req.query.limit, 10) - UpdatesManager.getUpdatesWithUserInfo doc_id, to: to, limit: limit, (error, updates) -> + UpdatesManager.getSummarizedUpdates doc_id, to: to, limit: limit, (error, updates) -> return next(error) if error? - res.send JSON.stringify updates: HttpController._buildUpdatesView(updates) + res.send JSON.stringify updates: updates - TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 - _buildUpdatesView: (updates) -> - view = [] - for update in updates.slice().reverse() - lastUpdate = view[view.length - 1] - if lastUpdate and update.meta.start_ts - lastUpdate.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES - if update.meta.user? - userExists = false - for user in lastUpdate.meta.users - if user.id == update.meta.user.id - userExists = true - break - if !userExists - lastUpdate.meta.users.push update.meta.user - lastUpdate.meta.start_ts = Math.min(lastUpdate.meta.start_ts, update.meta.start_ts) - lastUpdate.meta.end_ts = Math.max(lastUpdate.meta.end_ts, update.meta.end_ts) - lastUpdate.toV = update.v - else - newUpdate = - meta: - users: [] - start_ts: update.meta.start_ts - end_ts: update.meta.end_ts - fromV: update.v - toV: update.v - - if update.meta.user? - newUpdate.meta.users.push update.meta.user - - view.push newUpdate - - return view.reverse() restore: (req, res, next = (error) ->) -> {doc_id, project_id, version} = req.params diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 7261b3f341..bb8c9069b2 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -76,6 +76,11 @@ module.exports = UpdatesManager = return callback(error) if error? callback null, updates + getSummarizedUpdates: (doc_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.getUpdatesWithUserInfo doc_id, options, (error, updates) -> + return callback(error) if error? + callback null, UpdatesManager._summarizeUpdates(updates) + fillUserInfo: (updates, callback = (error, updates) ->) -> users = {} for update in updates @@ -105,3 +110,37 @@ module.exports = UpdatesManager = return false else return !!user_id.match(/^[a-f0-9]{24}$/) + + + TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 + _summarizeUpdates: (updates) -> + view = [] + for update in updates.slice().reverse() + lastUpdate = view[view.length - 1] + if lastUpdate and update.meta.start_ts - lastUpdate.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + if update.meta.user? + userExists = false + for user in lastUpdate.meta.users + if user.id == update.meta.user.id + userExists = true + break + if !userExists + lastUpdate.meta.users.push update.meta.user + lastUpdate.meta.start_ts = Math.min(lastUpdate.meta.start_ts, update.meta.start_ts) + lastUpdate.meta.end_ts = Math.max(lastUpdate.meta.end_ts, update.meta.end_ts) + lastUpdate.toV = update.v + else + newUpdate = + meta: + users: [] + start_ts: update.meta.start_ts + end_ts: update.meta.end_ts + fromV: update.v + toV: update.v + + if update.meta.user? + newUpdate.meta.users.push update.meta.user + + view.push newUpdate + + return view.reverse() diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index f6ec9bdee8..e628736f84 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -74,77 +74,17 @@ describe "HttpController", -> limit: @limit.toString() @res = send: sinon.stub() - @rawUpdates = ["raw-updates"] - @updatesView = ["updates-view"] - @HttpController._buildUpdatesView = sinon.stub().returns(@updatesView) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @rawUpdates) + @updates = ["mock-summarized-updates"] + @UpdatesManager.getSummarizedUpdates = sinon.stub().callsArgWith(2, null, @updates) @HttpController.getUpdates @req, @res, @next it "should get the updates", -> - @UpdatesManager.getUpdatesWithUserInfo + @UpdatesManager.getSummarizedUpdates .calledWith(@doc_id, to: @to, limit: @limit) .should.equal true - it "should build the updates view", -> - @HttpController._buildUpdatesView - .calledWith(@rawUpdates) - .should.equal true - it "should return the formatted updates", -> - @res.send.calledWith(JSON.stringify(updates: @updatesView)).should.equal true - - describe "_buildUpdatesView", -> - it "should concat updates that are close in time", -> - expect(@HttpController._buildUpdatesView [{ - meta: - user: @user_2 = { id: "mock-user-2" } - start_ts: @now + 20 - end_ts: @now + 30 - v: 5 - }, { - meta: - user: @user_1 = { id: "mock-user-1" } - start_ts: @now - end_ts: @now + 10 - v: 4 - }]).to.deep.equal [{ - meta: - users: [@user_1, @user_2] - start_ts: @now - end_ts: @now + 30 - fromV: 4 - toV: 5 - }] - - it "should leave updates that are far apart in time", -> - oneDay = 1000 * 60 * 60 * 24 - expect(@HttpController._buildUpdatesView [{ - meta: - user: @user_2 = { id: "mock-user-2" } - start_ts: @now + oneDay - end_ts: @now + oneDay + 10 - v: 5 - }, { - meta: - user: @user_1 = { id: "mock-user-2" } - start_ts: @now - end_ts: @now + 10 - v: 4 - }]).to.deep.equal [{ - meta: - users: [@user_2] - start_ts: @now + oneDay - end_ts: @now + oneDay + 10 - fromV: 5 - toV: 5 - }, { - meta: - users: [@user_1] - start_ts: @now - end_ts: @now + 10 - fromV: 4 - toV: 4 - }] + @res.send.calledWith(JSON.stringify(updates: @updates)).should.equal true describe "RestoreManager", -> beforeEach -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 67fc7b0d14..0a37cb9934 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -242,6 +242,29 @@ describe "UpdatesManager", -> it "shoudl return the updates with the filled details", -> @callback.calledWith(null, @updatesWithUserInfo).should.equal true + describe "getSummarizedUpdates", -> + beforeEach -> + @to = 42 + @limit = 10 + @updates = ["mock-updates"] + @summarizedUpdates = ["summarized-updates"] + @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + + it "should get the updates", -> + @UpdatesManager.getUpdatesWithUserInfo + .calledWith(@doc_id, { to: @to, limit: @limit }) + .should.equal true + + it "should summarize the updates", -> + @UpdatesManager._summarizeUpdates + .calledWith(@updates) + .should.equal true + + it "should call the callback with the summarized updates", -> + @callback.calledWith(null, @summarizedUpdates).should.equal true + describe "fillUserInfo", -> describe "with valid users", -> beforeEach (done) -> @@ -330,5 +353,60 @@ describe "UpdatesManager", -> op: "mock-op-2" }] + describe "_buildUpdatesView", -> + beforeEach -> + @now = Date.now() + + it "should concat updates that are close in time", -> + expect(@UpdatesManager._summarizeUpdates [{ + meta: + user: @user_2 = { id: "mock-user-2" } + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + meta: + user: @user_1 = { id: "mock-user-1" } + start_ts: @now + end_ts: @now + 10 + v: 4 + }]).to.deep.equal [{ + meta: + users: [@user_1, @user_2] + start_ts: @now + end_ts: @now + 30 + fromV: 4 + toV: 5 + }] + + it "should leave updates that are far apart in time", -> + oneDay = 1000 * 60 * 60 * 24 + expect(@UpdatesManager._summarizeUpdates [{ + meta: + user: @user_2 = { id: "mock-user-2" } + start_ts: @now + oneDay + end_ts: @now + oneDay + 10 + v: 5 + }, { + meta: + user: @user_1 = { id: "mock-user-2" } + start_ts: @now + end_ts: @now + 10 + v: 4 + }]).to.deep.equal [{ + meta: + users: [@user_2] + start_ts: @now + oneDay + end_ts: @now + oneDay + 10 + fromV: 5 + toV: 5 + }, { + meta: + users: [@user_1] + start_ts: @now + end_ts: @now + 10 + fromV: 4 + toV: 4 + }] From d32b0ee12f719d502053973208074491a51d54ce Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 18 Mar 2014 18:09:25 +0000 Subject: [PATCH 063/549] Fetch updates up to the limit, even after summarising --- .../app/coffee/HttpController.coffee | 1 - .../app/coffee/UpdatesManager.coffee | 61 ++++++-- .../coffee/GettingUpdatesTests.coffee | 136 ++++++++-------- .../UpdatesManager/UpdatesManagerTests.coffee | 147 +++++++++++++++--- 4 files changed, 241 insertions(+), 104 deletions(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 204ba68e48..3dbdeec947 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -42,7 +42,6 @@ module.exports = HttpController = return next(error) if error? res.send JSON.stringify updates: updates - restore: (req, res, next = (error) ->) -> {doc_id, project_id, version} = req.params user_id = req.headers["x-user-id"] diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index bb8c9069b2..08b590ea13 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -77,9 +77,38 @@ module.exports = UpdatesManager = callback null, updates getSummarizedUpdates: (doc_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.getUpdatesWithUserInfo doc_id, options, (error, updates) -> + options.limit ||= 25 + summarizedUpdates = [] + to = options.to + do fetchNextBatch = () -> + UpdatesManager._extendBatchOfSummarizedUpdates doc_id, summarizedUpdates, to, options.limit, (error, updates, endOfDatabase) -> + return callback(error) if error? + if endOfDatabase or updates.length >= options.limit + callback null, updates + else + to = updates[updates.length - 1].fromV - 1 + summarizedUpdates = updates + fetchNextBatch() + + _extendBatchOfSummarizedUpdates: ( + doc_id, + existingSummarizedUpdates, + to, desiredLength, + callback = (error, summarizedUpdates, endOfDatabase) -> + ) -> + UpdatesManager.getUpdatesWithUserInfo doc_id, { to: to, limit: 3 * desiredLength }, (error, updates) -> return callback(error) if error? - callback null, UpdatesManager._summarizeUpdates(updates) + if !updates? or updates.length == 0 + endOfDatabase = true + else + endOfDatabase = false + summarizedUpdates = UpdatesManager._summarizeUpdates( + updates, existingSummarizedUpdates + ) + console.log "Summarized", summarizedUpdates + callback null, + summarizedUpdates.slice(0, desiredLength), + endOfDatabase fillUserInfo: (updates, callback = (error, updates) ->) -> users = {} @@ -113,23 +142,26 @@ module.exports = UpdatesManager = TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 - _summarizeUpdates: (updates) -> - view = [] - for update in updates.slice().reverse() - lastUpdate = view[view.length - 1] - if lastUpdate and update.meta.start_ts - lastUpdate.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + _summarizeUpdates: (updates, existingSummarizedUpdates = []) -> + summarizedUpdates = existingSummarizedUpdates.slice() + for update in updates + earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] + console.log "Considering update", update, earliestUpdate + if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + console.log "Concatting" if update.meta.user? userExists = false - for user in lastUpdate.meta.users + for user in earliestUpdate.meta.users if user.id == update.meta.user.id userExists = true break if !userExists - lastUpdate.meta.users.push update.meta.user - lastUpdate.meta.start_ts = Math.min(lastUpdate.meta.start_ts, update.meta.start_ts) - lastUpdate.meta.end_ts = Math.max(lastUpdate.meta.end_ts, update.meta.end_ts) - lastUpdate.toV = update.v + earliestUpdate.meta.users.push update.meta.user + earliestUpdate.meta.start_ts = Math.min(earliestUpdate.meta.start_ts, update.meta.start_ts) + earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts) + earliestUpdate.fromV = update.v else + console.log "creating new" newUpdate = meta: users: [] @@ -141,6 +173,7 @@ module.exports = UpdatesManager = if update.meta.user? newUpdate.meta.users.push update.meta.user - view.push newUpdate + summarizedUpdates.push newUpdate + + return summarizedUpdates - return view.reverse() diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 99a103bb74..4f2406c62e 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -11,12 +11,15 @@ TrackChangesClient = require "./helpers/TrackChangesClient" MockWebApi = require "./helpers/MockWebApi" describe "Getting updates", -> - before -> + before (done) -> @now = Date.now() @to = @now @user_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() @minutes = 60 * 1000 + @days = 24 * 60 * @minutes MockWebApi.users[@user_id] = @user = email: "user@sharelatex.com" @@ -25,87 +28,84 @@ describe "Getting updates", -> id: @user_id sinon.spy MockWebApi, "getUser" + @updates = [] + for i in [0..9] + @updates.push { + op: [{ i: "a", p: 0 }] + meta: { ts: @now - (9 - i) * @days - 2 * @minutes, user_id: @user_id } + v: 2 * i + 1 + } + @updates.push { + op: [{ i: "b", p: 0 }] + meta: { ts: @now - (9 - i) * @days, user_id: @user_id } + v: 2 * i + 2 + } + + TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + throw error if error? + done() + after: () -> MockWebApi.getUser.restore() - describe "getting updates in the middle", -> + describe "getting updates up to the limit", -> before (done) -> - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() - @updates = [{ - op: [{ i: "one ", p: 0 }] - meta: { ts: @to - 20 * @minutes, user_id: @user_id } - v: 3 - }, { - op: [{ i: "two ", p: 4 }] - meta: { ts: @to - 10 * @minutes } - v: 4 - }, { - op: [{ i: "three ", p: 8 }] - meta: { ts: @to, user_id: @user_id } - v: @toVersion = 5 - }, { - op: [{ i: "four", p: 14 }] - meta: { ts: @to + 2 * @minutes, user_id: @user_id } - v: 6 - }] - - TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + TrackChangesClient.getUpdates @project_id, @doc_id, { to: 20, limit: 3 }, (error, body) => throw error if error? - TrackChangesClient.getUpdates @project_id, @doc_id, { to: @toVersion, limit: 2 }, (error, body) => - throw error if error? - @updates = body.updates - done() + @updates = body.updates + done() it "should fetch the user details from the web api", -> MockWebApi.getUser .calledWith(@user_id) .should.equal true - it "should return the updates", -> - expect(@updates).to.deep.equal [{ - meta: - start_ts: @to - end_ts: @to - users: [@user] - fromV: 5 - toV: 5 - }, { - meta: - users: [] - start_ts: @to - 10 * @minutes - end_ts: @to - 10 * @minutes - toV: 4 - fromV: 4 - }] - - describe "getting updates that should be combined", -> - before (done) -> - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() - @updates = [{ - op: [{ i: "two ", p: 4 }] - meta: { ts: @to - 2 * @minutes, user_id: @user_id } - v: 4 - }, { - op: [{ i: "three ", p: 8 }] - meta: { ts: @to, user_id: @user_id } - v: @toVersion = 5 - }] - - TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.getUpdates @project_id, @doc_id, { to: @toVersion, limit: 2 }, (error, body) => - throw error if error? - @updates = body.updates - done() - - it "should return the updates", -> + it "should return the same number of summarized updates as the limit", -> expect(@updates).to.deep.equal [{ meta: start_ts: @to - 2 * @minutes end_ts: @to users: [@user] - fromV: 4 - toV: 5 + toV: 20 + fromV: 19 + }, { + meta: + start_ts: @to - 1 * @days - 2 * @minutes + end_ts: @to - 1 * @days + users: [@user] + toV: 18 + fromV: 17 + }, { + meta: + start_ts: @to - 2 * @days - 2 * @minutes + end_ts: @to - 2 * @days + users: [@user] + toV: 16 + fromV: 15 }] + + + describe "getting updates beyond the end of the database", -> + before (done) -> + TrackChangesClient.getUpdates @project_id, @doc_id, { to: 4, limit: 30 }, (error, body) => + throw error if error? + @updates = body.updates + done() + + it "should return as many updates as it can", -> + expect(@updates).to.deep.equal [{ + meta: + start_ts: @to - 8 * @days - 2 * @minutes + end_ts: @to - 8 * @days + users: [@user] + toV: 4 + fromV: 3 + }, { + meta: + start_ts: @to - 9 * @days - 2 * @minutes + end_ts: @to - 9 * @days + users: [@user] + toV: 2 + fromV: 1 + }] + diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 0a37cb9934..bc9b2c3ac5 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -239,31 +239,107 @@ describe "UpdatesManager", -> .calledWith(@updates) .should.equal true - it "shoudl return the updates with the filled details", -> + it "should return the updates with the filled details", -> @callback.calledWith(null, @updatesWithUserInfo).should.equal true - describe "getSummarizedUpdates", -> + describe "_extendBatchOfSummarizedUpdates", -> beforeEach -> @to = 42 - @limit = 10 - @updates = ["mock-updates"] - @summarizedUpdates = ["summarized-updates"] - @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + @limit = 2 + @existingSummarizedUpdates = ["summarized-updates-3"] + @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - it "should get the updates", -> - @UpdatesManager.getUpdatesWithUserInfo - .calledWith(@doc_id, { to: @to, limit: @limit }) - .should.equal true + describe "when there are updates to get", -> + beforeEach -> + @updates = ["mock-updates"] + @existingSummarizedUpdates = ["summarized-updates-3"] + @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager._extendBatchOfSummarizedUpdates @doc_id, @existingSummarizedUpdates, @to, @limit, @callback - it "should summarize the updates", -> - @UpdatesManager._summarizeUpdates - .calledWith(@updates) - .should.equal true + it "should get the updates", -> + @UpdatesManager.getUpdatesWithUserInfo + .calledWith(@doc_id, { to: @to, limit: 3 * @limit }) + .should.equal true - it "should call the callback with the summarized updates", -> - @callback.calledWith(null, @summarizedUpdates).should.equal true + it "should summarize the updates", -> + @UpdatesManager._summarizeUpdates + .calledWith(@updates, @existingSummarizedUpdates) + .should.equal true + + it "should call the callback with the summarized updates and false for end-of-databse", -> + @callback.calledWith(null, @summarizedUpdates.slice(0, @limit), false).should.equal true + + describe "when there are no more updates", -> + beforeEach -> + @updates = [] + @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager._extendBatchOfSummarizedUpdates @doc_id, @existingSummarizedUpdates, @to, @limit, @callback + + it "should call the callback with the summarized updates and true for end-of-database", -> + @callback.calledWith(null, @summarizedUpdates.slice(0, @limit), true).should.equal true + + describe "getSummarizedUpdates", -> + describe "when one batch of updates is enough to meet the limit", -> + beforeEach -> + @to = 42 + @limit = 2 + @updates = ["summarized-updates-3", "summarized-updates-2"] + @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates) + @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + + it "should get the batch of summarized updates", -> + @UpdatesManager._extendBatchOfSummarizedUpdates + .calledWith(@doc_id, [], @to, @limit) + .should.equal true + + it "should call the callback with the updates", -> + @callback.calledWith(null, @updates).should.equal true + + describe "when multiple batches are needed to meet the limit", -> + beforeEach -> + @to = 6 + @limit = 4 + @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] + @UpdatesManager._extendBatchOfSummarizedUpdates = (doc_id, existingUpdates, to, limit, callback) => + if existingUpdates.length == 0 + callback null, @firstBatch, false + else + callback null, @firstBatch.concat(@secondBatch), false + sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" + @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + + it "should get the first batch of summarized updates", -> + @UpdatesManager._extendBatchOfSummarizedUpdates + .calledWith(@doc_id, [], @to, @limit) + .should.equal true + + it "should get the second batch of summarized updates", -> + @UpdatesManager._extendBatchOfSummarizedUpdates + .calledWith(@doc_id, @firstBatch, 4, @limit) + .should.equal true + + it "should call the callback with all the updates", -> + @callback.calledWith(null, @firstBatch.concat(@secondBatch)).should.equal true + + describe "when the end of the database is hit", -> + beforeEach -> + @to = 6 + @limit = 4 + @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, true) + @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + + it "should get the batch of summarized updates", -> + @UpdatesManager._extendBatchOfSummarizedUpdates + .calledWith(@doc_id, [], @to, @limit) + .should.equal true + + it "should call the callback with the updates", -> + @callback.calledWith(null, @updates).should.equal true describe "fillUserInfo", -> describe "with valid users", -> @@ -360,13 +436,13 @@ describe "UpdatesManager", -> it "should concat updates that are close in time", -> expect(@UpdatesManager._summarizeUpdates [{ meta: - user: @user_2 = { id: "mock-user-2" } + user: @user_1 = { id: "mock-user-1" } start_ts: @now + 20 end_ts: @now + 30 v: 5 }, { meta: - user: @user_1 = { id: "mock-user-1" } + user: @user_2 = { id: "mock-user-2" } start_ts: @now end_ts: @now + 10 v: 4 @@ -409,4 +485,33 @@ describe "UpdatesManager", -> toV: 4 }] - + it "should concat onto existing summarized updates", -> + @user_1 = { id: "mock-user-1" } + @user_2 = { id: "mock-user-2" } + expect(@UpdatesManager._summarizeUpdates [{ + meta: + user: @user_1 + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + meta: + user: @user_2 + start_ts: @now + end_ts: @now + 10 + v: 4 + }], [{ + meta: + users: [@user_1] + start_ts: @now + 40 + end_ts: @now + 50 + fromV: 6 + toV: 8 + }]).to.deep.equal [{ + meta: + users: [@user_1, @user_2] + start_ts: @now + end_ts: @now + 50 + fromV: 4 + toV: 8 + }] From fec648666c30e881de9bbd0e55473e6d0e904bea Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 19 Mar 2014 16:14:17 +0000 Subject: [PATCH 064/549] Add in project_id to flushing HTTP endpoint --- services/track-changes/app.coffee | 2 +- .../coffee/AppendingUpdatesTests.coffee | 16 ++++++++++------ .../coffee/helpers/TrackChangesClient.coffee | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 7c47607aeb..4e62a63c28 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -10,7 +10,7 @@ app = express() app.use express.logger() -app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock +app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushUpdatesWithLock app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 827d238239..80d6815acc 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -13,6 +13,7 @@ TrackChangesClient = require "./helpers/TrackChangesClient" describe "Appending doc ops to the history", -> describe "when the history does not exist yet", -> before (done) -> + @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() TrackChangesClient.pushRawUpdates @doc_id, [{ @@ -29,7 +30,7 @@ describe "Appending doc ops to the history", -> v: 5 }], (error) => throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() @@ -43,6 +44,7 @@ describe "Appending doc ops to the history", -> describe "when the history has already been started", -> beforeEach (done) -> + @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() TrackChangesClient.pushRawUpdates @doc_id, [{ @@ -59,7 +61,7 @@ describe "Appending doc ops to the history", -> v: 5 }], (error) => throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, updates) => + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, updates) => throw error if error? done() @@ -79,7 +81,7 @@ describe "Appending doc ops to the history", -> v: 8 }], (error) => throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() @@ -109,7 +111,7 @@ describe "Appending doc ops to the history", -> v: 8 }], (error) => throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() @@ -123,6 +125,7 @@ describe "Appending doc ops to the history", -> describe "when the updates need processing in batches", -> before (done) -> + @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() updates = [] @@ -137,7 +140,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.pushRawUpdates @doc_id, updates, (error) => throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() @@ -150,6 +153,7 @@ describe "Appending doc ops to the history", -> describe "when there are multiple ops in each update", -> before (done) -> + @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() oneDay = 24 * 60 * 60 * 1000 @@ -163,7 +167,7 @@ describe "Appending doc ops to the history", -> v: 4 }], (error) => throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) => + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 6c65941570..71efab30b8 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -3,9 +3,9 @@ rclient = require("redis").createClient() # Only works locally for now {db, ObjectId} = require "../../../../app/js/mongojs" module.exports = TrackChangesClient = - flushAndGetCompressedUpdates: (doc_id, callback = (error, updates) ->) -> + flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> request.post { - url: "http://localhost:3015/doc/#{doc_id}/flush" + url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/flush" }, (error, response, body) => response.statusCode.should.equal 204 db.docHistory From 962fc183290131cc47dd6b38926e421e8697daab Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 19 Mar 2014 16:40:55 +0000 Subject: [PATCH 065/549] Store project_id alongside each change --- .../app/coffee/DiffManager.coffee | 2 +- .../app/coffee/HttpController.coffee | 5 +- .../app/coffee/MongoManager.coffee | 7 +- .../app/coffee/UpdatesManager.coffee | 45 +++++------ .../coffee/AppendingUpdatesTests.coffee | 6 ++ .../DiffManager/DiffManagerTests.coffee | 4 +- .../HttpController/HttpControllerTests.coffee | 9 ++- .../MongoManager/MongoManagerTests.coffee | 10 ++- .../UpdatesManager/UpdatesManagerTests.coffee | 81 ++++++++++--------- 9 files changed, 89 insertions(+), 80 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index c791fe1e09..43eddad85d 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -5,7 +5,7 @@ logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> - UpdatesManager.getUpdatesWithUserInfo doc_id, from: fromVersion, to: toVersion, (error, updates) -> + UpdatesManager.getUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 3dbdeec947..703c1254f1 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -6,8 +6,9 @@ logger = require "logger-sharelatex" module.exports = HttpController = flushUpdatesWithLock: (req, res, next = (error) ->) -> doc_id = req.params.doc_id + project_id = req.params.project_id logger.log doc_id: doc_id, "compressing doc history" - UpdatesManager.processUncompressedUpdatesWithLock doc_id, (error) -> + UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return next(error) if error? res.send 204 @@ -38,7 +39,7 @@ module.exports = HttpController = if req.query.limit? limit = parseInt(req.query.limit, 10) - UpdatesManager.getSummarizedUpdates doc_id, to: to, limit: limit, (error, updates) -> + UpdatesManager.getSummarizedUpdates project_id, doc_id, to: to, limit: limit, (error, updates) -> return next(error) if error? res.send JSON.stringify updates: updates diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 9285b59c3a..a733d3946e 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -24,16 +24,17 @@ module.exports = MongoManager = else callback null, null - insertCompressedUpdates: (doc_id, updates, callback = (error) ->) -> + insertCompressedUpdates: (project_id, doc_id, updates, callback = (error) ->) -> jobs = [] for update in updates do (update) -> - jobs.push (callback) -> MongoManager.insertCompressedUpdate doc_id, update, callback + jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, callback async.series jobs, callback - insertCompressedUpdate: (doc_id, update, callback = (error) ->) -> + insertCompressedUpdate: (project_id, doc_id, update, callback = (error) ->) -> db.docHistory.insert { doc_id: ObjectId(doc_id.toString()) + project_id: ObjectId(project_id.toString()) op: update.op meta: update.meta v: update.v diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 08b590ea13..0613a50800 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -7,7 +7,7 @@ logger = require "logger-sharelatex" async = require "async" module.exports = UpdatesManager = - compressAndSaveRawUpdates: (doc_id, rawUpdates, callback = (error) ->) -> + compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, callback = (error) ->) -> length = rawUpdates.length if length == 0 return callback() @@ -23,65 +23,65 @@ module.exports = UpdatesManager = if rawUpdates[0]? and rawUpdates[0].v != lastCompressedUpdate.v + 1 error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") - logger.error err: error, doc_id: doc_id, "inconsistent doc versions" + logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" # Push the update back into Mongo - catching errors at this # point is useless, we're already bailing - MongoManager.insertCompressedUpdates doc_id, [lastCompressedUpdate], () -> + MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], () -> return callback error return compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - MongoManager.insertCompressedUpdates doc_id, compressedUpdates, (error) -> + MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, (error) -> return callback(error) if error? - logger.log doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" + logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" callback() REDIS_READ_BATCH_SIZE: 100 - processUncompressedUpdates: (doc_id, callback = (error) ->) -> + processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? length = rawUpdates.length - UpdatesManager.compressAndSaveRawUpdates doc_id, rawUpdates, (error) -> + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, (error) -> return callback(error) if error? - logger.log doc_id: doc_id, "compressed and saved doc updates" + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" RedisManager.deleteOldestRawUpdates doc_id, length, (error) -> return callback(error) if error? if length == UpdatesManager.REDIS_READ_BATCH_SIZE # There might be more updates - logger.log doc_id: doc_id, "continuing processing updates" + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" setTimeout () -> - UpdatesManager.processUncompressedUpdates doc_id, callback + UpdatesManager.processUncompressedUpdates project_id, doc_id, callback , 0 else - logger.log doc_id: doc_id, "all raw updates processed" + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" callback() - processUncompressedUpdatesWithLock: (doc_id, callback = (error) ->) -> + processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> LockManager.runWithLock( "HistoryLock:#{doc_id}", (releaseLock) -> - UpdatesManager.processUncompressedUpdates doc_id, releaseLock + UpdatesManager.processUncompressedUpdates project_id, doc_id, releaseLock callback ) - getUpdates: (doc_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.processUncompressedUpdatesWithLock doc_id, (error) -> + getUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? MongoManager.getUpdates doc_id, options, callback - getUpdatesWithUserInfo: (doc_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.getUpdates doc_id, options, (error, updates) -> + getUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.getUpdates project_id, doc_id, options, (error, updates) -> return callback(error) if error? UpdatesManager.fillUserInfo updates, (error, updates) -> return callback(error) if error? callback null, updates - getSummarizedUpdates: (doc_id, options = {}, callback = (error, updates) ->) -> + getSummarizedUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> options.limit ||= 25 summarizedUpdates = [] to = options.to do fetchNextBatch = () -> - UpdatesManager._extendBatchOfSummarizedUpdates doc_id, summarizedUpdates, to, options.limit, (error, updates, endOfDatabase) -> + UpdatesManager._extendBatchOfSummarizedUpdates project_id, doc_id, summarizedUpdates, to, options.limit, (error, updates, endOfDatabase) -> return callback(error) if error? if endOfDatabase or updates.length >= options.limit callback null, updates @@ -91,12 +91,13 @@ module.exports = UpdatesManager = fetchNextBatch() _extendBatchOfSummarizedUpdates: ( + project_id, doc_id, existingSummarizedUpdates, to, desiredLength, callback = (error, summarizedUpdates, endOfDatabase) -> ) -> - UpdatesManager.getUpdatesWithUserInfo doc_id, { to: to, limit: 3 * desiredLength }, (error, updates) -> + UpdatesManager.getUpdatesWithUserInfo project_id, doc_id, { to: to, limit: 3 * desiredLength }, (error, updates) -> return callback(error) if error? if !updates? or updates.length == 0 endOfDatabase = true @@ -105,7 +106,6 @@ module.exports = UpdatesManager = summarizedUpdates = UpdatesManager._summarizeUpdates( updates, existingSummarizedUpdates ) - console.log "Summarized", summarizedUpdates callback null, summarizedUpdates.slice(0, desiredLength), endOfDatabase @@ -146,9 +146,7 @@ module.exports = UpdatesManager = summarizedUpdates = existingSummarizedUpdates.slice() for update in updates earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] - console.log "Considering update", update, earliestUpdate if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES - console.log "Concatting" if update.meta.user? userExists = false for user in earliestUpdate.meta.users @@ -161,7 +159,6 @@ module.exports = UpdatesManager = earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts) earliestUpdate.fromV = update.v else - console.log "creating new" newUpdate = meta: users: [] diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 80d6815acc..1fa4166f60 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -42,6 +42,12 @@ describe "Appending doc ops to the history", -> it "should insert the correct version number into mongo", -> expect(@updates[0].v).to.equal 5 + it "should store the doc id", -> + expect(@updates[0].doc_id.toString()).to.equal @doc_id + + it "should store the project id", -> + expect(@updates[0].project_id.toString()).to.equal @project_id + describe "when the history has already been started", -> beforeEach (done) -> @project_id = ObjectId().toString() diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 57c367db2a..c58d40068a 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -25,7 +25,7 @@ describe "DiffManager", -> @updates = [ "mock-update-1", "mock-update-2" ] @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @content, @version) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback it "should get the latest version of the doc", -> @@ -35,7 +35,7 @@ describe "DiffManager", -> it "should get the latest updates", -> @UpdatesManager.getUpdatesWithUserInfo - .calledWith(@doc_id, from: @from, to: @to) + .calledWith(@project_id, @doc_id, from: @from, to: @to) .should.equal true it "should call the callback with the content, version and updates", -> diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index e628736f84..55742d5115 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -23,14 +23,15 @@ describe "HttpController", -> @req = params: doc_id: @doc_id + project_id: @project_id @res = send: sinon.stub() - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) @HttpController.flushUpdatesWithLock @req, @res, @next it "should process the updates", -> @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@doc_id) + .calledWith(@project_id, @doc_id) .should.equal true it "should return a success code", -> @@ -75,12 +76,12 @@ describe "HttpController", -> @res = send: sinon.stub() @updates = ["mock-summarized-updates"] - @UpdatesManager.getSummarizedUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getSummarizedUpdates = sinon.stub().callsArgWith(3, null, @updates) @HttpController.getUpdates @req, @res, @next it "should get the updates", -> @UpdatesManager.getSummarizedUpdates - .calledWith(@doc_id, to: @to, limit: @limit) + .calledWith(@project_id, @doc_id, to: @to, limit: @limit) .should.equal true it "should return the formatted updates", -> diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index cd8caec27b..6c24f42e37 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -12,6 +12,7 @@ describe "MongoManager", -> "./mongojs" : { db: @db = {}, ObjectId: ObjectId } @callback = sinon.stub() @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() describe "getLastCompressedUpdate", -> beforeEach -> @@ -100,11 +101,12 @@ describe "MongoManager", -> @update = { op: "op", meta: "meta", v: "v"} @db.docHistory = insert: sinon.stub().callsArg(1) - @MongoManager.insertCompressedUpdate @doc_id, @update, @callback + @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, @callback it "should insert the update", -> @db.docHistory.insert .calledWith({ + project_id: ObjectId(@project_id), doc_id: ObjectId(@doc_id), op: @update.op, meta: @update.meta, @@ -118,15 +120,15 @@ describe "MongoManager", -> describe "insertCompressedUpdates", -> beforeEach (done) -> @updates = [ "mock-update-1", "mock-update-2" ] - @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(2) - @MongoManager.insertCompressedUpdates @doc_id, @updates, (args...) => + @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(3) + @MongoManager.insertCompressedUpdates @project_id, @doc_id, @updates, (args...) => @callback(args...) done() it "should insert each update", -> for update in @updates @MongoManager.insertCompressedUpdate - .calledWith(@doc_id, update) + .calledWith(@project_id, @doc_id, update) .should.equal true it "should call the callback", -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index bc9b2c3ac5..60611d8907 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -15,6 +15,7 @@ describe "UpdatesManager", -> "./WebApiManager": @WebApiManager = {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } @doc_id = "doc-id-123" + @project_id = "project-id-123" @callback = sinon.stub() describe "compressAndSaveRawUpdates", -> @@ -22,7 +23,7 @@ describe "UpdatesManager", -> beforeEach -> @MongoManager.popLastCompressedUpdate = sinon.stub() @MongoManager.insertCompressedUpdates = sinon.stub() - @UpdatesManager.compressAndSaveRawUpdates @doc_id, [], @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, [], @callback it "should not need to access the database", -> @MongoManager.popLastCompressedUpdate.called.should.equal false @@ -37,9 +38,9 @@ describe "UpdatesManager", -> @compressedUpdates = { v: 13, op: "compressed-op-12" } @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(3) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> @MongoManager.popLastCompressedUpdate @@ -53,7 +54,7 @@ describe "UpdatesManager", -> it "should save the compressed ops", -> @MongoManager.insertCompressedUpdates - .calledWith(@doc_id, @compressedUpdates) + .calledWith(@project_id, @doc_id, @compressedUpdates) .should.equal true it "should call the callback", -> @@ -65,13 +66,13 @@ describe "UpdatesManager", -> @compressedUpdates = { v: 13, op: "compressed-op-12" } @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(2) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(3) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) describe "when the raw ops start where the existing history ends", -> beforeEach -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback it "should try to pop the last compressed op", -> @MongoManager.popLastCompressedUpdate @@ -85,7 +86,7 @@ describe "UpdatesManager", -> it "should save the compressed ops", -> @MongoManager.insertCompressedUpdates - .calledWith(@doc_id, @compressedUpdates) + .calledWith(@project_id, @doc_id, @compressedUpdates) .should.equal true it "should call the callback", -> @@ -95,7 +96,7 @@ describe "UpdatesManager", -> beforeEach -> @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback it "should only compress the more recent raw ops", -> @UpdateCompressor.compressRawUpdates @@ -105,7 +106,7 @@ describe "UpdatesManager", -> describe "when the raw ops do not follow from the last compressed op version", -> beforeEach -> @rawUpdates = [{ v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback it "should call the callback with an error", -> @callback @@ -115,7 +116,7 @@ describe "UpdatesManager", -> it "should put the popped update back into mongo", -> @MongoManager.insertCompressedUpdates.calledOnce.should.equal true @MongoManager.insertCompressedUpdates - .calledWith(@doc_id, [@lastCompressedUpdate]) + .calledWith(@project_id, @doc_id, [@lastCompressedUpdate]) .should.equal true describe "processUncompressedUpdates", -> @@ -123,9 +124,9 @@ describe "UpdatesManager", -> beforeEach -> @updates = ["mock-update"] @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) - @UpdatesManager.processUncompressedUpdates @doc_id, @callback + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback it "should get the oldest updates", -> @RedisManager.getOldestRawUpdates @@ -134,7 +135,7 @@ describe "UpdatesManager", -> it "should compress and save the updates", -> @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@doc_id, @updates) + .calledWith(@project_id, @doc_id, @updates) .should.equal true it "should delete the batch of uncompressed updates that was just processed", -> @@ -155,9 +156,9 @@ describe "UpdatesManager", -> @redisArray = @redisArray.slice(batchSize) callback null, updates sinon.spy @RedisManager, "getOldestRawUpdates" - @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(2) + @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) - @UpdatesManager.processUncompressedUpdates @doc_id, (args...) => + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => @callback(args...) done() @@ -166,13 +167,13 @@ describe "UpdatesManager", -> it "should compress and save the updates in batches", -> @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@doc_id, @updates.slice(0,2)) + .calledWith(@project_id, @doc_id, @updates.slice(0,2)) .should.equal true @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@doc_id, @updates.slice(2,4)) + .calledWith(@project_id, @doc_id, @updates.slice(2,4)) .should.equal true @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@doc_id, @updates.slice(4,5)) + .calledWith(@project_id, @doc_id, @updates.slice(4,5)) .should.equal true it "should delete the batches of uncompressed updates", -> @@ -185,7 +186,7 @@ describe "UpdatesManager", -> beforeEach -> @UpdatesManager.processUncompressedUpdates = sinon.stub().callsArg(2) @LockManager.runWithLock = sinon.stub().callsArg(2) - @UpdatesManager.processUncompressedUpdatesWithLock @doc_id, @callback + @UpdatesManager.processUncompressedUpdatesWithLock @project_id, @doc_id, @callback it "should run processUncompressedUpdates with the lock", -> @LockManager.runWithLock @@ -202,12 +203,12 @@ describe "UpdatesManager", -> @updates = ["mock-updates"] @options = { to: "mock-to", limit: "mock-limit" } @MongoManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1) - @UpdatesManager.getUpdates @doc_id, @options, @callback + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + @UpdatesManager.getUpdates @project_id, @doc_id, @options, @callback it "should process outstanding updates", -> @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@doc_id) + .calledWith(@project_id, @doc_id) .should.equal true it "should get the updates from the database", -> @@ -225,13 +226,13 @@ describe "UpdatesManager", -> @updates = ["mock-updates"] @options = { to: "mock-to", limit: "mock-limit" } @updatesWithUserInfo = ["updates-with-user-info"] - @UpdatesManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getUpdates = sinon.stub().callsArgWith(3, null, @updates) @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - @UpdatesManager.getUpdatesWithUserInfo @doc_id, @options, @callback + @UpdatesManager.getUpdatesWithUserInfo @project_id, @doc_id, @options, @callback it "should get the updates", -> @UpdatesManager.getUpdates - .calledWith(@doc_id, @options) + .calledWith(@project_id, @doc_id, @options) .should.equal true it "should file the updates with the user info", -> @@ -255,12 +256,12 @@ describe "UpdatesManager", -> @existingSummarizedUpdates = ["summarized-updates-3"] @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager._extendBatchOfSummarizedUpdates @doc_id, @existingSummarizedUpdates, @to, @limit, @callback + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @doc_id, @existingSummarizedUpdates, @to, @limit, @callback it "should get the updates", -> @UpdatesManager.getUpdatesWithUserInfo - .calledWith(@doc_id, { to: @to, limit: 3 * @limit }) + .calledWith(@project_id, @doc_id, { to: @to, limit: 3 * @limit }) .should.equal true it "should summarize the updates", -> @@ -275,8 +276,8 @@ describe "UpdatesManager", -> beforeEach -> @updates = [] @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager._extendBatchOfSummarizedUpdates @doc_id, @existingSummarizedUpdates, @to, @limit, @callback + @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @doc_id, @existingSummarizedUpdates, @to, @limit, @callback it "should call the callback with the summarized updates and true for end-of-database", -> @callback.calledWith(null, @summarizedUpdates.slice(0, @limit), true).should.equal true @@ -287,12 +288,12 @@ describe "UpdatesManager", -> @to = 42 @limit = 2 @updates = ["summarized-updates-3", "summarized-updates-2"] - @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates) - @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(5, null, @updates) + @UpdatesManager.getSummarizedUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback it "should get the batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@doc_id, [], @to, @limit) + .calledWith(@project_id, @doc_id, [], @to, @limit) .should.equal true it "should call the callback with the updates", -> @@ -304,22 +305,22 @@ describe "UpdatesManager", -> @limit = 4 @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] - @UpdatesManager._extendBatchOfSummarizedUpdates = (doc_id, existingUpdates, to, limit, callback) => + @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, doc_id, existingUpdates, to, limit, callback) => if existingUpdates.length == 0 callback null, @firstBatch, false else callback null, @firstBatch.concat(@secondBatch), false sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" - @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager.getSummarizedUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback it "should get the first batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@doc_id, [], @to, @limit) + .calledWith(@project_id, @doc_id, [], @to, @limit) .should.equal true it "should get the second batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@doc_id, @firstBatch, 4, @limit) + .calledWith(@project_id, @doc_id, @firstBatch, 4, @limit) .should.equal true it "should call the callback with all the updates", -> @@ -330,12 +331,12 @@ describe "UpdatesManager", -> @to = 6 @limit = 4 @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, true) - @UpdatesManager.getSummarizedUpdates @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(5, null, @updates, true) + @UpdatesManager.getSummarizedUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback it "should get the batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@doc_id, [], @to, @limit) + .calledWith(@project_id, @doc_id, [], @to, @limit) .should.equal true it "should call the callback with the updates", -> From b7de4c9c6c30543a77f0052cf7e8aab9ff1ffe9a Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 19 Mar 2014 17:44:16 +0000 Subject: [PATCH 066/549] Implement project update getter methods --- .../app/coffee/DiffManager.coffee | 2 +- .../app/coffee/HttpController.coffee | 2 +- .../app/coffee/MongoManager.coffee | 19 ++++- .../app/coffee/UpdatesManager.coffee | 22 ++++-- .../DiffManager/DiffManagerTests.coffee | 4 +- .../HttpController/HttpControllerTests.coffee | 4 +- .../MongoManager/MongoManagerTests.coffee | 65 +++++++++++++++-- .../UpdatesManager/UpdatesManagerTests.coffee | 69 +++++++++++++++---- 8 files changed, 155 insertions(+), 32 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 43eddad85d..9183c8ecaa 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -5,7 +5,7 @@ logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> - UpdatesManager.getUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> + UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 703c1254f1..66c2c294ad 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -39,7 +39,7 @@ module.exports = HttpController = if req.query.limit? limit = parseInt(req.query.limit, 10) - UpdatesManager.getSummarizedUpdates project_id, doc_id, to: to, limit: limit, (error, updates) -> + UpdatesManager.getSummarizedDocUpdates project_id, doc_id, to: to, limit: limit, (error, updates) -> return next(error) if error? res.send JSON.stringify updates: updates diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index a733d3946e..71f926a745 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -40,7 +40,7 @@ module.exports = MongoManager = v: update.v }, callback - getUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> + getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) if options.from? @@ -59,6 +59,23 @@ module.exports = MongoManager = cursor.toArray callback + getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> + query = + project_id: ObjectId(project_id.toString()) + + if options.before? + query["meta.end_ts"] = { $lt: options.before } + + cursor = db.docHistory + .find( query ) + .sort( "meta.end_ts": -1 ) + + if options.limit? + cursor.limit(options.limit) + + cursor.toArray callback + ensureIndices: (callback = (error) ->) -> db.docHistory.ensureIndex { doc_id: 1, v: 1 }, callback + db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, callback diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 0613a50800..9c63f47de8 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -64,19 +64,29 @@ module.exports = UpdatesManager = callback ) - getUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> + getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? - MongoManager.getUpdates doc_id, options, callback + MongoManager.getDocUpdates doc_id, options, callback - getUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.getUpdates project_id, doc_id, options, (error, updates) -> + getDocUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.getDocUpdates project_id, doc_id, options, (error, updates) -> return callback(error) if error? UpdatesManager.fillUserInfo updates, (error, updates) -> return callback(error) if error? callback null, updates - getSummarizedUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> + getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> + MongoManager.getProjectUpdates project_id, options, callback + + getProjectUpdatesWithUserInfo: (project_id, options = {}, callback = (error, updates) ->) -> + UpdatesManager.getProjectUpdates project_id, options, (error, updates) -> + return callback(error) if error? + UpdatesManager.fillUserInfo updates, (error, updates) -> + return callback(error) if error? + callback null, updates + + getSummarizedDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> options.limit ||= 25 summarizedUpdates = [] to = options.to @@ -97,7 +107,7 @@ module.exports = UpdatesManager = to, desiredLength, callback = (error, summarizedUpdates, endOfDatabase) -> ) -> - UpdatesManager.getUpdatesWithUserInfo project_id, doc_id, { to: to, limit: 3 * desiredLength }, (error, updates) -> + UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, { to: to, limit: 3 * desiredLength }, (error, updates) -> return callback(error) if error? if !updates? or updates.length == 0 endOfDatabase = true diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index c58d40068a..e3214a2f36 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -25,7 +25,7 @@ describe "DiffManager", -> @updates = [ "mock-update-1", "mock-update-2" ] @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @content, @version) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback it "should get the latest version of the doc", -> @@ -34,7 +34,7 @@ describe "DiffManager", -> .should.equal true it "should get the latest updates", -> - @UpdatesManager.getUpdatesWithUserInfo + @UpdatesManager.getDocUpdatesWithUserInfo .calledWith(@project_id, @doc_id, from: @from, to: @to) .should.equal true diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 55742d5115..4d15670653 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -76,11 +76,11 @@ describe "HttpController", -> @res = send: sinon.stub() @updates = ["mock-summarized-updates"] - @UpdatesManager.getSummarizedUpdates = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.getSummarizedDocUpdates = sinon.stub().callsArgWith(3, null, @updates) @HttpController.getUpdates @req, @res, @next it "should get the updates", -> - @UpdatesManager.getSummarizedUpdates + @UpdatesManager.getSummarizedDocUpdates .calledWith(@project_id, @doc_id, to: @to, limit: @limit) .should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 6c24f42e37..c5609994a1 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -134,7 +134,7 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "getUpdates", -> + describe "getDocUpdates", -> beforeEach -> @updates = ["mock-update"] @db.docHistory = {} @@ -148,7 +148,7 @@ describe "MongoManager", -> describe "with a to version", -> beforeEach -> - @MongoManager.getUpdates @doc_id, from: @from, to: @to, @callback + @MongoManager.getDocUpdates @doc_id, from: @from, to: @to, @callback it "should find the all updates between the to and from versions", -> @db.docHistory.find @@ -172,7 +172,7 @@ describe "MongoManager", -> describe "without a to version", -> beforeEach -> - @MongoManager.getUpdates @doc_id, from: @from, @callback + @MongoManager.getDocUpdates @doc_id, from: @from, @callback it "should find the all updates after the from version", -> @db.docHistory.find @@ -187,7 +187,7 @@ describe "MongoManager", -> describe "with a limit", -> beforeEach -> - @MongoManager.getUpdates @doc_id, from: @from, limit: @limit = 10, @callback + @MongoManager.getDocUpdates @doc_id, from: @from, limit: @limit = 10, @callback it "should limit the results", -> @db.docHistory.limit @@ -195,3 +195,60 @@ describe "MongoManager", -> .should.equal true + describe "getDocUpdates", -> + beforeEach -> + @updates = ["mock-update"] + @db.docHistory = {} + @db.docHistory.find = sinon.stub().returns @db.docHistory + @db.docHistory.sort = sinon.stub().returns @db.docHistory + @db.docHistory.limit = sinon.stub().returns @db.docHistory + @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates) + + @before = Date.now() + + describe "with a before timestamp", -> + beforeEach -> + @MongoManager.getProjectUpdates @project_id, before: @before, @callback + + it "should find the all updates before the timestamp", -> + @db.docHistory.find + .calledWith({ + project_id: ObjectId(@project_id) + "meta.end_ts": { $lt: @before } + }) + .should.equal true + + it "should sort in descending version order", -> + @db.docHistory.sort + .calledWith("meta.end_ts": -1) + .should.equal true + + it "should not limit the results", -> + @db.docHistory.limit + .called.should.equal false + + it "should call the call back with the updates", -> + @callback.calledWith(null, @updates).should.equal true + + describe "without a before timestamp", -> + beforeEach -> + @MongoManager.getProjectUpdates @project_id, {}, @callback + + it "should find the all updates", -> + @db.docHistory.find + .calledWith({ + project_id: ObjectId(@project_id) + }) + .should.equal true + + it "should call the call back with the updates", -> + @callback.calledWith(null, @updates).should.equal true + + describe "with a limit", -> + beforeEach -> + @MongoManager.getProjectUpdates @project_id, before: @before, limit: @limit = 10, @callback + + it "should limit the results", -> + @db.docHistory.limit + .calledWith(@limit) + .should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 60611d8907..0673a08c8f 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -198,13 +198,13 @@ describe "UpdatesManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "getUpdates", -> + describe "getDocUpdates", -> beforeEach -> @updates = ["mock-updates"] @options = { to: "mock-to", limit: "mock-limit" } - @MongoManager.getUpdates = sinon.stub().callsArgWith(2, null, @updates) + @MongoManager.getDocUpdates = sinon.stub().callsArgWith(2, null, @updates) @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - @UpdatesManager.getUpdates @project_id, @doc_id, @options, @callback + @UpdatesManager.getDocUpdates @project_id, @doc_id, @options, @callback it "should process outstanding updates", -> @UpdatesManager.processUncompressedUpdatesWithLock @@ -212,7 +212,7 @@ describe "UpdatesManager", -> .should.equal true it "should get the updates from the database", -> - @MongoManager.getUpdates + @MongoManager.getDocUpdates .calledWith(@doc_id, @options) .should.equal true @@ -221,17 +221,17 @@ describe "UpdatesManager", -> .calledWith(null, @updates) .should.equal true - describe "getUpdatesWithUserInfo", -> + describe "getDocUpdatesWithUserInfo", -> beforeEach -> @updates = ["mock-updates"] @options = { to: "mock-to", limit: "mock-limit" } @updatesWithUserInfo = ["updates-with-user-info"] - @UpdatesManager.getUpdates = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, @updates) @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - @UpdatesManager.getUpdatesWithUserInfo @project_id, @doc_id, @options, @callback + @UpdatesManager.getDocUpdatesWithUserInfo @project_id, @doc_id, @options, @callback it "should get the updates", -> - @UpdatesManager.getUpdates + @UpdatesManager.getDocUpdates .calledWith(@project_id, @doc_id, @options) .should.equal true @@ -243,6 +243,45 @@ describe "UpdatesManager", -> it "should return the updates with the filled details", -> @callback.calledWith(null, @updatesWithUserInfo).should.equal true + describe "getProjectUpdates", -> + beforeEach -> + @updates = ["mock-updates"] + @options = { before: "mock-before", limit: "mock-limit" } + @MongoManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.getProjectUpdates @project_id, @options, @callback + + it "should get the updates from the database", -> + @MongoManager.getProjectUpdates + .calledWith(@project_id, @options) + .should.equal true + + it "should return the updates", -> + @callback + .calledWith(null, @updates) + .should.equal true + + describe "getProjectUpdatesWithUserInfo", -> + beforeEach -> + @updates = ["mock-updates"] + @options = { before: "mock-before", limit: "mock-limit" } + @updatesWithUserInfo = ["updates-with-user-info"] + @UpdatesManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) + @UpdatesManager.getProjectUpdatesWithUserInfo @project_id, @options, @callback + + it "should get the updates", -> + @UpdatesManager.getProjectUpdates + .calledWith(@project_id, @options) + .should.equal true + + it "should file the updates with the user info", -> + @UpdatesManager.fillUserInfo + .calledWith(@updates) + .should.equal true + + it "should return the updates with the filled details", -> + @callback.calledWith(null, @updatesWithUserInfo).should.equal true + describe "_extendBatchOfSummarizedUpdates", -> beforeEach -> @to = 42 @@ -256,11 +295,11 @@ describe "UpdatesManager", -> @existingSummarizedUpdates = ["summarized-updates-3"] @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @doc_id, @existingSummarizedUpdates, @to, @limit, @callback it "should get the updates", -> - @UpdatesManager.getUpdatesWithUserInfo + @UpdatesManager.getDocUpdatesWithUserInfo .calledWith(@project_id, @doc_id, { to: @to, limit: 3 * @limit }) .should.equal true @@ -276,20 +315,20 @@ describe "UpdatesManager", -> beforeEach -> @updates = [] @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @doc_id, @existingSummarizedUpdates, @to, @limit, @callback it "should call the callback with the summarized updates and true for end-of-database", -> @callback.calledWith(null, @summarizedUpdates.slice(0, @limit), true).should.equal true - describe "getSummarizedUpdates", -> + describe "getSummarizedDocUpdates", -> describe "when one batch of updates is enough to meet the limit", -> beforeEach -> @to = 42 @limit = 2 @updates = ["summarized-updates-3", "summarized-updates-2"] @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(5, null, @updates) - @UpdatesManager.getSummarizedUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager.getSummarizedDocUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback it "should get the batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates @@ -311,7 +350,7 @@ describe "UpdatesManager", -> else callback null, @firstBatch.concat(@secondBatch), false sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" - @UpdatesManager.getSummarizedUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager.getSummarizedDocUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback it "should get the first batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates @@ -332,7 +371,7 @@ describe "UpdatesManager", -> @limit = 4 @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(5, null, @updates, true) - @UpdatesManager.getSummarizedUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager.getSummarizedDocUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback it "should get the batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates From 55959101225c1b0f8efbe7cde69ee5b3b9b1cfd1 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 20 Mar 2014 12:10:04 +0000 Subject: [PATCH 067/549] Return a summary of the entire project changes --- services/track-changes/app.coffee | 2 +- .../app/coffee/HttpController.coffee | 15 +-- .../app/coffee/UpdatesManager.coffee | 47 +++++--- .../coffee/GettingUpdatesTests.coffee | 17 ++- .../coffee/helpers/TrackChangesClient.coffee | 20 +++- .../HttpController/HttpControllerTests.coffee | 18 +-- .../UpdatesManager/UpdatesManagerTests.coffee | 103 +++++++++++------- 7 files changed, 136 insertions(+), 86 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 4e62a63c28..a0b6f3d5b3 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -14,7 +14,7 @@ app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushUpdatesWi app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff -app.get "/project/:project_id/doc/:doc_id/updates", HttpController.getUpdates +app.get "/project/:project_id/updates", HttpController.getUpdates app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 66c2c294ad..abc2226fc5 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -31,17 +31,18 @@ module.exports = HttpController = res.send JSON.stringify(diff: diff) getUpdates: (req, res, next = (error) ->) -> - doc_id = req.params.doc_id project_id = req.params.project_id - if req.query.to? - to = parseInt(req.query.to, 10) - if req.query.limit? - limit = parseInt(req.query.limit, 10) + if req.query.before? + before = parseInt(req.query.before, 10) + if req.query.min_count? + min_count = parseInt(req.query.min_count, 10) - UpdatesManager.getSummarizedDocUpdates project_id, doc_id, to: to, limit: limit, (error, updates) -> + UpdatesManager.getSummarizedProjectUpdates project_id, before: before, min_count: min_count, (error, updates, nextBeforeTimestamp) -> return next(error) if error? - res.send JSON.stringify updates: updates + res.send JSON.stringify + updates: updates + nextBeforeTimestamp: nextBeforeTimestamp restore: (req, res, next = (error) ->) -> {doc_id, project_id, version} = req.params diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 9c63f47de8..cf5a83ce71 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -86,39 +86,49 @@ module.exports = UpdatesManager = return callback(error) if error? callback null, updates - getSummarizedDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> - options.limit ||= 25 + getSummarizedProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> + options.min_count ||= 25 summarizedUpdates = [] - to = options.to + before = options.before do fetchNextBatch = () -> - UpdatesManager._extendBatchOfSummarizedUpdates project_id, doc_id, summarizedUpdates, to, options.limit, (error, updates, endOfDatabase) -> + UpdatesManager._extendBatchOfSummarizedUpdates project_id, summarizedUpdates, before, options.min_count, (error, updates, nextBeforeUpdate) -> return callback(error) if error? - if endOfDatabase or updates.length >= options.limit - callback null, updates + if !nextBeforeUpdate? or updates.length >= options.min_count + callback null, updates, nextBeforeUpdate else - to = updates[updates.length - 1].fromV - 1 + before = nextBeforeUpdate summarizedUpdates = updates fetchNextBatch() _extendBatchOfSummarizedUpdates: ( - project_id, - doc_id, + project_id, existingSummarizedUpdates, - to, desiredLength, + before, desiredLength, callback = (error, summarizedUpdates, endOfDatabase) -> ) -> - UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, { to: to, limit: 3 * desiredLength }, (error, updates) -> + UpdatesManager.getProjectUpdatesWithUserInfo project_id, { before: before, limit: 3 * desiredLength }, (error, updates) -> return callback(error) if error? - if !updates? or updates.length == 0 - endOfDatabase = true + + # Suppose in this request we have fetch the solid updates. In the next request we need + # to fetch the dotted updates. These are defined by having an end timestamp less than + # the last update's end timestamp (updates are ordered by descending end_ts). I.e. + # start_ts--v v--end_ts + # doc1: |......| |...| |-------| + # doc2: |------------------| + # ^----- Next time, fetch all updates with an + # end_ts less than this + # + if updates? and updates.length > 0 + nextBeforeTimestamp = updates[updates.length - 1].meta.end_ts else - endOfDatabase = false + nextBeforeTimestamp = null + summarizedUpdates = UpdatesManager._summarizeUpdates( updates, existingSummarizedUpdates ) callback null, - summarizedUpdates.slice(0, desiredLength), - endOfDatabase + summarizedUpdates, + nextBeforeTimestamp fillUserInfo: (updates, callback = (error, updates) ->) -> users = {} @@ -165,6 +175,10 @@ module.exports = UpdatesManager = break if !userExists earliestUpdate.meta.users.push update.meta.user + + if update.doc_id.toString() not in earliestUpdate.doc_ids + earliestUpdate.doc_ids.push update.doc_id.toString() + earliestUpdate.meta.start_ts = Math.min(earliestUpdate.meta.start_ts, update.meta.start_ts) earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts) earliestUpdate.fromV = update.v @@ -176,6 +190,7 @@ module.exports = UpdatesManager = end_ts: update.meta.end_ts fromV: update.v toV: update.v + doc_ids: [update.doc_id.toString()] if update.meta.user? newUpdate.meta.users.push update.meta.user diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 4f2406c62e..1bfc939629 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -43,14 +43,16 @@ describe "Getting updates", -> TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => throw error if error? - done() + TrackChangesClient.flushUpdates @project_id, @doc_id, (error) => + throw error if error? + done() after: () -> MockWebApi.getUser.restore() describe "getting updates up to the limit", -> before (done) -> - TrackChangesClient.getUpdates @project_id, @doc_id, { to: 20, limit: 3 }, (error, body) => + TrackChangesClient.getUpdates @project_id, { before: @to + 1, min_count: 3 }, (error, body) => throw error if error? @updates = body.updates done() @@ -60,8 +62,9 @@ describe "Getting updates", -> .calledWith(@user_id) .should.equal true - it "should return the same number of summarized updates as the limit", -> - expect(@updates).to.deep.equal [{ + it "should return at least the min_count number of summarized updates", -> + expect(@updates.slice(0,3)).to.deep.equal [{ + doc_ids: [@doc_id] meta: start_ts: @to - 2 * @minutes end_ts: @to @@ -69,6 +72,7 @@ describe "Getting updates", -> toV: 20 fromV: 19 }, { + doc_ids: [@doc_id] meta: start_ts: @to - 1 * @days - 2 * @minutes end_ts: @to - 1 * @days @@ -76,6 +80,7 @@ describe "Getting updates", -> toV: 18 fromV: 17 }, { + doc_ids: [@doc_id] meta: start_ts: @to - 2 * @days - 2 * @minutes end_ts: @to - 2 * @days @@ -87,13 +92,14 @@ describe "Getting updates", -> describe "getting updates beyond the end of the database", -> before (done) -> - TrackChangesClient.getUpdates @project_id, @doc_id, { to: 4, limit: 30 }, (error, body) => + TrackChangesClient.getUpdates @project_id, { before: @to - 8 * @days + 1, min_count: 30 }, (error, body) => throw error if error? @updates = body.updates done() it "should return as many updates as it can", -> expect(@updates).to.deep.equal [{ + doc_ids: [@doc_id] meta: start_ts: @to - 8 * @days - 2 * @minutes end_ts: @to - 8 * @days @@ -101,6 +107,7 @@ describe "Getting updates", -> toV: 4 fromV: 3 }, { + doc_ids: [@doc_id] meta: start_ts: @to - 9 * @days - 2 * @minutes end_ts: @to - 9 * @days diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 71efab30b8..f9e3a42e2f 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -4,14 +4,22 @@ rclient = require("redis").createClient() # Only works locally for now module.exports = TrackChangesClient = flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> + TrackChangesClient.flushUpdates project_id, doc_id, (error) -> + return callback(error) if error? + TrackChangesClient.getCompressedUpdates doc_id, callback + + flushUpdates: (project_id, doc_id, callback = (error) ->) -> request.post { url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/flush" }, (error, response, body) => response.statusCode.should.equal 204 - db.docHistory - .find(doc_id: ObjectId(doc_id)) - .sort("meta.end_ts": 1) - .toArray callback + callback(error) + + getCompressedUpdates: (doc_id, callback = (error, updates) ->) -> + db.docHistory + .find(doc_id: ObjectId(doc_id)) + .sort("meta.end_ts": 1) + .toArray callback pushRawUpdates: (doc_id, updates, callback = (error) ->) -> rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback @@ -23,9 +31,9 @@ module.exports = TrackChangesClient = response.statusCode.should.equal 200 callback null, JSON.parse(body) - getUpdates: (project_id, doc_id, options, callback = (error, body) ->) -> + getUpdates: (project_id, options, callback = (error, body) ->) -> request.get { - url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/updates?to=#{options.to}&limit=#{options.limit}" + url: "http://localhost:3015/project/#{project_id}/updates?before=#{options.before}&min_count=#{options.min_count}" }, (error, response, body) => response.statusCode.should.equal 200 callback null, JSON.parse(body) diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 4d15670653..4caa1b83ef 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -64,28 +64,28 @@ describe "HttpController", -> describe "getUpdates", -> beforeEach -> - @to = 42 - @limit = 10 + @before = Date.now() + @nextBeforeTimestamp = @before - 100 + @min_count = 10 @req = params: - doc_id: @doc_id project_id: @project_id query: - to: @to.toString() - limit: @limit.toString() + before: @before.toString() + min_count: @min_count.toString() @res = send: sinon.stub() @updates = ["mock-summarized-updates"] - @UpdatesManager.getSummarizedDocUpdates = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.getSummarizedProjectUpdates = sinon.stub().callsArgWith(2, null, @updates, @nextBeforeTimestamp) @HttpController.getUpdates @req, @res, @next it "should get the updates", -> - @UpdatesManager.getSummarizedDocUpdates - .calledWith(@project_id, @doc_id, to: @to, limit: @limit) + @UpdatesManager.getSummarizedProjectUpdates + .calledWith(@project_id, before: @before, min_count: @min_count) .should.equal true it "should return the formatted updates", -> - @res.send.calledWith(JSON.stringify(updates: @updates)).should.equal true + @res.send.calledWith(JSON.stringify(updates: @updates, nextBeforeTimestamp: @nextBeforeTimestamp)).should.equal true describe "RestoreManager", -> beforeEach -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 0673a08c8f..d5608f8bee 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -284,23 +284,26 @@ describe "UpdatesManager", -> describe "_extendBatchOfSummarizedUpdates", -> beforeEach -> - @to = 42 - @limit = 2 + @before = Date.now() + @min_count = 2 @existingSummarizedUpdates = ["summarized-updates-3"] @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] describe "when there are updates to get", -> beforeEach -> - @updates = ["mock-updates"] + @updates = [ + {op: "mock-op-1", meta: end_ts: @before - 10}, + {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} + ] @existingSummarizedUpdates = ["summarized-updates-3"] @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) - @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @doc_id, @existingSummarizedUpdates, @to, @limit, @callback + @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback it "should get the updates", -> - @UpdatesManager.getDocUpdatesWithUserInfo - .calledWith(@project_id, @doc_id, { to: @to, limit: 3 * @limit }) + @UpdatesManager.getProjectUpdatesWithUserInfo + .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) .should.equal true it "should summarize the updates", -> @@ -308,78 +311,81 @@ describe "UpdatesManager", -> .calledWith(@updates, @existingSummarizedUpdates) .should.equal true - it "should call the callback with the summarized updates and false for end-of-databse", -> - @callback.calledWith(null, @summarizedUpdates.slice(0, @limit), false).should.equal true + it "should call the callback with the summarized updates and the next before timestamp", -> + @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true describe "when there are no more updates", -> beforeEach -> @updates = [] @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) - @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @doc_id, @existingSummarizedUpdates, @to, @limit, @callback + @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - it "should call the callback with the summarized updates and true for end-of-database", -> - @callback.calledWith(null, @summarizedUpdates.slice(0, @limit), true).should.equal true + it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> + @callback.calledWith(null, @summarizedUpdates, null).should.equal true - describe "getSummarizedDocUpdates", -> + describe "getSummarizedProjectUpdates", -> describe "when one batch of updates is enough to meet the limit", -> beforeEach -> - @to = 42 - @limit = 2 + @before = Date.now() + @min_count = 2 @updates = ["summarized-updates-3", "summarized-updates-2"] - @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(5, null, @updates) - @UpdatesManager.getSummarizedDocUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback + @nextBeforeTimestamp = @before - 100 + @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) + @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback it "should get the batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, @doc_id, [], @to, @limit) + .calledWith(@project_id, [], @before, @min_count) .should.equal true it "should call the callback with the updates", -> - @callback.calledWith(null, @updates).should.equal true + @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true describe "when multiple batches are needed to meet the limit", -> beforeEach -> - @to = 6 - @limit = 4 + @before = Date.now() + @min_count = 4 @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + @nextBeforeTimestamp = @before - 100 @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] - @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, doc_id, existingUpdates, to, limit, callback) => + @nextNextBeforeTimestamp = @before - 200 + @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => if existingUpdates.length == 0 - callback null, @firstBatch, false + callback null, @firstBatch, @nextBeforeTimestamp else - callback null, @firstBatch.concat(@secondBatch), false + callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" - @UpdatesManager.getSummarizedDocUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback it "should get the first batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, @doc_id, [], @to, @limit) + .calledWith(@project_id, [], @before, @min_count) .should.equal true it "should get the second batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, @doc_id, @firstBatch, 4, @limit) + .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) .should.equal true it "should call the callback with all the updates", -> - @callback.calledWith(null, @firstBatch.concat(@secondBatch)).should.equal true + @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true describe "when the end of the database is hit", -> beforeEach -> - @to = 6 - @limit = 4 + @before = Date.now() + @min_count = 4 @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(5, null, @updates, true) - @UpdatesManager.getSummarizedDocUpdates @project_id, @doc_id, { to: @to, limit: @limit }, @callback + @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) + @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback it "should get the batch of summarized updates", -> @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, @doc_id, [], @to, @limit) + .calledWith(@project_id, [], @before, @min_count) .should.equal true it "should call the callback with the updates", -> - @callback.calledWith(null, @updates).should.equal true + @callback.calledWith(null, @updates, null).should.equal true describe "fillUserInfo", -> describe "with valid users", -> @@ -469,24 +475,31 @@ describe "UpdatesManager", -> op: "mock-op-2" }] - describe "_buildUpdatesView", -> + describe "_summarizeUpdates", -> beforeEach -> @now = Date.now() + @user_1 = { id: "mock-user-1" } + @user_2 = { id: "mock-user-2" } + @doc_id_1 = "mock-doc-id-1" + @doc_id_2 = "mock-doc-id-2" it "should concat updates that are close in time", -> expect(@UpdatesManager._summarizeUpdates [{ + doc_id: @doc_id_1 meta: - user: @user_1 = { id: "mock-user-1" } + user: @user_1 start_ts: @now + 20 end_ts: @now + 30 v: 5 }, { + doc_id: @doc_id_1 meta: - user: @user_2 = { id: "mock-user-2" } + user: @user_2 start_ts: @now end_ts: @now + 10 v: 4 }]).to.deep.equal [{ + doc_ids: [@doc_id_1] meta: users: [@user_1, @user_2] start_ts: @now @@ -498,18 +511,21 @@ describe "UpdatesManager", -> it "should leave updates that are far apart in time", -> oneDay = 1000 * 60 * 60 * 24 expect(@UpdatesManager._summarizeUpdates [{ + doc_id: @doc_id_1 meta: - user: @user_2 = { id: "mock-user-2" } + user: @user_2 start_ts: @now + oneDay end_ts: @now + oneDay + 10 v: 5 }, { + doc_id: @doc_id_1 meta: - user: @user_1 = { id: "mock-user-2" } + user: @user_1 start_ts: @now end_ts: @now + 10 v: 4 }]).to.deep.equal [{ + doc_ids: [@doc_id_1] meta: users: [@user_2] start_ts: @now + oneDay @@ -517,6 +533,7 @@ describe "UpdatesManager", -> fromV: 5 toV: 5 }, { + doc_ids: [@doc_id_1] meta: users: [@user_1] start_ts: @now @@ -526,21 +543,22 @@ describe "UpdatesManager", -> }] it "should concat onto existing summarized updates", -> - @user_1 = { id: "mock-user-1" } - @user_2 = { id: "mock-user-2" } expect(@UpdatesManager._summarizeUpdates [{ + doc_id: @doc_id_2 meta: user: @user_1 start_ts: @now + 20 end_ts: @now + 30 v: 5 }, { + doc_id: @doc_id_2 meta: user: @user_2 start_ts: @now end_ts: @now + 10 v: 4 }], [{ + doc_ids: [@doc_id_1] meta: users: [@user_1] start_ts: @now + 40 @@ -548,6 +566,7 @@ describe "UpdatesManager", -> fromV: 6 toV: 8 }]).to.deep.equal [{ + doc_ids: [@doc_id_1, @doc_id_2] meta: users: [@user_1, @user_2] start_ts: @now From 6aaa7ba8d593a3169e5d11da47165d4edb3ebbba Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 20 Mar 2014 13:37:23 +0000 Subject: [PATCH 068/549] Group toV and fromV by the doc they correspond to --- .../app/coffee/UpdatesManager.coffee | 16 +++-- .../coffee/GettingUpdatesTests.coffee | 30 ++++---- .../UpdatesManager/UpdatesManagerTests.coffee | 68 +++++++++++-------- 3 files changed, 66 insertions(+), 48 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index cf5a83ce71..839c74fe6d 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -176,21 +176,29 @@ module.exports = UpdatesManager = if !userExists earliestUpdate.meta.users.push update.meta.user - if update.doc_id.toString() not in earliestUpdate.doc_ids - earliestUpdate.doc_ids.push update.doc_id.toString() + doc_id = update.doc_id.toString() + doc = earliestUpdate.docs[doc_id] + if doc? + doc.fromV = Math.min(doc.fromV, update.v) + doc.toV = Math.max(doc.toV, update.v) + else + earliestUpdate.docs[doc_id] = + fromV: update.v + toV: update.v earliestUpdate.meta.start_ts = Math.min(earliestUpdate.meta.start_ts, update.meta.start_ts) earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts) - earliestUpdate.fromV = update.v else newUpdate = meta: users: [] start_ts: update.meta.start_ts end_ts: update.meta.end_ts + docs: {} + + newUpdate.docs[update.doc_id.toString()] = fromV: update.v toV: update.v - doc_ids: [update.doc_id.toString()] if update.meta.user? newUpdate.meta.users.push update.meta.user diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 1bfc939629..13e70162da 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -63,30 +63,30 @@ describe "Getting updates", -> .should.equal true it "should return at least the min_count number of summarized updates", -> + docs1 = {} + docs1[@doc_id] = toV: 20, fromV: 19 + docs2 = {} + docs2[@doc_id] = toV: 18, fromV: 17 + docs3 = {} + docs3[@doc_id] = toV: 16, fromV: 15 expect(@updates.slice(0,3)).to.deep.equal [{ - doc_ids: [@doc_id] + docs: docs1 meta: start_ts: @to - 2 * @minutes end_ts: @to users: [@user] - toV: 20 - fromV: 19 }, { - doc_ids: [@doc_id] + docs: docs2 meta: start_ts: @to - 1 * @days - 2 * @minutes end_ts: @to - 1 * @days users: [@user] - toV: 18 - fromV: 17 }, { - doc_ids: [@doc_id] + docs: docs3 meta: start_ts: @to - 2 * @days - 2 * @minutes end_ts: @to - 2 * @days users: [@user] - toV: 16 - fromV: 15 }] @@ -98,21 +98,21 @@ describe "Getting updates", -> done() it "should return as many updates as it can", -> + docs1 = {} + docs1[@doc_id] = toV: 4, fromV: 3 + docs2 = {} + docs2[@doc_id] = toV: 2, fromV: 1 expect(@updates).to.deep.equal [{ - doc_ids: [@doc_id] + docs: docs1 meta: start_ts: @to - 8 * @days - 2 * @minutes end_ts: @to - 8 * @days users: [@user] - toV: 4 - fromV: 3 }, { - doc_ids: [@doc_id] + docs: docs2 meta: start_ts: @to - 9 * @days - 2 * @minutes end_ts: @to - 9 * @days users: [@user] - toV: 2 - fromV: 1 }] diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index d5608f8bee..59e1293ac4 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -480,97 +480,107 @@ describe "UpdatesManager", -> @now = Date.now() @user_1 = { id: "mock-user-1" } @user_2 = { id: "mock-user-2" } - @doc_id_1 = "mock-doc-id-1" - @doc_id_2 = "mock-doc-id-2" it "should concat updates that are close in time", -> - expect(@UpdatesManager._summarizeUpdates [{ - doc_id: @doc_id_1 + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" meta: user: @user_1 start_ts: @now + 20 end_ts: @now + 30 v: 5 }, { - doc_id: @doc_id_1 + doc_id: "doc-id-1" meta: user: @user_2 start_ts: @now end_ts: @now + 10 v: 4 - }]).to.deep.equal [{ - doc_ids: [@doc_id_1] + }] + + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 meta: users: [@user_1, @user_2] start_ts: @now end_ts: @now + 30 - fromV: 4 - toV: 5 }] it "should leave updates that are far apart in time", -> oneDay = 1000 * 60 * 60 * 24 - expect(@UpdatesManager._summarizeUpdates [{ - doc_id: @doc_id_1 + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" meta: user: @user_2 start_ts: @now + oneDay end_ts: @now + oneDay + 10 v: 5 }, { - doc_id: @doc_id_1 + doc_id: "doc-id-1" meta: user: @user_1 start_ts: @now end_ts: @now + 10 v: 4 - }]).to.deep.equal [{ - doc_ids: [@doc_id_1] + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 5 + toV: 5 meta: users: [@user_2] start_ts: @now + oneDay end_ts: @now + oneDay + 10 - fromV: 5 - toV: 5 }, { - doc_ids: [@doc_id_1] + docs: + "doc-id-1": + fromV: 4 + toV: 4 meta: users: [@user_1] start_ts: @now end_ts: @now + 10 - fromV: 4 - toV: 4 }] it "should concat onto existing summarized updates", -> - expect(@UpdatesManager._summarizeUpdates [{ - doc_id: @doc_id_2 + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-2" meta: user: @user_1 start_ts: @now + 20 end_ts: @now + 30 v: 5 }, { - doc_id: @doc_id_2 + doc_id: "doc-id-2" meta: user: @user_2 start_ts: @now end_ts: @now + 10 v: 4 }], [{ - doc_ids: [@doc_id_1] + docs: + "doc-id-1": + fromV: 6 + toV: 8 meta: users: [@user_1] start_ts: @now + 40 end_ts: @now + 50 - fromV: 6 - toV: 8 - }]).to.deep.equal [{ - doc_ids: [@doc_id_1, @doc_id_2] + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + toV: 8 + fromV: 6 + "doc-id-2": + toV: 5 + fromV: 4 meta: users: [@user_1, @user_2] start_ts: @now end_ts: @now + 50 - fromV: 4 - toV: 8 }] From cd9cb51027e78195c1fc73a8b9ce3d4ace487fec Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 21 Mar 2014 13:17:58 +0000 Subject: [PATCH 069/549] Delete doc_id from DocsWithHistoryOps set when processing --- .../app/coffee/RedisManager.coffee | 17 ++++++++++++----- .../app/coffee/UpdatesManager.coffee | 2 +- .../coffee/AppendingUpdatesTests.coffee | 17 +++++++++++------ .../acceptance/coffee/GettingADiffTests.coffee | 2 +- .../coffee/GettingUpdatesTests.coffee | 2 +- .../acceptance/coffee/RestoringVersions.coffee | 2 +- .../coffee/helpers/TrackChangesClient.coffee | 6 ++++-- .../RedisManager/RedisManagerTests.coffee | 15 ++++++++++++--- .../UpdatesManager/UpdatesManagerTests.coffee | 6 +++--- 9 files changed, 46 insertions(+), 23 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 81a1375bf6..98f310a9f8 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -4,11 +4,12 @@ redisConf = Settings.redis?.web or {host: "localhost", port: 6379} rclient = redis.createClient(redisConf.port, redisConf.host) rclient.auth(redisConf.password) -buildRawUpdatesKey = (doc_id) -> "UncompressedHistoryOps:#{doc_id}" +rawUpdatesKey = (doc_id) -> "UncompressedHistoryOps:#{doc_id}" +docsWithHistoryOpsKey = (project_id) -> "DocsWithHistoryOps:#{project_id}" module.exports = RedisManager = getOldestRawUpdates: (doc_id, batchSize, callback = (error, rawUpdates) ->) -> - key = buildRawUpdatesKey(doc_id) + key = rawUpdatesKey(doc_id) rclient.lrange key, 0, batchSize - 1, (error, jsonUpdates) -> try rawUpdates = ( JSON.parse(update) for update in jsonUpdates or [] ) @@ -16,6 +17,12 @@ module.exports = RedisManager = return callback(e) callback null, rawUpdates - deleteOldestRawUpdates: (doc_id, batchSize, callback = (error, rawUpdates) ->) -> - key = buildRawUpdatesKey(doc_id) - rclient.ltrim key, batchSize, -1, callback + deleteOldestRawUpdates: (project_id, doc_id, batchSize, callback = (error) ->) -> + # It's ok to delete the doc_id from the set here. Even though the list + # of updates may not be empty, we will continue to process it until it is. + multi = rclient.multi() + multi.ltrim rawUpdatesKey(doc_id), batchSize, -1 + multi.srem docsWithHistoryOpsKey(project_id), doc_id + multi.exec (error, results) -> + return callback(error) if error? + callback null diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 839c74fe6d..e582108e29 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -44,7 +44,7 @@ module.exports = UpdatesManager = UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, (error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - RedisManager.deleteOldestRawUpdates doc_id, length, (error) -> + RedisManager.deleteOldestRawUpdates project_id, doc_id, length, (error) -> return callback(error) if error? if length == UpdatesManager.REDIS_READ_BATCH_SIZE # There might be more updates diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 1fa4166f60..7a8d465504 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -16,7 +16,7 @@ describe "Appending doc ops to the history", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - TrackChangesClient.pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } v: 3 @@ -48,12 +48,17 @@ describe "Appending doc ops to the history", -> it "should store the project id", -> expect(@updates[0].project_id.toString()).to.equal @project_id + it "should clear the doc from the DocsWithHistoryOps set", (done) -> + rclient.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, member) -> + member.should.equal 0 + done() + describe "when the history has already been started", -> beforeEach (done) -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() - TrackChangesClient.pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } v: 3 @@ -73,7 +78,7 @@ describe "Appending doc ops to the history", -> describe "when the updates are recent and from the same user", -> beforeEach (done) -> - TrackChangesClient.pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now(), user_id: @user_id } v: 6 @@ -103,7 +108,7 @@ describe "Appending doc ops to the history", -> describe "when the updates are far apart", -> beforeEach (done) -> oneDay = 24 * 60 * 60 * 1000 - TrackChangesClient.pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now() + oneDay, user_id: @user_id } v: 6 @@ -144,7 +149,7 @@ describe "Appending doc ops to the history", -> } @expectedOp[0].i = "a" + @expectedOp[0].i - TrackChangesClient.pushRawUpdates @doc_id, updates, (error) => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, updates, (error) => throw error if error? TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? @@ -163,7 +168,7 @@ describe "Appending doc ops to the history", -> @doc_id = ObjectId().toString() @user_id = ObjectId().toString() oneDay = 24 * 60 * 60 * 1000 - TrackChangesClient.pushRawUpdates @doc_id, [{ + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }] meta: { ts: Date.now(), user_id: @user_id } v: 3 diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index 6fe573c19e..7561b68673 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -58,7 +58,7 @@ describe "Getting a diff", -> lines: @lines version: 7 - TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? TrackChangesClient.getDiff @project_id, @doc_id, @fromVersion, @toVersion, (error, diff) => throw error if error? diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 13e70162da..8319c80456 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -41,7 +41,7 @@ describe "Getting updates", -> v: 2 * i + 2 } - TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? TrackChangesClient.flushUpdates @project_id, @doc_id, (error) => throw error if error? diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index 03f4c8b8f7..7e3c29a0e7 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -52,7 +52,7 @@ describe "Restoring a version", -> lines: @lines version: 7 - TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) => throw error if error? diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index f9e3a42e2f..dea3a885d5 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -21,8 +21,10 @@ module.exports = TrackChangesClient = .sort("meta.end_ts": 1) .toArray callback - pushRawUpdates: (doc_id, updates, callback = (error) ->) -> - rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback + pushRawUpdates: (project_id, doc_id, updates, callback = (error) ->) -> + rclient.sadd "DocsWithHistoryOps:#{project_id}", doc_id, (error) -> + return callback(error) if error? + rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback getDiff: (project_id, doc_id, from, to, callback = (error, diff) ->) -> request.get { diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee index 669e64103d..27c829a948 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -11,8 +11,10 @@ describe "RedisManager", -> "redis" : createClient: () => @rclient = auth: sinon.stub() + multi: () => @rclient "settings-sharelatex": {} @doc_id = "doc-id-123" + @project_id = "project-id-123" @batchSize = 100 @callback = sinon.stub() @@ -33,13 +35,20 @@ describe "RedisManager", -> describe "deleteOldestRawUpdates", -> beforeEach -> - @rclient.ltrim = sinon.stub().callsArg(3) - @RedisManager.deleteOldestRawUpdates @doc_id, @batchSize, @callback + @rclient.ltrim = sinon.stub() + @rclient.srem = sinon.stub() + @rclient.exec = sinon.stub().callsArgWith(0) + @RedisManager.deleteOldestRawUpdates @project_id, @doc_id, @batchSize, @callback it "should delete the updates from redis", -> @rclient.ltrim .calledWith("UncompressedHistoryOps:#{@doc_id}", @batchSize, -1) .should.equal true - it "should call the callback", -> + it "should delete the doc from the set of docs with history ops", -> + @rclient.srem + .calledWith("DocsWithHistoryOps:#{@project_id}", @doc_id) + .should.equal true + + it "should call the callback ", -> @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 59e1293ac4..c6abb6a7b6 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -125,7 +125,7 @@ describe "UpdatesManager", -> @updates = ["mock-update"] @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) - @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) + @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback it "should get the oldest updates", -> @@ -140,7 +140,7 @@ describe "UpdatesManager", -> it "should delete the batch of uncompressed updates that was just processed", -> @RedisManager.deleteOldestRawUpdates - .calledWith(@doc_id, @updates.length) + .calledWith(@project_id, @doc_id, @updates.length) .should.equal true it "should call the callback", -> @@ -157,7 +157,7 @@ describe "UpdatesManager", -> callback null, updates sinon.spy @RedisManager, "getOldestRawUpdates" @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) - @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(2) + @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => @callback(args...) done() From 2cd85fefec9ce8586958cfa75a1786ad8d745145 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 21 Mar 2014 13:48:14 +0000 Subject: [PATCH 070/549] Flush all docs in a project before getting updates --- .../app/coffee/RedisManager.coffee | 3 ++ .../app/coffee/UpdatesManager.coffee | 14 ++++++++- .../coffee/GettingUpdatesTests.coffee | 4 +-- .../RedisManager/RedisManagerTests.coffee | 14 +++++++++ .../UpdatesManager/UpdatesManagerTests.coffee | 29 +++++++++++++++++++ 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 98f310a9f8..676259d311 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -26,3 +26,6 @@ module.exports = RedisManager = multi.exec (error, results) -> return callback(error) if error? callback null + + getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> + rclient.smembers docsWithHistoryOpsKey(project_id), callback diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index e582108e29..42cd38fad4 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -64,6 +64,16 @@ module.exports = UpdatesManager = callback ) + processUncompressedUpdatesForProject: (project_id, callback = (error) ->) -> + RedisManager.getDocIdsWithHistoryOps project_id, (error, doc_ids) -> + return callback(error) if error? + jobs = [] + for doc_id in doc_ids + do (doc_id) -> + jobs.push (callback) -> + UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, callback + async.parallelLimit jobs, 5, callback + getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? @@ -77,7 +87,9 @@ module.exports = UpdatesManager = callback null, updates getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> - MongoManager.getProjectUpdates project_id, options, callback + UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> + return callback(error) if error? + MongoManager.getProjectUpdates project_id, options, callback getProjectUpdatesWithUserInfo: (project_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.getProjectUpdates project_id, options, (error, updates) -> diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 8319c80456..cbf93f105a 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -43,9 +43,7 @@ describe "Getting updates", -> TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? - TrackChangesClient.flushUpdates @project_id, @doc_id, (error) => - throw error if error? - done() + done() after: () -> MockWebApi.getUser.restore() diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee index 27c829a948..5efb9768f6 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -52,3 +52,17 @@ describe "RedisManager", -> it "should call the callback ", -> @callback.called.should.equal true + + describe "getDocIdsWithHistoryOps", -> + beforeEach -> + @doc_ids = ["mock-id-1", "mock-id-2"] + @rclient.smembers = sinon.stub().callsArgWith(1, null, @doc_ids) + @RedisManager.getDocIdsWithHistoryOps @project_id, @callback + + it "should read the doc_ids from redis", -> + @rclient.smembers + .calledWith("DocsWithHistoryOps:#{@project_id}") + .should.equal true + + it "should call the callback with the doc_ids", -> + @callback.calledWith(null, @doc_ids).should.equal true \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index c6abb6a7b6..9bdf5fd635 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -248,8 +248,14 @@ describe "UpdatesManager", -> @updates = ["mock-updates"] @options = { before: "mock-before", limit: "mock-limit" } @MongoManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) + @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) @UpdatesManager.getProjectUpdates @project_id, @options, @callback + it "should process any outstanding updates", -> + @UpdatesManager.processUncompressedUpdatesForProject + .calledWith(@project_id) + .should.equal true + it "should get the updates from the database", -> @MongoManager.getProjectUpdates .calledWith(@project_id, @options) @@ -260,6 +266,29 @@ describe "UpdatesManager", -> .calledWith(null, @updates) .should.equal true + describe "processUncompressedUpdatesForProject", -> + beforeEach (done) -> + @doc_ids = ["mock-id-1", "mock-id-2"] + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) + @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => + @callback() + done() + + it "should get all the docs with history ops", -> + @RedisManager.getDocIdsWithHistoryOps + .calledWith(@project_id) + .should.equal true + + it "should process the doc ops for the each doc_id", -> + for doc_id in @doc_ids + @UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(@project_id, doc_id) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + describe "getProjectUpdatesWithUserInfo", -> beforeEach -> @updates = ["mock-updates"] From 8cae7268384f99863596910582982927985e5804 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 21 Mar 2014 14:40:51 +0000 Subject: [PATCH 071/549] Backport project_id onto doc updates that don't have one yet --- .../app/coffee/MongoManager.coffee | 20 +++++++++++-- .../app/coffee/UpdatesManager.coffee | 30 ++++++++++--------- .../MongoManager/MongoManagerTests.coffee | 22 ++++++++++++++ .../UpdatesManager/UpdatesManagerTests.coffee | 14 ++++++--- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 71f926a745..330a903a8a 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -75,7 +75,21 @@ module.exports = MongoManager = cursor.toArray callback - ensureIndices: (callback = (error) ->) -> - db.docHistory.ensureIndex { doc_id: 1, v: 1 }, callback - db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, callback + backportProjectId: (project_id, doc_id, callback = (error) ->) -> + db.docHistory.update { + doc_id: ObjectId(doc_id.toString()) + project_id: { $exists: false } + }, { + $set: { project_id: ObjectId(project_id.toString()) } + }, { + multi: true + }, callback + + ensureIndices: (callback = (error) ->) -> + # For finding all updates that go into a diff for a doc + db.docHistory.ensureIndex { doc_id: 1, v: 1 }, callback + # For finding all updates that affect a project + db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, callback + # For finding updates that don't yet have a project_id and need it inserting + db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, callback diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 42cd38fad4..c13b42d326 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -38,23 +38,25 @@ module.exports = UpdatesManager = REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> - RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> + MongoManager.backportProjectId project_id, doc_id, (error) -> return callback(error) if error? - length = rawUpdates.length - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, (error) -> + RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - RedisManager.deleteOldestRawUpdates project_id, doc_id, length, (error) -> + length = rawUpdates.length + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, (error) -> return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + RedisManager.deleteOldestRawUpdates project_id, doc_id, length, (error) -> + return callback(error) if error? + if length == UpdatesManager.REDIS_READ_BATCH_SIZE + # There might be more updates + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" + setTimeout () -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, callback + , 0 + else + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" + callback() processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> LockManager.runWithLock( diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index c5609994a1..b3a8fc04ca 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -252,3 +252,25 @@ describe "MongoManager", -> @db.docHistory.limit .calledWith(@limit) .should.equal true + + describe "backportProjectId", -> + beforeEach -> + @db.docHistory = + update: sinon.stub().callsArg(3) + @MongoManager.backportProjectId @project_id, @doc_id, @callback + + it "should insert the project_id into all entries for the doc_id which don't have it set", -> + @db.docHistory.update + .calledWith({ + doc_id: ObjectId(@doc_id) + project_id: { $exists: false } + }, { + $set: { project_id: ObjectId(@project_id) } + }, { + multi: true + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 9bdf5fd635..6ff9f64e11 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -120,14 +120,22 @@ describe "UpdatesManager", -> .should.equal true describe "processUncompressedUpdates", -> + beforeEach -> + @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) + @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) + @MongoManager.backportProjectId = sinon.stub().callsArg(2) + describe "when there is fewer than one batch to send", -> beforeEach -> @updates = ["mock-update"] @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) - @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback + it "should backport the project id", -> + @MongoManager.backportProjectId + .calledWith(@project_id, @doc_id) + .should.equal true + it "should get the oldest updates", -> @RedisManager.getOldestRawUpdates .calledWith(@doc_id, @UpdatesManager.REDIS_READ_BATCH_SIZE) @@ -156,8 +164,6 @@ describe "UpdatesManager", -> @redisArray = @redisArray.slice(batchSize) callback null, updates sinon.spy @RedisManager, "getOldestRawUpdates" - @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) - @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => @callback(args...) done() From bd4bb3d3cf32ea1ab8455940024f6f1e9652e445 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 21 Mar 2014 15:57:17 +0000 Subject: [PATCH 072/549] Add in project flushing end point --- services/track-changes/app.coffee | 4 +++- .../app/coffee/HttpController.coffee | 11 +++++++-- .../coffee/helpers/TrackChangesClient.coffee | 11 +++++++-- .../HttpController/HttpControllerTests.coffee | 23 +++++++++++++++++-- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index a0b6f3d5b3..7489701712 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -10,12 +10,14 @@ app = express() app.use express.logger() -app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushUpdatesWithLock +app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff app.get "/project/:project_id/updates", HttpController.getUpdates +app.post "/project/:project_id/flush", HttpController.flushProject + app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore app.get "/status", (req, res, next) -> diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index abc2226fc5..71df05a8f8 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -4,14 +4,21 @@ RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" module.exports = HttpController = - flushUpdatesWithLock: (req, res, next = (error) ->) -> + flushDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id - logger.log doc_id: doc_id, "compressing doc history" + logger.log project_id: project_id, doc_id: doc_id, "compressing doc history" UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return next(error) if error? res.send 204 + flushProject: (req, res, next = (error) ->) -> + project_id = req.params.project_id + logger.log project_id: project_id, "compressing project history" + UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> + return next(error) if error? + res.send 204 + getDiff: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index dea3a885d5..de75cce0b0 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -4,17 +4,24 @@ rclient = require("redis").createClient() # Only works locally for now module.exports = TrackChangesClient = flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> - TrackChangesClient.flushUpdates project_id, doc_id, (error) -> + TrackChangesClient.flushDoc project_id, doc_id, (error) -> return callback(error) if error? TrackChangesClient.getCompressedUpdates doc_id, callback - flushUpdates: (project_id, doc_id, callback = (error) ->) -> + flushDoc: (project_id, doc_id, callback = (error) ->) -> request.post { url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/flush" }, (error, response, body) => response.statusCode.should.equal 204 callback(error) + flushProject: (project_id, callback = (error) ->) -> + request.post { + url: "http://localhost:3015/project/#{project_id}/flush" + }, (error, response, body) => + response.statusCode.should.equal 204 + callback(error) + getCompressedUpdates: (doc_id, callback = (error, updates) ->) -> db.docHistory .find(doc_id: ObjectId(doc_id)) diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 4caa1b83ef..f756cbb0e6 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -18,7 +18,7 @@ describe "HttpController", -> @user_id = "mock-user-123" @now = Date.now() - describe "flushUpdatesWithLock", -> + describe "flushDoc", -> beforeEach -> @req = params: @@ -27,7 +27,7 @@ describe "HttpController", -> @res = send: sinon.stub() @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - @HttpController.flushUpdatesWithLock @req, @res, @next + @HttpController.flushDoc @req, @res, @next it "should process the updates", -> @UpdatesManager.processUncompressedUpdatesWithLock @@ -37,6 +37,25 @@ describe "HttpController", -> it "should return a success code", -> @res.send.calledWith(204).should.equal true + describe "flushProject", -> + beforeEach -> + @req = + params: + project_id: @project_id + @res = + send: sinon.stub() + @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) + @HttpController.flushProject @req, @res, @next + + it "should process the updates", -> + @UpdatesManager.processUncompressedUpdatesForProject + .calledWith(@project_id) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true + + describe "getDiff", -> beforeEach -> @from = 42 From 22e3aac5b0edd08e08c729d636fc53959ee0bdd4 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 25 Mar 2014 11:40:48 +0000 Subject: [PATCH 073/549] Don't trip up over empty ops --- .../app/coffee/UpdateCompressor.coffee | 23 +++++-- .../coffee/AppendingUpdatesTests.coffee | 32 +++++++++ .../coffee/FlushingUpdatesTests.coffee | 56 ++++++++++++++++ .../UpdateCompressorTests.coffee | 66 ++++++++++++++++++- 4 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 5ba379a8d9..6d7527057f 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -2,6 +2,8 @@ strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] module.exports = UpdateCompressor = + NOOP: "noop" + # Updates come from the doc updater in format # { # op: [ { ... op1 ... }, { ... op2 ... } ] @@ -19,14 +21,23 @@ module.exports = UpdateCompressor = convertToSingleOpUpdates: (updates) -> splitUpdates = [] for update in updates - for op in update.op + if update.op.length == 0 splitUpdates.push - op: op + op: UpdateCompressor.NOOP meta: start_ts: update.meta.start_ts or update.meta.ts end_ts: update.meta.end_ts or update.meta.ts user_id: update.meta.user_id v: update.v + else + for op in update.op + splitUpdates.push + op: op + meta: + start_ts: update.meta.start_ts or update.meta.ts + end_ts: update.meta.end_ts or update.meta.ts + user_id: update.meta.user_id + v: update.v return splitUpdates concatUpdatesWithSameVersion: (updates) -> @@ -34,12 +45,14 @@ module.exports = UpdateCompressor = for update in updates lastUpdate = concattedUpdates[concattedUpdates.length - 1] if lastUpdate? and lastUpdate.v == update.v - lastUpdate.op.push update.op + lastUpdate.op.push update.op unless update.op == UpdateCompressor.NOOP else - concattedUpdates.push - op: [ update.op ] + nextUpdate = + op: [] meta: update.meta v: update.v + nextUpdate.op.push update.op unless update.op == UpdateCompressor.NOOP + concattedUpdates.push nextUpdate return concattedUpdates compressRawUpdates: (lastPreviousUpdate, rawUpdates) -> diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 7a8d465504..b86da90c19 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -194,3 +194,35 @@ describe "Appending doc ops to the history", -> expect(@updates[0].v).to.equal 3 expect(@updates[1].v).to.equal 4 + describe "when there is a no-op update", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + oneDay = 24 * 60 * 60 * 1000 + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }, { + op: [{ i: "foo", p: 3 }] + meta: { ts: Date.now() + oneDay, user_id: @user_id } + v: 4 + }], (error) => + throw error if error? + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => + throw error if error? + done() + + it "should insert the compressed no-op into mongo", -> + expect(@updates[0].op).to.deep.equal [] + + + it "should insert the compressed next update into mongo", -> + expect(@updates[1].op).to.deep.equal [{ + p: 3, i: "foo" + }] + + it "should insert the correct version numbers into mongo", -> + expect(@updates[0].v).to.equal 3 + expect(@updates[1].v).to.equal 4 diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee new file mode 100644 index 0000000000..70ee40307e --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -0,0 +1,56 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +expect = chai.expect +mongojs = require "../../../app/js/mongojs" +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" +request = require "request" +rclient = require("redis").createClient() # Only works locally for now + +TrackChangesClient = require "./helpers/TrackChangesClient" + +describe "Flushing updates", -> + describe "flushing a doc's updates", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> + throw error if error? + done() + + it "should flush the op into mongo", (done) -> + TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> + expect(updates[0].op).to.deep.equal [{ + p: 3, i: "f" + }] + done() + + describe "flushing a project's updates", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushProject @project_id, (error) -> + throw error if error? + done() + + it "should flush the op into mongo", (done) -> + TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> + expect(updates[0].op).to.deep.equal [{ + p: 3, i: "f" + }] + done() diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index ec41dcca30..3d14a84eb9 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -38,6 +38,18 @@ describe "UpdateCompressor", -> v: 43 }] + it "should return no-op updates when the op list is empty", -> + expect(@UpdateCompressor.convertToSingleOpUpdates [{ + op: [] + meta: { ts: @ts1, user_id: @user_id } + v: 42 + }]) + .to.deep.equal [{ + op: @UpdateCompressor.NOOP + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + v: 42 + }] + describe "concatUpdatesWithSameVersion", -> it "should concat updates with the same version", -> expect(@UpdateCompressor.concatUpdatesWithSameVersion [{ @@ -63,6 +75,18 @@ describe "UpdateCompressor", -> v: 43 }] + it "should turn a noop into an empty op", -> + expect(@UpdateCompressor.concatUpdatesWithSameVersion [{ + op: @UpdateCompressor.NOOP + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + v: 42 + }]) + .to.deep.equal [{ + op: [] + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + v: 42 + }] + describe "compress", -> describe "insert - insert", -> it "should append one insert to the other", -> @@ -239,4 +263,44 @@ describe "UpdateCompressor", -> v: 43 }] - + describe "noop - insert", -> + it "should leave them untouched", -> + expect(@UpdateCompressor.compressUpdates [{ + op: @UpdateCompressor.NOOP + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, i: "bar" } + meta: ts: @ts1, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: @UpdateCompressor.NOOP + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, i: "bar" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 43 + }] + + describe "noop - delete", -> + it "should leave them untouched", -> + expect(@UpdateCompressor.compressUpdates [{ + op: @UpdateCompressor.NOOP + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, d: "bar" } + meta: ts: @ts1, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: @UpdateCompressor.NOOP + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, d: "bar" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 43 + }] From 9f1efe6dcad68687e0c81e1f460aa49759a3b2ca Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 28 Mar 2014 13:05:16 +0000 Subject: [PATCH 074/549] Add in getProjectDetails to WebApiManager --- .../app/coffee/WebApiManager.coffee | 51 +++++++++++++------ .../WebApiManager/WebApiManagerTests.coffee | 44 +++++++++++++++- 2 files changed, 79 insertions(+), 16 deletions(-) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index fc4eda9729..555dc0179c 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -3,11 +3,9 @@ logger = require "logger-sharelatex" Settings = require "settings-sharelatex" module.exports = WebApiManager = - getUserInfo: (user_id, callback = (error, userInfo) ->) -> - url = "#{Settings.apis.web.url}/user/#{user_id}/personal_info" - logger.log user_id: user_id, "getting user info from web" + sendRequest: (url, callback = (error, body) ->) -> request.get { - url: url + url: "#{Settings.apis.web.url}#{url}" auth: user: Settings.apis.web.user pass: Settings.apis.web.pass @@ -16,17 +14,40 @@ module.exports = WebApiManager = if error? return callback(error) if res.statusCode >= 200 and res.statusCode < 300 - try - user = JSON.parse(body) - catch error - return callback(error) - callback null, { - id: user.id - email: user.email - first_name: user.first_name - last_name: user.last_name - } + return callback null, body else error = new Error("web returned a non-success status code: #{res.statusCode}") + callback error + + getUserInfo: (user_id, callback = (error, userInfo) ->) -> + url = "/user/#{user_id}/personal_info" + logger.log user_id: user_id, "getting user info from web" + WebApiManager.sendRequest url, (error, body) -> + if error? logger.error err: error, user_id: user_id, url: url, "error accessing web" - callback error \ No newline at end of file + return callback error + + try + user = JSON.parse(body) + catch error + return callback(error) + callback null, { + id: user.id + email: user.email + first_name: user.first_name + last_name: user.last_name + } + + getProjectDetails: (project_id, callback = (error, details) ->) -> + url = "/project/#{project_id}/details" + logger.log project_id: project_id, "getting project details from web" + WebApiManager.sendRequest url, (error, body) -> + if error? + logger.error err: error, project_id: project_id, url: url, "error accessing web" + return callback error + + try + project = JSON.parse(body) + catch error + return callback(error) + callback null, project \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee index 589dfeb7e5..aec51ab2ca 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee @@ -18,12 +18,15 @@ describe "WebApiManager", -> pass: "password" @callback = sinon.stub() @user_id = "mock-user-id" + @project_id = "mock-project-id" @user_info = email: "leo@sharelatex.com" id: @user_id first_name: "Leo" last_nane: "Lion" extra_param: "blah" + @project = + features: "mock-features" describe "getUserInfo", -> describe "successfully", -> @@ -33,7 +36,6 @@ describe "WebApiManager", -> @WebApiManager.getUserInfo @user_id, @callback it 'should get the user from the web api', -> - url = @request.get .calledWith({ url: "#{@settings.apis.web.url}/user/#{@user_id}/personal_info" @@ -65,6 +67,46 @@ describe "WebApiManager", -> @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") @WebApiManager.getUserInfo @user_id, @callback + it "should return the callback with an error", -> + @callback + .calledWith(new Error("web returned failure status code: 500")) + .should.equal true + + + describe "getProjectDetails", -> + describe "successfully", -> + beforeEach -> + @body = JSON.stringify @project + @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) + @WebApiManager.getProjectDetails @project_id, @callback + + it 'should get the project from the web api', -> + @request.get + .calledWith({ + url: "#{@settings.apis.web.url}/project/#{@project_id}/details" + auth: + user: @settings.apis.web.user + pass: @settings.apis.web.pass + sendImmediately: true + }) + .should.equal true + + it "should call the callback with the project", -> + @callback.calledWith(null, @project).should.equal true + + describe "when the web API returns an error", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) + @WebApiManager.getProjectDetails @project_id, @callback + + it "should return an error to the callback", -> + @callback.calledWith(@error).should.equal true + + describe "when the web returns a failure error code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") + @WebApiManager.getProjectDetails @project_id, @callback + it "should return the callback with an error", -> @callback .calledWith(new Error("web returned failure status code: 500")) From 953081c39d6760c2c6700a273a8086e1714fcf7c Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 28 Mar 2014 16:01:34 +0000 Subject: [PATCH 075/549] Delete updates that are older than a week, unless versioning or preserveHistory is enabled --- .../app/coffee/MongoManager.coffee | 37 ++++- .../app/coffee/UpdateTrimmer.coffee | 32 ++++ .../app/coffee/UpdatesManager.coffee | 5 +- .../track-changes/app/coffee/mongojs.coffee | 2 +- services/track-changes/package.json | 3 +- .../coffee/FlushingUpdatesTests.coffee | 116 +++++++++++--- .../coffee/GettingUpdatesTests.coffee | 28 ++-- .../coffee/helpers/MockWebApi.coffee | 14 ++ .../coffee/helpers/TrackChangesClient.coffee | 17 +++ .../MongoManager/MongoManagerTests.coffee | 53 +++++++ .../UpdateTrimmer/UpdateTrimmerTests.coffee | 142 ++++++++++++++++++ .../UpdatesManager/UpdatesManagerTests.coffee | 7 + 12 files changed, 417 insertions(+), 39 deletions(-) create mode 100644 services/track-changes/app/coffee/UpdateTrimmer.coffee create mode 100644 services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 330a903a8a..0ded71fb63 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -75,6 +75,12 @@ module.exports = MongoManager = cursor.toArray callback + deleteOldProjectUpdates: (project_id, before, callback = (error) ->) -> + db.docHistory.remove { + project_id: ObjectId(project_id) + "meta.end_ts": { $lt: before } + }, callback + backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { doc_id: ObjectId(doc_id.toString()) @@ -85,11 +91,28 @@ module.exports = MongoManager = multi: true }, callback - ensureIndices: (callback = (error) ->) -> - # For finding all updates that go into a diff for a doc - db.docHistory.ensureIndex { doc_id: 1, v: 1 }, callback - # For finding all updates that affect a project - db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, callback - # For finding updates that don't yet have a project_id and need it inserting - db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, callback + getProjectMetaData: (project_id, callback = (error, metadata) ->) -> + db.projectHistoryMetaData.find { + project_id: ObjectId(project_id.toString()) + }, (error, results) -> + return callback(error) if error? + callback null, results[0] + setProjectMetaData: (project_id, metadata, callback = (error) ->) -> + db.projectHistoryMetaData.update { + project_id: ObjectId(project_id) + }, { + $set: metadata + }, { + upsert: true + }, callback + + ensureIndices: () -> + # For finding all updates that go into a diff for a doc + db.docHistory.ensureIndex { doc_id: 1, v: 1 } + # For finding all updates that affect a project + db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 } + # For finding updates that don't yet have a project_id and need it inserting + db.docHistory.ensureIndex { doc_id: 1, project_id: 1 } + # For finding project meta-data + db.projectHistoryMetaData.ensureIndex { project_id: 1 } diff --git a/services/track-changes/app/coffee/UpdateTrimmer.coffee b/services/track-changes/app/coffee/UpdateTrimmer.coffee new file mode 100644 index 0000000000..12a673eddc --- /dev/null +++ b/services/track-changes/app/coffee/UpdateTrimmer.coffee @@ -0,0 +1,32 @@ +MongoManager = require "./MongoManager" +WebApiManager = require "./WebApiManager" +logger = require "logger-sharelatex" + +module.exports = UpdateTrimmer = + _shouldTrimUpdates: (project_id, callback = (error, shouldTrim) ->) -> + MongoManager.getProjectMetaData project_id, (error, metadata) -> + return callback(error) if error? + if metadata?.preserveHistory + return callback null, false + else + WebApiManager.getProjectDetails project_id, (error, details) -> + return callback(error) if error? + logger.log project_id: project_id, details: details, "got details" + if details?.features?.versioning + MongoManager.setProjectMetaData project_id, preserveHistory: true, (error) -> + return callback(error) if error? + callback null, false + else + callback null, true + + deleteOldProjectUpdates: (project_id, callback = (error) ->) -> + UpdateTrimmer._shouldTrimUpdates project_id, (error, shouldTrim) -> + return callback(error) if error? + if shouldTrim + logger.log project_id: project_id, "deleting old updates" + oneWeek = 7 * 24 * 60 * 60 * 1000 + before = Date.now() - oneWeek + MongoManager.deleteOldProjectUpdates project_id, before, callback + else + logger.log project_id: project_id, "not deleting old updates" + callback() \ No newline at end of file diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index c13b42d326..e0e531e957 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -3,6 +3,7 @@ RedisManager = require "./RedisManager" UpdateCompressor = require "./UpdateCompressor" LockManager = require "./LockManager" WebApiManager = require "./WebApiManager" +UpdateTrimmer = require "./UpdateTrimmer" logger = require "logger-sharelatex" async = require "async" @@ -74,7 +75,9 @@ module.exports = UpdatesManager = do (doc_id) -> jobs.push (callback) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, callback - async.parallelLimit jobs, 5, callback + async.parallelLimit jobs, 5, (error) -> + return callback(error) if error? + UpdateTrimmer.deleteOldProjectUpdates project_id, callback getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index ff56198fa7..8c2971945c 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" -db = mongojs.connect(Settings.mongo.url, ["docHistory"]) +db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData"]) module.exports = db: db ObjectId: mongojs.ObjectId diff --git a/services/track-changes/package.json b/services/track-changes/package.json index fdde105959..0eb4ba9cac 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -22,6 +22,7 @@ "grunt-contrib-coffee": "~0.10.1", "bunyan": "~0.22.1", "grunt-bunyan": "~0.5.0", - "grunt-forever": "~0.4.2" + "grunt-forever": "~0.4.2", + "timekeeper": "0.0.4" } } diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee index 70ee40307e..ee225858ee 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -9,6 +9,7 @@ request = require "request" rclient = require("redis").createClient() # Only works locally for now TrackChangesClient = require "./helpers/TrackChangesClient" +MockWebApi = require "./helpers/MockWebApi" describe "Flushing updates", -> describe "flushing a doc's updates", -> @@ -16,6 +17,7 @@ describe "Flushing updates", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } @@ -34,23 +36,103 @@ describe "Flushing updates", -> done() describe "flushing a project's updates", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } - v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushProject @project_id, (error) -> + describe "with versioning enabled", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + + @weeks = 7 * 24 * 60 * 60 * 1000 + + MockWebApi.projects[@project_id] = + features: + versioning: true + + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "g", p: 2 }] + meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id } + v: 2 + }, { + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => throw error if error? + TrackChangesClient.flushProject @project_id, (error) -> + throw error if error? + done() + + it "should not delete the old updates", (done) -> + TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> + expect(updates.length).to.equal 2 done() - it "should flush the op into mongo", (done) -> - TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates[0].op).to.deep.equal [{ - p: 3, i: "f" - }] - done() + it "should preserve history forever", (done) -> + TrackChangesClient.getProjectMetaData @project_id, (error, project) -> + expect(project.preserveHistory).to.equal true + done() + + describe "without versioning enabled", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + + @weeks = 7 * 24 * 60 * 60 * 1000 + + MockWebApi.projects[@project_id] = + features: + versioning: false + + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "g", p: 2 }] + meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id } + v: 2 + }, { + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushProject @project_id, (error) -> + throw error if error? + done() + + it "should delete the older update, but the newer update", (done) -> + TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> + expect(updates.length).to.equal 1 + expect(updates[0].op).to.deep.equal [{ i: "f", p: 3 }] + done() + + describe "without versioning enabled but with preserveHistory set to true", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + + @weeks = 7 * 24 * 60 * 60 * 1000 + + MockWebApi.projects[@project_id] = + features: + versioning: false + + TrackChangesClient.setPreserveHistoryForProject @project_id, (error) => + throw error if error? + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "g", p: 2 }] + meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id } + v: 2 + }, { + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushProject @project_id, (error) -> + throw error if error? + done() + + it "should not delete the old updates", (done) -> + TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> + expect(updates.length).to.equal 2 + done() diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index cbf93f105a..6452649fc4 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -19,7 +19,11 @@ describe "Getting updates", -> @project_id = ObjectId().toString() @minutes = 60 * 1000 - @days = 24 * 60 * @minutes + @hours = 60 * @minutes + + MockWebApi.projects[@project_id] = + features: + versioning: true MockWebApi.users[@user_id] = @user = email: "user@sharelatex.com" @@ -32,12 +36,12 @@ describe "Getting updates", -> for i in [0..9] @updates.push { op: [{ i: "a", p: 0 }] - meta: { ts: @now - (9 - i) * @days - 2 * @minutes, user_id: @user_id } + meta: { ts: @now - (9 - i) * @hours - 2 * @minutes, user_id: @user_id } v: 2 * i + 1 } @updates.push { op: [{ i: "b", p: 0 }] - meta: { ts: @now - (9 - i) * @days, user_id: @user_id } + meta: { ts: @now - (9 - i) * @hours, user_id: @user_id } v: 2 * i + 2 } @@ -76,21 +80,21 @@ describe "Getting updates", -> }, { docs: docs2 meta: - start_ts: @to - 1 * @days - 2 * @minutes - end_ts: @to - 1 * @days + start_ts: @to - 1 * @hours - 2 * @minutes + end_ts: @to - 1 * @hours users: [@user] }, { docs: docs3 meta: - start_ts: @to - 2 * @days - 2 * @minutes - end_ts: @to - 2 * @days + start_ts: @to - 2 * @hours - 2 * @minutes + end_ts: @to - 2 * @hours users: [@user] }] describe "getting updates beyond the end of the database", -> before (done) -> - TrackChangesClient.getUpdates @project_id, { before: @to - 8 * @days + 1, min_count: 30 }, (error, body) => + TrackChangesClient.getUpdates @project_id, { before: @to - 8 * @hours + 1, min_count: 30 }, (error, body) => throw error if error? @updates = body.updates done() @@ -103,14 +107,14 @@ describe "Getting updates", -> expect(@updates).to.deep.equal [{ docs: docs1 meta: - start_ts: @to - 8 * @days - 2 * @minutes - end_ts: @to - 8 * @days + start_ts: @to - 8 * @hours - 2 * @minutes + end_ts: @to - 8 * @hours users: [@user] }, { docs: docs2 meta: - start_ts: @to - 9 * @days - 2 * @minutes - end_ts: @to - 9 * @days + start_ts: @to - 9 * @hours - 2 * @minutes + end_ts: @to - 9 * @hours users: [@user] }] diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee index 04ee281541..f7cfc9e2e9 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee @@ -4,9 +4,14 @@ app = express() module.exports = MockWebApi = users: {} + projects: {} + getUser: (user_id, callback = (error) ->) -> callback null, @users[user_id] + getProject: (project_id, callback = (error, project) ->) -> + callback null, @projects[project_id] + run: () -> app.get "/user/:user_id/personal_info", (req, res, next) => @getUser req.params.user_id, (error, user) -> @@ -17,6 +22,15 @@ module.exports = MockWebApi = else res.send JSON.stringify user + app.get "/project/:project_id/details", (req, res, next) => + @getProject req.params.project_id, (error, project) -> + if error? + res.send 500 + if !project? + res.send 404 + else + res.send JSON.stringify project + app.listen 3000, (error) -> throw error if error? diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index de75cce0b0..661b0eafb8 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -28,6 +28,23 @@ module.exports = TrackChangesClient = .sort("meta.end_ts": 1) .toArray callback + getProjectMetaData: (project_id, callback = (error, updates) ->) -> + db.projectHistoryMetaData + .find { + project_id: ObjectId(project_id) + }, + (error, projects) -> + callback error, projects[0] + + setPreserveHistoryForProject: (project_id, callback = (error) ->) -> + db.projectHistoryMetaData.update { + project_id: ObjectId(project_id) + }, { + $set: { preserveHistory: true } + }, { + upsert: true + }, callback + pushRawUpdates: (project_id, doc_id, updates, callback = (error) ->) -> rclient.sadd "DocsWithHistoryOps:#{project_id}", doc_id, (error) -> return callback(error) if error? diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index b3a8fc04ca..2228147b75 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -274,3 +274,56 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true + describe "getProjectMetaData", -> + beforeEach -> + @metadata = { "mock": "metadata" } + @db.projectHistoryMetaData = + find: sinon.stub().callsArgWith(1, null, [@metadata]) + @MongoManager.getProjectMetaData @project_id, @callback + + it "should look up the meta data in the db", -> + @db.projectHistoryMetaData.find + .calledWith({ project_id: ObjectId(@project_id) }) + .should.equal true + + it "should return the metadata", -> + @callback.calledWith(null, @metadata).should.equal true + + describe "setProjectMetaData", -> + beforeEach -> + @metadata = { "mock": "metadata" } + @db.projectHistoryMetaData = + update: sinon.stub().callsArgWith(3, null, [@metadata]) + @MongoManager.setProjectMetaData @project_id, @metadata, @callback + + it "should upsert the metadata into the DB", -> + @db.projectHistoryMetaData.update + .calledWith({ + project_id: ObjectId(@project_id) + }, { + $set: @metadata + }, { + upsert: true + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "deleteOldProjectUpdates", -> + beforeEach -> + @before = Date.now() - 10000 + @db.docHistory = + remove: sinon.stub().callsArg(1) + @MongoManager.deleteOldProjectUpdates @project_id, @before, @callback + + it "should delete updates before the 'before' time", -> + @db.docHistory.remove + .calledWith({ + project_id: ObjectId(@project_id) + "meta.end_ts": { "$lt": @before } + }) + .should.equal true + + it "should return the callback", -> + @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee new file mode 100644 index 0000000000..aa0ac00f78 --- /dev/null +++ b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee @@ -0,0 +1,142 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/UpdateTrimmer.js" +SandboxedModule = require('sandboxed-module') +tk = require "timekeeper" + +describe "UpdateTrimmer", -> + beforeEach -> + @now = new Date() + tk.freeze(@now) + + @UpdateTrimmer = SandboxedModule.require modulePath, requires: + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "./WebApiManager": @WebApiManager = {} + "./MongoManager": @MongoManager = {} + + @callback = sinon.stub() + @project_id = "mock-project-id" + + afterEach -> + tk.reset() + + describe "_shouldTrimUpdates", -> + beforeEach -> + @metadata = {} + @details = + features: {} + @MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, @metadata) + @MongoManager.setProjectMetaData = sinon.stub().callsArgWith(2) + @WebApiManager.getProjectDetails = sinon.stub().callsArgWith(1, null, @details) + + describe "with preserveHistory set in the project meta data", -> + beforeEach -> + @metadata.preserveHistory = true + @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + + it "should look up the meta data", -> + @MongoManager.getProjectMetaData + .calledWith(@project_id) + .should.equal true + + it "should not look up the project details", -> + @WebApiManager.getProjectDetails + .called + .should.equal false + + it "should return false", -> + @callback.calledWith(null, false).should.equal true + + describe "without preserveHistory set in the project meta data", -> + beforeEach -> + @metadata.preserveHistory = false + + describe "when the project has the versioning feature", -> + beforeEach -> + @details.features.versioning = true + @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + + it "should look up the meta data", -> + @MongoManager.getProjectMetaData + .calledWith(@project_id) + .should.equal true + + it "should look up the project details", -> + @WebApiManager.getProjectDetails + .calledWith(@project_id) + .should.equal true + + it "should insert preserveHistory into the metadata", -> + @MongoManager.setProjectMetaData + .calledWith(@project_id, {preserveHistory: true}) + .should.equal true + + it "should return false", -> + @callback.calledWith(null, false).should.equal true + + describe "when the project does not have the versioning feature", -> + beforeEach -> + @details.features.versioning = false + @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + + it "should return true", -> + @callback.calledWith(null, true).should.equal true + + describe "without any meta data", -> + beforeEach -> + @MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, null) + + describe "when the project has the versioning feature", -> + beforeEach -> + @details.features.versioning = true + @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + + it "should insert preserveHistory into the metadata", -> + @MongoManager.setProjectMetaData + .calledWith(@project_id, {preserveHistory: true}) + .should.equal true + + it "should return false", -> + @callback.calledWith(null, false).should.equal true + + describe "when the project does not have the versioning feature", -> + beforeEach -> + @details.features.versioning = false + @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + + it "should return true", -> + @callback.calledWith(null, true).should.equal true + + describe "deleteOldProjectUpdates", -> + beforeEach -> + @oneWeek = 7 * 24 * 60 * 60 * 1000 + @MongoManager.deleteOldProjectUpdates = sinon.stub().callsArg(2) + + describe "when the updates should be trimmed", -> + beforeEach -> + @UpdateTrimmer._shouldTrimUpdates = sinon.stub().callsArgWith(1, null, true) + @UpdateTrimmer.deleteOldProjectUpdates @project_id, @callback + + it "should delete week old updates in mongo", -> + before = Date.now() - @oneWeek + @MongoManager.deleteOldProjectUpdates + .calledWith(@project_id, before) + .should.equal true + + it 'should call the callback', -> + @callback.called.should.equal true + + describe "when the updates should not be trimmed", -> + beforeEach -> + @UpdateTrimmer._shouldTrimUpdates = sinon.stub().callsArgWith(1, null, false) + @UpdateTrimmer.deleteOldProjectUpdates @project_id, @callback + + it "should not delete any updates in mongo", -> + @MongoManager.deleteOldProjectUpdates + .called + .should.equal false + + it 'should call the callback', -> + @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 6ff9f64e11..0e50ee3e20 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -13,6 +13,7 @@ describe "UpdatesManager", -> "./RedisManager" : @RedisManager = {} "./LockManager" : @LockManager = {} "./WebApiManager": @WebApiManager = {} + "./UpdateTrimmer": @UpdateTrimmer = {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } @doc_id = "doc-id-123" @project_id = "project-id-123" @@ -277,6 +278,7 @@ describe "UpdatesManager", -> @doc_ids = ["mock-id-1", "mock-id-2"] @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) + @UpdateTrimmer.deleteOldProjectUpdates = sinon.stub().callsArg(1) @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => @callback() done() @@ -292,6 +294,11 @@ describe "UpdatesManager", -> .calledWith(@project_id, doc_id) .should.equal true + it "should delete old updates for the project", -> + @UpdateTrimmer.deleteOldProjectUpdates + .calledWith(@project_id) + .should.equal true + it "should call the callback", -> @callback.called.should.equal true From eebfa6499e60f574103c730f0c3ea0aef3b136ea Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 15 Apr 2014 08:02:49 +0100 Subject: [PATCH 076/549] Be more forgiving with version mismatches --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 ++ .../test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 2 ++ 2 files changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index e0e531e957..4b8c798fa6 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -22,6 +22,7 @@ module.exports = UpdatesManager = while rawUpdates[0]? and rawUpdates[0].v <= lastCompressedUpdate.v rawUpdates.shift() + ### TODO: Restore this when errors have died down after the Sunday crash. if rawUpdates[0]? and rawUpdates[0].v != lastCompressedUpdate.v + 1 error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" @@ -30,6 +31,7 @@ module.exports = UpdatesManager = MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], () -> return callback error return + ### compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, (error) -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 0e50ee3e20..ac302da5c4 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -104,6 +104,7 @@ describe "UpdatesManager", -> .calledWith(@lastCompressedUpdate, @rawUpdates.slice(-2)) .should.equal true + ### describe "when the raw ops do not follow from the last compressed op version", -> beforeEach -> @rawUpdates = [{ v: 13, op: "mock-op-13" }] @@ -119,6 +120,7 @@ describe "UpdatesManager", -> @MongoManager.insertCompressedUpdates .calledWith(@project_id, @doc_id, [@lastCompressedUpdate]) .should.equal true + ### describe "processUncompressedUpdates", -> beforeEach -> From 24343a38cda28f600ded2e6c6da343f79e316a3d Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 1 May 2014 10:26:01 +0100 Subject: [PATCH 077/549] Add error handling of mismatched versions back in --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 -- .../test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 2 -- 2 files changed, 4 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 4b8c798fa6..e0e531e957 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -22,7 +22,6 @@ module.exports = UpdatesManager = while rawUpdates[0]? and rawUpdates[0].v <= lastCompressedUpdate.v rawUpdates.shift() - ### TODO: Restore this when errors have died down after the Sunday crash. if rawUpdates[0]? and rawUpdates[0].v != lastCompressedUpdate.v + 1 error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" @@ -31,7 +30,6 @@ module.exports = UpdatesManager = MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], () -> return callback error return - ### compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, (error) -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index ac302da5c4..0e50ee3e20 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -104,7 +104,6 @@ describe "UpdatesManager", -> .calledWith(@lastCompressedUpdate, @rawUpdates.slice(-2)) .should.equal true - ### describe "when the raw ops do not follow from the last compressed op version", -> beforeEach -> @rawUpdates = [{ v: 13, op: "mock-op-13" }] @@ -120,7 +119,6 @@ describe "UpdatesManager", -> @MongoManager.insertCompressedUpdates .calledWith(@project_id, @doc_id, [@lastCompressedUpdate]) .should.equal true - ### describe "processUncompressedUpdates", -> beforeEach -> From c835528dea5b330d7b689b6bcafc308b93d60985 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 9 May 2014 12:44:13 +0100 Subject: [PATCH 078/549] Add in metrics --- services/track-changes/app.coffee | 7 ++++++- services/track-changes/package.json | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 7489701712..f6b4c0f112 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -2,13 +2,18 @@ Settings = require "settings-sharelatex" logger = require "logger-sharelatex" logger.initialize("track-changes") +Path = require "path" +Metrics = require "metrics-sharelatex" +Metrics.initialize("track-changes") +Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) + require("./app/js/MongoManager").ensureIndices() HttpController = require "./app/js/HttpController" express = require "express" app = express() -app.use express.logger() +app.use Metrics.http.monitor(logger) app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 0eb4ba9cac..945880b526 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -7,6 +7,7 @@ "mongojs": "~0.9.11", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#master", "request": "~2.33.0", "redis": "~0.10.1" }, From 8b0b79bc322758e9f7bd1905edcf147859940392 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 May 2014 15:59:12 +0100 Subject: [PATCH 079/549] Use TTL index to auto-delete updates after a week when versioning is not enabled --- .../app/coffee/MongoManager.coffee | 18 ++++-- .../app/coffee/UpdateTrimmer.coffee | 4 +- .../app/coffee/UpdatesManager.coffee | 38 ++++++------ .../coffee/AppendingUpdatesTests.coffee | 46 +++++++++++++++ .../coffee/FlushingUpdatesTests.coffee | 1 + .../coffee/GettingADiffTests.coffee | 1 + .../coffee/RestoringVersions.coffee | 1 + .../MongoManager/MongoManagerTests.coffee | 59 ++++++++++++++----- .../UpdateTrimmer/UpdateTrimmerTests.coffee | 16 ++--- .../UpdatesManager/UpdatesManagerTests.coffee | 37 +++++++----- 10 files changed, 157 insertions(+), 64 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 0ded71fb63..7bdedcbe98 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -24,21 +24,24 @@ module.exports = MongoManager = else callback null, null - insertCompressedUpdates: (project_id, doc_id, updates, callback = (error) ->) -> + insertCompressedUpdates: (project_id, doc_id, updates, permanent, callback = (error) ->) -> jobs = [] for update in updates do (update) -> - jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, callback + jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, permanent, callback async.series jobs, callback - insertCompressedUpdate: (project_id, doc_id, update, callback = (error) ->) -> - db.docHistory.insert { + insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> + update = { doc_id: ObjectId(doc_id.toString()) project_id: ObjectId(project_id.toString()) op: update.op meta: update.meta v: update.v - }, callback + } + if temporary + update.tempCreatedAt = new Date() + db.docHistory.insert update, callback getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = @@ -116,3 +119,8 @@ module.exports = MongoManager = db.docHistory.ensureIndex { doc_id: 1, project_id: 1 } # For finding project meta-data db.projectHistoryMetaData.ensureIndex { project_id: 1 } + # TTL index for auto deleting week old temporary ops + minutes = 60 + hours = 60 * minutes + days = 24 * hours + db.docHistory.ensureIndex { tempCreatedAt: 1 }, { expireAfterSeconds: 7 * days } diff --git a/services/track-changes/app/coffee/UpdateTrimmer.coffee b/services/track-changes/app/coffee/UpdateTrimmer.coffee index 12a673eddc..649a801a43 100644 --- a/services/track-changes/app/coffee/UpdateTrimmer.coffee +++ b/services/track-changes/app/coffee/UpdateTrimmer.coffee @@ -3,7 +3,7 @@ WebApiManager = require "./WebApiManager" logger = require "logger-sharelatex" module.exports = UpdateTrimmer = - _shouldTrimUpdates: (project_id, callback = (error, shouldTrim) ->) -> + shouldTrimUpdates: (project_id, callback = (error, shouldTrim) ->) -> MongoManager.getProjectMetaData project_id, (error, metadata) -> return callback(error) if error? if metadata?.preserveHistory @@ -20,7 +20,7 @@ module.exports = UpdateTrimmer = callback null, true deleteOldProjectUpdates: (project_id, callback = (error) ->) -> - UpdateTrimmer._shouldTrimUpdates project_id, (error, shouldTrim) -> + UpdateTrimmer.shouldTrimUpdates project_id, (error, shouldTrim) -> return callback(error) if error? if shouldTrim logger.log project_id: project_id, "deleting old updates" diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index e0e531e957..0aae777695 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -8,7 +8,7 @@ logger = require "logger-sharelatex" async = require "async" module.exports = UpdatesManager = - compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, callback = (error) ->) -> + compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> length = rawUpdates.length if length == 0 return callback() @@ -27,37 +27,39 @@ module.exports = UpdatesManager = logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" # Push the update back into Mongo - catching errors at this # point is useless, we're already bailing - MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], () -> + MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], temporary, () -> return callback error return compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, (error) -> + MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, temporary,(error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" callback() REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> - MongoManager.backportProjectId project_id, doc_id, (error) -> + UpdateTrimmer.shouldTrimUpdates project_id, (error, temporary) -> return callback(error) if error? - RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> + MongoManager.backportProjectId project_id, doc_id, (error) -> return callback(error) if error? - length = rawUpdates.length - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, (error) -> + RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - RedisManager.deleteOldestRawUpdates project_id, doc_id, length, (error) -> + length = rawUpdates.length + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + RedisManager.deleteOldestRawUpdates project_id, doc_id, length, (error) -> + return callback(error) if error? + if length == UpdatesManager.REDIS_READ_BATCH_SIZE + # There might be more updates + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" + setTimeout () -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, callback + , 0 + else + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" + callback() processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> LockManager.runWithLock( diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index b86da90c19..070ac4eb66 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -9,6 +9,7 @@ request = require "request" rclient = require("redis").createClient() # Only works locally for now TrackChangesClient = require "./helpers/TrackChangesClient" +MockWebApi = require "./helpers/MockWebApi" describe "Appending doc ops to the history", -> describe "when the history does not exist yet", -> @@ -16,6 +17,7 @@ describe "Appending doc ops to the history", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } @@ -58,6 +60,7 @@ describe "Appending doc ops to the history", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }] meta: { ts: Date.now(), user_id: @user_id } @@ -139,6 +142,7 @@ describe "Appending doc ops to the history", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false updates = [] @expectedOp = [{ p:0, i: "" }] for i in [0..250] @@ -167,6 +171,7 @@ describe "Appending doc ops to the history", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false oneDay = 24 * 60 * 60 * 1000 TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }] @@ -199,6 +204,7 @@ describe "Appending doc ops to the history", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false oneDay = 24 * 60 * 60 * 1000 TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [] @@ -226,3 +232,43 @@ describe "Appending doc ops to the history", -> it "should insert the correct version numbers into mongo", -> expect(@updates[0].v).to.equal 3 expect(@updates[1].v).to.equal 4 + + describe "when the project has versioning enabled", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: true + + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => + throw error if error? + done() + + it "should not add a tempCreatedAt entry in the update in mongo", -> + expect(@updates[0].tempCreatedAt).to.be.undefined + + describe "when the project does not have versioning enabled", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false + + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ i: "f", p: 3 }] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => + throw error if error? + done() + + it "should add a tempCreatedAt entry in the update in mongo", -> + expect(@updates[0].tempCreatedAt).to.exist diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee index ee225858ee..80b6275962 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -17,6 +17,7 @@ describe "Flushing updates", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: true TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "f", p: 3 }] diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index 7561b68673..1285177bf9 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -21,6 +21,7 @@ describe "Getting a diff", -> @user_id = ObjectId().toString() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: true MockWebApi.users[@user_id] = @user = email: "user@sharelatex.com" diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index 7e3c29a0e7..ebe9a7795e 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -19,6 +19,7 @@ describe "Restoring a version", -> @user_id = ObjectId().toString() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: true minutes = 60 * 1000 diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 2228147b75..737500ee48 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -5,15 +5,20 @@ expect = chai.expect modulePath = "../../../../app/js/MongoManager.js" SandboxedModule = require('sandboxed-module') {ObjectId} = require("mongojs") +tk = require "timekeeper" describe "MongoManager", -> beforeEach -> + tk.freeze(new Date()) @MongoManager = SandboxedModule.require modulePath, requires: "./mongojs" : { db: @db = {}, ObjectId: ObjectId } @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() + afterEach -> + tk.reset() + describe "getLastCompressedUpdate", -> beforeEach -> @update = "mock-update" @@ -101,34 +106,56 @@ describe "MongoManager", -> @update = { op: "op", meta: "meta", v: "v"} @db.docHistory = insert: sinon.stub().callsArg(1) - @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, @callback - it "should insert the update", -> - @db.docHistory.insert - .calledWith({ - project_id: ObjectId(@project_id), - doc_id: ObjectId(@doc_id), - op: @update.op, - meta: @update.meta, - v: @update.v - }) - .should.equal true + describe "temporarly", -> + beforeEach -> + @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, true, @callback - it "should call the callback", -> - @callback.called.should.equal true + it "should insert the update with a tempCreatedAt field", -> + @db.docHistory.insert + .calledWith({ + project_id: ObjectId(@project_id), + doc_id: ObjectId(@doc_id), + op: @update.op, + meta: @update.meta, + v: @update.v + tempCreatedAt: new Date() + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "permanenty", -> + beforeEach -> + @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, false, @callback + + it "should insert the update with no tempCreatedAt field", -> + @db.docHistory.insert + .calledWith({ + project_id: ObjectId(@project_id), + doc_id: ObjectId(@doc_id), + op: @update.op, + meta: @update.meta, + v: @update.v + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true describe "insertCompressedUpdates", -> beforeEach (done) -> @updates = [ "mock-update-1", "mock-update-2" ] - @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(3) - @MongoManager.insertCompressedUpdates @project_id, @doc_id, @updates, (args...) => + @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(4) + @MongoManager.insertCompressedUpdates @project_id, @doc_id, @updates, @temporary = true, (args...) => @callback(args...) done() it "should insert each update", -> for update in @updates @MongoManager.insertCompressedUpdate - .calledWith(@project_id, @doc_id, update) + .calledWith(@project_id, @doc_id, update, @temporary) .should.equal true it "should call the callback", -> diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee index aa0ac00f78..10b0599c23 100644 --- a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee @@ -22,7 +22,7 @@ describe "UpdateTrimmer", -> afterEach -> tk.reset() - describe "_shouldTrimUpdates", -> + describe "shouldTrimUpdates", -> beforeEach -> @metadata = {} @details = @@ -34,7 +34,7 @@ describe "UpdateTrimmer", -> describe "with preserveHistory set in the project meta data", -> beforeEach -> @metadata.preserveHistory = true - @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + @UpdateTrimmer.shouldTrimUpdates @project_id, @callback it "should look up the meta data", -> @MongoManager.getProjectMetaData @@ -56,7 +56,7 @@ describe "UpdateTrimmer", -> describe "when the project has the versioning feature", -> beforeEach -> @details.features.versioning = true - @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + @UpdateTrimmer.shouldTrimUpdates @project_id, @callback it "should look up the meta data", -> @MongoManager.getProjectMetaData @@ -79,7 +79,7 @@ describe "UpdateTrimmer", -> describe "when the project does not have the versioning feature", -> beforeEach -> @details.features.versioning = false - @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + @UpdateTrimmer.shouldTrimUpdates @project_id, @callback it "should return true", -> @callback.calledWith(null, true).should.equal true @@ -91,7 +91,7 @@ describe "UpdateTrimmer", -> describe "when the project has the versioning feature", -> beforeEach -> @details.features.versioning = true - @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + @UpdateTrimmer.shouldTrimUpdates @project_id, @callback it "should insert preserveHistory into the metadata", -> @MongoManager.setProjectMetaData @@ -104,7 +104,7 @@ describe "UpdateTrimmer", -> describe "when the project does not have the versioning feature", -> beforeEach -> @details.features.versioning = false - @UpdateTrimmer._shouldTrimUpdates @project_id, @callback + @UpdateTrimmer.shouldTrimUpdates @project_id, @callback it "should return true", -> @callback.calledWith(null, true).should.equal true @@ -116,7 +116,7 @@ describe "UpdateTrimmer", -> describe "when the updates should be trimmed", -> beforeEach -> - @UpdateTrimmer._shouldTrimUpdates = sinon.stub().callsArgWith(1, null, true) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, true) @UpdateTrimmer.deleteOldProjectUpdates @project_id, @callback it "should delete week old updates in mongo", -> @@ -130,7 +130,7 @@ describe "UpdateTrimmer", -> describe "when the updates should not be trimmed", -> beforeEach -> - @UpdateTrimmer._shouldTrimUpdates = sinon.stub().callsArgWith(1, null, false) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, false) @UpdateTrimmer.deleteOldProjectUpdates @project_id, @callback it "should not delete any updates in mongo", -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 0e50ee3e20..12554c817b 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -18,13 +18,14 @@ describe "UpdatesManager", -> @doc_id = "doc-id-123" @project_id = "project-id-123" @callback = sinon.stub() + @temporary = "temp-mock" describe "compressAndSaveRawUpdates", -> describe "when there are no raw ops", -> beforeEach -> @MongoManager.popLastCompressedUpdate = sinon.stub() @MongoManager.insertCompressedUpdates = sinon.stub() - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, [], @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, [], @temporary, @callback it "should not need to access the database", -> @MongoManager.popLastCompressedUpdate.called.should.equal false @@ -39,9 +40,9 @@ describe "UpdatesManager", -> @compressedUpdates = { v: 13, op: "compressed-op-12" } @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(3) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback it "should try to pop the last compressed op", -> @MongoManager.popLastCompressedUpdate @@ -55,7 +56,7 @@ describe "UpdatesManager", -> it "should save the compressed ops", -> @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @compressedUpdates) + .calledWith(@project_id, @doc_id, @compressedUpdates, @temporary) .should.equal true it "should call the callback", -> @@ -67,13 +68,13 @@ describe "UpdatesManager", -> @compressedUpdates = { v: 13, op: "compressed-op-12" } @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(3) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) describe "when the raw ops start where the existing history ends", -> beforeEach -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback it "should try to pop the last compressed op", -> @MongoManager.popLastCompressedUpdate @@ -87,7 +88,7 @@ describe "UpdatesManager", -> it "should save the compressed ops", -> @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @compressedUpdates) + .calledWith(@project_id, @doc_id, @compressedUpdates, @temporary) .should.equal true it "should call the callback", -> @@ -97,7 +98,7 @@ describe "UpdatesManager", -> beforeEach -> @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback it "should only compress the more recent raw ops", -> @UpdateCompressor.compressRawUpdates @@ -107,7 +108,7 @@ describe "UpdatesManager", -> describe "when the raw ops do not follow from the last compressed op version", -> beforeEach -> @rawUpdates = [{ v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @callback + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback it "should call the callback with an error", -> @callback @@ -117,14 +118,15 @@ describe "UpdatesManager", -> it "should put the popped update back into mongo", -> @MongoManager.insertCompressedUpdates.calledOnce.should.equal true @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, [@lastCompressedUpdate]) + .calledWith(@project_id, @doc_id, [@lastCompressedUpdate], @temporary) .should.equal true describe "processUncompressedUpdates", -> beforeEach -> - @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(3) + @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(4) @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) @MongoManager.backportProjectId = sinon.stub().callsArg(2) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") describe "when there is fewer than one batch to send", -> beforeEach -> @@ -132,6 +134,11 @@ describe "UpdatesManager", -> @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback + it "should check if the updates are temporary", -> + @UpdateTrimmer.shouldTrimUpdates + .calledWith(@project_id) + .should.equal true + it "should backport the project id", -> @MongoManager.backportProjectId .calledWith(@project_id, @doc_id) @@ -144,7 +151,7 @@ describe "UpdatesManager", -> it "should compress and save the updates", -> @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates) + .calledWith(@project_id, @doc_id, @updates, @temporary) .should.equal true it "should delete the batch of uncompressed updates that was just processed", -> @@ -174,13 +181,13 @@ describe "UpdatesManager", -> it "should compress and save the updates in batches", -> @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates.slice(0,2)) + .calledWith(@project_id, @doc_id, @updates.slice(0,2), @temporary) .should.equal true @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates.slice(2,4)) + .calledWith(@project_id, @doc_id, @updates.slice(2,4), @temporary) .should.equal true @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates.slice(4,5)) + .calledWith(@project_id, @doc_id, @updates.slice(4,5), @temporary) .should.equal true it "should delete the batches of uncompressed updates", -> From 29ad81c134047286cd884145b39714a84381f4c5 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 May 2014 16:41:14 +0100 Subject: [PATCH 080/549] Use expiresAt rather than tempCreatedAt --- services/track-changes/app/coffee/MongoManager.coffee | 11 ++++++----- .../acceptance/coffee/AppendingUpdatesTests.coffee | 8 ++++---- .../unit/coffee/MongoManager/MongoManagerTests.coffee | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 7bdedcbe98..536bf77168 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -40,7 +40,11 @@ module.exports = MongoManager = v: update.v } if temporary - update.tempCreatedAt = new Date() + seconds = 1000 + minutes = 60 * seconds + hours = 60 * minutes + days = 24 * hours + update.expiresAt = new Date(Date.now() + 7 * days) db.docHistory.insert update, callback getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> @@ -120,7 +124,4 @@ module.exports = MongoManager = # For finding project meta-data db.projectHistoryMetaData.ensureIndex { project_id: 1 } # TTL index for auto deleting week old temporary ops - minutes = 60 - hours = 60 * minutes - days = 24 * hours - db.docHistory.ensureIndex { tempCreatedAt: 1 }, { expireAfterSeconds: 7 * days } + db.docHistory.ensureIndex { tempCreatedAt: 1 }, { expireAfterSeconds: 0 } diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 070ac4eb66..a82767e7c5 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -250,8 +250,8 @@ describe "Appending doc ops to the history", -> throw error if error? done() - it "should not add a tempCreatedAt entry in the update in mongo", -> - expect(@updates[0].tempCreatedAt).to.be.undefined + it "should not add a expiresAt entry in the update in mongo", -> + expect(@updates[0].expiresAt).to.be.undefined describe "when the project does not have versioning enabled", -> before (done) -> @@ -270,5 +270,5 @@ describe "Appending doc ops to the history", -> throw error if error? done() - it "should add a tempCreatedAt entry in the update in mongo", -> - expect(@updates[0].tempCreatedAt).to.exist + it "should add a expiresAt entry in the update in mongo", -> + expect(@updates[0].expiresAt).to.exist diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 737500ee48..186aac8a62 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -111,7 +111,7 @@ describe "MongoManager", -> beforeEach -> @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, true, @callback - it "should insert the update with a tempCreatedAt field", -> + it "should insert the update with a expiresAt field one week away", -> @db.docHistory.insert .calledWith({ project_id: ObjectId(@project_id), @@ -119,7 +119,7 @@ describe "MongoManager", -> op: @update.op, meta: @update.meta, v: @update.v - tempCreatedAt: new Date() + expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) }) .should.equal true @@ -130,7 +130,7 @@ describe "MongoManager", -> beforeEach -> @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, false, @callback - it "should insert the update with no tempCreatedAt field", -> + it "should insert the update with no expiresAt field", -> @db.docHistory.insert .calledWith({ project_id: ObjectId(@project_id), From 6a371c267fb5b09c66e36e44e0ac8b0b5959d654 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 May 2014 16:41:40 +0100 Subject: [PATCH 081/549] Fix indexes --- services/track-changes/app/coffee/MongoManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 536bf77168..5bcca5d467 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -124,4 +124,4 @@ module.exports = MongoManager = # For finding project meta-data db.projectHistoryMetaData.ensureIndex { project_id: 1 } # TTL index for auto deleting week old temporary ops - db.docHistory.ensureIndex { tempCreatedAt: 1 }, { expireAfterSeconds: 0 } + db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0 } From cc962e1c44aaa4d3bf56dd21c53acb052fc65523 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 May 2014 17:00:30 +0100 Subject: [PATCH 082/549] Add indexes in background --- services/track-changes/app/coffee/MongoManager.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 5bcca5d467..1fc13ca299 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -116,12 +116,12 @@ module.exports = MongoManager = ensureIndices: () -> # For finding all updates that go into a diff for a doc - db.docHistory.ensureIndex { doc_id: 1, v: 1 } + db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } # For finding all updates that affect a project - db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 } + db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding updates that don't yet have a project_id and need it inserting - db.docHistory.ensureIndex { doc_id: 1, project_id: 1 } + db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data - db.projectHistoryMetaData.ensureIndex { project_id: 1 } + db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } # TTL index for auto deleting week old temporary ops - db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0 } + db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true } From eb7bcc6922e3deb6c499a81a73451175f32349a8 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 29 May 2014 15:37:16 +0100 Subject: [PATCH 083/549] Remove old history deletion method --- services/track-changes/app.coffee | 2 -- .../app/coffee/MongoManager.coffee | 6 ---- .../app/coffee/UpdateTrimmer.coffee | 11 ------- .../app/coffee/UpdatesManager.coffee | 4 +-- .../coffee/FlushingUpdatesTests.coffee | 13 ++++---- .../MongoManager/MongoManagerTests.coffee | 18 ----------- .../UpdateTrimmer/UpdateTrimmerTests.coffee | 31 ------------------- .../UpdatesManager/UpdatesManagerTests.coffee | 6 ---- 8 files changed, 7 insertions(+), 84 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index f6b4c0f112..779351975c 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -7,8 +7,6 @@ Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) -require("./app/js/MongoManager").ensureIndices() - HttpController = require "./app/js/HttpController" express = require "express" app = express() diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1fc13ca299..f2f664dfd6 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -82,12 +82,6 @@ module.exports = MongoManager = cursor.toArray callback - deleteOldProjectUpdates: (project_id, before, callback = (error) ->) -> - db.docHistory.remove { - project_id: ObjectId(project_id) - "meta.end_ts": { $lt: before } - }, callback - backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { doc_id: ObjectId(doc_id.toString()) diff --git a/services/track-changes/app/coffee/UpdateTrimmer.coffee b/services/track-changes/app/coffee/UpdateTrimmer.coffee index 649a801a43..83a08e36fa 100644 --- a/services/track-changes/app/coffee/UpdateTrimmer.coffee +++ b/services/track-changes/app/coffee/UpdateTrimmer.coffee @@ -19,14 +19,3 @@ module.exports = UpdateTrimmer = else callback null, true - deleteOldProjectUpdates: (project_id, callback = (error) ->) -> - UpdateTrimmer.shouldTrimUpdates project_id, (error, shouldTrim) -> - return callback(error) if error? - if shouldTrim - logger.log project_id: project_id, "deleting old updates" - oneWeek = 7 * 24 * 60 * 60 * 1000 - before = Date.now() - oneWeek - MongoManager.deleteOldProjectUpdates project_id, before, callback - else - logger.log project_id: project_id, "not deleting old updates" - callback() \ No newline at end of file diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 0aae777695..1e2d540752 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -77,9 +77,7 @@ module.exports = UpdatesManager = do (doc_id) -> jobs.push (callback) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, callback - async.parallelLimit jobs, 5, (error) -> - return callback(error) if error? - UpdateTrimmer.deleteOldProjectUpdates project_id, callback + async.parallelLimit jobs, 5, callback getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee index 80b6275962..63ddca4da9 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -63,9 +63,9 @@ describe "Flushing updates", -> throw error if error? done() - it "should not delete the old updates", (done) -> + it "should not mark the updates for deletion", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates.length).to.equal 2 + expect(updates[0].expiresAt).to.not.exist done() it "should preserve history forever", (done) -> @@ -99,10 +99,9 @@ describe "Flushing updates", -> throw error if error? done() - it "should delete the older update, but the newer update", (done) -> + it "should mark the updates for deletion", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates.length).to.equal 1 - expect(updates[0].op).to.deep.equal [{ i: "f", p: 3 }] + expect(updates[0].expiresAt).to.exist done() describe "without versioning enabled but with preserveHistory set to true", -> @@ -133,7 +132,7 @@ describe "Flushing updates", -> throw error if error? done() - it "should not delete the old updates", (done) -> + it "should not mark the updates for deletion", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates.length).to.equal 2 + expect(updates[0].expiresAt).to.not.exist done() diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 186aac8a62..fa1ec9232f 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -336,21 +336,3 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true - - describe "deleteOldProjectUpdates", -> - beforeEach -> - @before = Date.now() - 10000 - @db.docHistory = - remove: sinon.stub().callsArg(1) - @MongoManager.deleteOldProjectUpdates @project_id, @before, @callback - - it "should delete updates before the 'before' time", -> - @db.docHistory.remove - .calledWith({ - project_id: ObjectId(@project_id) - "meta.end_ts": { "$lt": @before } - }) - .should.equal true - - it "should return the callback", -> - @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee index 10b0599c23..afcfe53513 100644 --- a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee @@ -109,34 +109,3 @@ describe "UpdateTrimmer", -> it "should return true", -> @callback.calledWith(null, true).should.equal true - describe "deleteOldProjectUpdates", -> - beforeEach -> - @oneWeek = 7 * 24 * 60 * 60 * 1000 - @MongoManager.deleteOldProjectUpdates = sinon.stub().callsArg(2) - - describe "when the updates should be trimmed", -> - beforeEach -> - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, true) - @UpdateTrimmer.deleteOldProjectUpdates @project_id, @callback - - it "should delete week old updates in mongo", -> - before = Date.now() - @oneWeek - @MongoManager.deleteOldProjectUpdates - .calledWith(@project_id, before) - .should.equal true - - it 'should call the callback', -> - @callback.called.should.equal true - - describe "when the updates should not be trimmed", -> - beforeEach -> - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, false) - @UpdateTrimmer.deleteOldProjectUpdates @project_id, @callback - - it "should not delete any updates in mongo", -> - @MongoManager.deleteOldProjectUpdates - .called - .should.equal false - - it 'should call the callback', -> - @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 12554c817b..fdc19360b7 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -285,7 +285,6 @@ describe "UpdatesManager", -> @doc_ids = ["mock-id-1", "mock-id-2"] @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) - @UpdateTrimmer.deleteOldProjectUpdates = sinon.stub().callsArg(1) @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => @callback() done() @@ -301,11 +300,6 @@ describe "UpdatesManager", -> .calledWith(@project_id, doc_id) .should.equal true - it "should delete old updates for the project", -> - @UpdateTrimmer.deleteOldProjectUpdates - .calledWith(@project_id) - .should.equal true - it "should call the callback", -> @callback.called.should.equal true From 556afc87766f48862c659de9a64d4a84f7ebfece Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 6 Jun 2014 13:00:28 +0100 Subject: [PATCH 084/549] Rename settings to defaults --- .../{settings.development.coffee => settings.defaults.coffee} | 2 ++ 1 file changed, 2 insertions(+) rename services/track-changes/config/{settings.development.coffee => settings.defaults.coffee} (85%) diff --git a/services/track-changes/config/settings.development.coffee b/services/track-changes/config/settings.defaults.coffee similarity index 85% rename from services/track-changes/config/settings.development.coffee rename to services/track-changes/config/settings.defaults.coffee index f79f657fa6..8fdfa918e8 100755 --- a/services/track-changes/config/settings.development.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -8,6 +8,8 @@ module.exports = apis: documentupdater: url: "http://localhost:3003" + docstore: + url: "http://localhost:3016" web: url: "http://localhost:3000" user: "sharelatex" From e7b4359c62c8ff268c0b54aec89b6579a6f6314a Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 19 Aug 2014 14:15:44 +0100 Subject: [PATCH 085/549] Lock down module versions --- services/track-changes/package.json | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 945880b526..626e167895 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -1,13 +1,18 @@ { "name": "history-sharelatex", "version": "0.0.1", + "description": "An API for saving and compressing individual document updates into a browsable history", + "repository": { + "type": "git", + "url": "https://github.com/sharelatex/track-changes-sharelatex.git" + }, "dependencies": { "async": "~0.2.10", "express": "3.3.5", "mongojs": "~0.9.11", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#master", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#master", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", "redis": "~0.10.1" }, From f0e5b914a6e7db5b15e1744da177ee6f29e1aee4 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 19 Aug 2014 14:17:11 +0100 Subject: [PATCH 086/549] Release version 0.1.0 --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 626e167895..025ae19288 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -1,6 +1,6 @@ { "name": "history-sharelatex", - "version": "0.0.1", + "version": "0.1.0", "description": "An API for saving and compressing individual document updates into a browsable history", "repository": { "type": "git", From a0bb289fd334a56dbfd475dfbbf08ad5f510cd9e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 26 Sep 2014 17:21:33 +0100 Subject: [PATCH 087/549] works with sentinal v1 --- services/track-changes/app/coffee/LockManager.coffee | 6 ++---- services/track-changes/app/coffee/RedisManager.coffee | 6 ++---- services/track-changes/package.json | 1 + 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 5f71a336fb..1b81687c36 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -1,8 +1,6 @@ Settings = require "settings-sharelatex" -redis = require('redis') -redisConf = Settings.redis?.web or {host: "localhost", port: 6379} -rclient = redis.createClient(redisConf.port, redisConf.host) -rclient.auth(redisConf.password) +redis = require("redis-sharelatex") +rclient = redis.createClient(Settings.redis.web) module.exports = LockManager = LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 676259d311..eec6ce5086 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -1,8 +1,6 @@ Settings = require "settings-sharelatex" -redis = require('redis') -redisConf = Settings.redis?.web or {host: "localhost", port: 6379} -rclient = redis.createClient(redisConf.port, redisConf.host) -rclient.auth(redisConf.password) +redis = require("redis-sharelatex") +rclient = redis.createClient(Settings.redis.web) rawUpdatesKey = (doc_id) -> "UncompressedHistoryOps:#{doc_id}" docsWithHistoryOpsKey = (project_id) -> "DocsWithHistoryOps:#{project_id}" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 945880b526..252fe6cdc6 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -9,6 +9,7 @@ "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#master", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#master", "request": "~2.33.0", + "redis-sharelatex": "0.0.1", "redis": "~0.10.1" }, "devDependencies": { From 1452eb457210d71615c44a7a57a7a1ed27c6476e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 26 Sep 2014 17:51:41 +0100 Subject: [PATCH 088/549] fixed tests --- .../test/unit/coffee/LockManager/LockManagerTests.coffee | 8 ++++++-- .../unit/coffee/RedisManager/RedisManagerTests.coffee | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index e5caea82c1..9ea9c81f15 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -7,11 +7,15 @@ SandboxedModule = require('sandboxed-module') describe "LockManager", -> beforeEach -> + @Settings = + redis: + web:{} @LockManager = SandboxedModule.require modulePath, requires: - "redis": + "redis-sharelatex": createClient: () => @rclient = auth: sinon.stub() - "settings-sharelatex": @Settings = {} + "settings-sharelatex": @Settings + @key = "lock-key" @callback = sinon.stub() diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee index 5efb9768f6..7293892be0 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -8,11 +8,13 @@ SandboxedModule = require('sandboxed-module') describe "RedisManager", -> beforeEach -> @RedisManager = SandboxedModule.require modulePath, requires: - "redis" : + "redis-sharelatex" : createClient: () => @rclient = auth: sinon.stub() multi: () => @rclient - "settings-sharelatex": {} + "settings-sharelatex": + redis: + web:{} @doc_id = "doc-id-123" @project_id = "project-id-123" @batchSize = 100 From 3f4452e6afc5eb210090a65782968c346c027ac6 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 29 Sep 2014 11:33:40 +0100 Subject: [PATCH 089/549] don't run acceptence tests on travis --- services/track-changes/.travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml index 6adc08643a..ffa7d7f9f8 100644 --- a/services/track-changes/.travis.yml +++ b/services/track-changes/.travis.yml @@ -15,7 +15,6 @@ before_script: script: - grunt test:unit - - grunt test:acceptance services: - redis-server From 66bba0dec9c56ff898ebbfce3d3cd7a0692a8790 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 29 Sep 2014 11:41:09 +0100 Subject: [PATCH 090/549] bump redis-sharelatex --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e76335ac7d..b600532a92 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -14,7 +14,7 @@ "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", - "redis-sharelatex": "0.0.1", + "redis-sharelatex": "0.0.3", "redis": "~0.10.1" }, "devDependencies": { From affee1ffe6c7f57772d31f70bf97f68eaaf50c73 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 29 Sep 2014 16:58:22 +0100 Subject: [PATCH 091/549] changed redis-sharelatex to use 0.0.4 and update minor versions --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b600532a92..4b03ee789a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -14,7 +14,7 @@ "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", - "redis-sharelatex": "0.0.3", + "redis-sharelatex": "~0.0.4", "redis": "~0.10.1" }, "devDependencies": { From 3328acc82ba39d2cf20af6d6e9cd8b488f28f380 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 7 Oct 2014 08:33:00 +0100 Subject: [PATCH 092/549] bump sharelatex-redis --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b600532a92..4b03ee789a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -14,7 +14,7 @@ "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", - "redis-sharelatex": "0.0.3", + "redis-sharelatex": "~0.0.4", "redis": "~0.10.1" }, "devDependencies": { From 715b0df4a1bd7e53ecf8a691543c46cbcba52cbb Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 5 Feb 2015 16:36:41 +0000 Subject: [PATCH 093/549] prototype for expanding packs --- .../app/coffee/MongoManager.coffee | 97 +++++++++++++++---- services/track-changes/package.json | 3 +- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index f2f664dfd6..da7cc08001 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,7 +1,9 @@ {db, ObjectId} = require "./mongojs" async = require "async" +_ = require "underscore" module.exports = MongoManager = + # only used in this module getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> db.docHistory .find(doc_id: ObjectId(doc_id.toString())) @@ -11,9 +13,11 @@ module.exports = MongoManager = return callback(error) if error? return callback null, compressedUpdates[0] or null + # only used in this module deleteCompressedUpdate: (id, callback = (error) ->) -> db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) + # used in UpdatesManager popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? @@ -24,6 +28,7 @@ module.exports = MongoManager = else callback null, null + # used in UpdatesManager insertCompressedUpdates: (project_id, doc_id, updates, permanent, callback = (error) ->) -> jobs = [] for update in updates @@ -45,8 +50,78 @@ module.exports = MongoManager = hours = 60 * minutes days = 24 * hours update.expiresAt = new Date(Date.now() + 7 * days) + # may need to roll over a pack here if we are inserting packs db.docHistory.insert update, callback + _findResults: (param, query, limit, callback) -> + sort = {} + sort[param] = -1; + cursor = db.docHistory + .find( query ) + .sort( sort ) + + if limit? + cursor.limit(limit) + + rangeQuery = query[param] + extraQuery = _.clone(query) + if rangeQuery?['$gte']? + extraQuery[param] = {'$lt' : rangeQuery['$gte']} + else if rangeQuery?['$gt'] + extraQuery[param] = {'$lte' : rangeQuery['$gt']} + + filterFn = (item) -> + #console.log 'filter', item, rangeQuery + return false if rangeQuery?['$gte']? && item[param] < rangeQuery['$gte'] + return false if rangeQuery?['$lte']? && item[param] > rangeQuery['$lte'] + return false if rangeQuery?['$lt']? && item[param] >= rangeQuery['$lt'] + return false if rangeQuery?['$gt']? && item[param] <= rangeQuery['$gt'] + #console.log 'accepted' + return true + + # need to support limit here + + cursor.toArray (err, updates) -> + console.log 'query=', query, 'UPDATES=', updates + all = MongoManager._unpackResults(updates).filter(filterFn) + if all.length == 0 + # need an extra result set + console.log 'extraQuery', extraQuery + extra = db.docHistory + .find(extraQuery) + .sort(sort) + .limit(1) + extra.toArray (err, updates2) -> + all2 = MongoManager._unpackResults(updates2).filter(filterFn) + console.log 'got extra', all2 + callback err, all2 + return + if err? + callback err, updates + else + callback err, all + + _unpackResults: (updates) -> + result = [] + # iterate over the updates + # if it's a pack, expand it into ops and insert it into the array at that point + updates.forEach (item) -> + if item.pack? + all = MongoManager._explodePackToOps item + result = result.concat all + else + result.push item + return result + + _explodePackToOps: (packObj) -> + doc_id = packObj.doc_id + project_id = packObj.project_id + result = packObj.pack.map (item) -> + item.doc_id = doc_id + item.project_id = project_id + item + return result.reverse() + getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) @@ -57,14 +132,7 @@ module.exports = MongoManager = query["v"] ||= {} query["v"]["$lte"] = options.to - cursor = db.docHistory - .find( query ) - .sort( v: -1 ) - - if options.limit? - cursor.limit(options.limit) - - cursor.toArray callback + MongoManager._findResults('v', query, options.limit, callback) getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> query = @@ -73,14 +141,7 @@ module.exports = MongoManager = if options.before? query["meta.end_ts"] = { $lt: options.before } - cursor = db.docHistory - .find( query ) - .sort( "meta.end_ts": -1 ) - - if options.limit? - cursor.limit(options.limit) - - cursor.toArray callback + MongoManager._findResults('meta.end_ts', query, options.limit, callback) backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { @@ -109,9 +170,9 @@ module.exports = MongoManager = }, callback ensureIndices: () -> - # For finding all updates that go into a diff for a doc + # For finding all updates that go into a diff for a doc (getLastCompressedUpdate, getDocUpdates v > from && v < to) db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } - # For finding all updates that affect a project + # For finding all updates that affect a project (getProjectUpdates meta.end_ts < before db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 4b03ee789a..e6f006d739 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -15,7 +15,8 @@ "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.4", - "redis": "~0.10.1" + "redis": "~0.10.1", + "underscore": "~1.7.0" }, "devDependencies": { "chai": "~1.9.0", From 8e810bab14be0bda28070993c5f41be81c9b8268 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 5 Feb 2015 16:37:06 +0000 Subject: [PATCH 094/549] report error for inconsistent history results --- services/track-changes/app/coffee/UpdatesManager.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 1e2d540752..92357fc2dc 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -137,6 +137,10 @@ module.exports = UpdatesManager = # if updates? and updates.length > 0 nextBeforeTimestamp = updates[updates.length - 1].meta.end_ts + if nextBeforeTimestamp >= before + error = new Error("history order is broken") + logger.error err: error, project_id:project_id, nextBeforeTimestamp: nextBeforeTimestamp, before:before, "error in project history" + return callback(error) else nextBeforeTimestamp = null From e47476369af8dc65de54abb9734b1cef0ec62e5b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 6 Feb 2015 15:04:46 +0000 Subject: [PATCH 095/549] clean up docHistory pack decoding --- .../app/coffee/MongoManager.coffee | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index da7cc08001..494ed04b6e 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -69,37 +69,47 @@ module.exports = MongoManager = extraQuery[param] = {'$lt' : rangeQuery['$gte']} else if rangeQuery?['$gt'] extraQuery[param] = {'$lte' : rangeQuery['$gt']} + else + delete extraQuery[param] filterFn = (item) -> - #console.log 'filter', item, rangeQuery return false if rangeQuery?['$gte']? && item[param] < rangeQuery['$gte'] return false if rangeQuery?['$lte']? && item[param] > rangeQuery['$lte'] return false if rangeQuery?['$lt']? && item[param] >= rangeQuery['$lt'] return false if rangeQuery?['$gt']? && item[param] <= rangeQuery['$gt'] - #console.log 'accepted' return true - # need to support limit here - - cursor.toArray (err, updates) -> - console.log 'query=', query, 'UPDATES=', updates - all = MongoManager._unpackResults(updates).filter(filterFn) - if all.length == 0 + needMore = false + cursor.toArray (err, result) -> + unpackedSet = MongoManager._unpackResults(result) + updates = unpackedSet.filter(filterFn) + updates = updates.slice(0, limit) if limit? + last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null + if limit? && updates.length == limit + needMore = false + else if extraQuery[param]? && last? && filterFn(last) + needMore = true + else if extraQuery[param]? && updates.length == 0 + needMore = true + if needMore # need an extra result set - console.log 'extraQuery', extraQuery extra = db.docHistory .find(extraQuery) .sort(sort) .limit(1) - extra.toArray (err, updates2) -> - all2 = MongoManager._unpackResults(updates2).filter(filterFn) - console.log 'got extra', all2 - callback err, all2 + extra.toArray (err, result2) -> + if err? + return callback err, updates + else + extraSet = MongoManager._unpackResults(result2).filter(filterFn) + updates = updates.concat extraSet + updates = updates.slice(0, limit) if limit? + callback err, updates return if err? - callback err, updates + callback err, result else - callback err, all + callback err, updates _unpackResults: (updates) -> result = [] @@ -121,7 +131,7 @@ module.exports = MongoManager = item.project_id = project_id item return result.reverse() - + getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) From f50f091fc698483a2832e20d1c39735f348de7fd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 6 Feb 2015 16:59:09 +0000 Subject: [PATCH 096/549] added comments --- .../app/coffee/MongoManager.coffee | 91 +++++++++++++++---- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 494ed04b6e..efcefe9286 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -53,25 +53,58 @@ module.exports = MongoManager = # may need to roll over a pack here if we are inserting packs db.docHistory.insert update, callback + # The following function implements a method like a mongo find, but + # which expands any documents containing a 'pack' field into + # multiple values + # + # e.g. a single update looks like + # + # { + # "doc_id" : 549dae9e0a2a615c0c7f0c98, + # "project_id" : 549dae9c0a2a615c0c7f0c8c, + # "op" : [ {"p" : 6981, "d" : "?" } ], + # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, + # "v" : 17082 + # } + # + # and a pack looks like this + # + # { + # "doc_id" : 549dae9e0a2a615c0c7f0c98, + # "project_id" : 549dae9c0a2a615c0c7f0c8c, + # "pack" : [ D1, D2, D3, ....], + # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, + # "v" : 17082 + # } + # + # where D1, D2, D3, .... are single updates stripped of their + # doc_id and project_id fields (which are the same for all the + # updates in the pack). The meta and v fields of the pack itself + # are those of the first entry in the pack D1 (this makes it + # possible to treat packs and single updates in the same way). + _findResults: (param, query, limit, callback) -> + # param - the field used to select and sort ops within a range, + # either 'v' or 'meta.end_ts' + # query - the mongo query selector, includes both the doc_id/project_id and + # the range on v or meta.end_ts + # limit - the mongo limit, we need to apply it after unpacking any + # packs + sort = {} sort[param] = -1; cursor = db.docHistory .find( query ) .sort( sort ) - + # if we have packs, we will trim the results more later after expanding them if limit? cursor.limit(limit) + # take the part of the query which selects the range over the parameter rangeQuery = query[param] - extraQuery = _.clone(query) - if rangeQuery?['$gte']? - extraQuery[param] = {'$lt' : rangeQuery['$gte']} - else if rangeQuery?['$gt'] - extraQuery[param] = {'$lte' : rangeQuery['$gt']} - else - delete extraQuery[param] + # helper function to check if an item from a pack is inside the + # desired range filterFn = (item) -> return false if rangeQuery?['$gte']? && item[param] < rangeQuery['$gte'] return false if rangeQuery?['$lte']? && item[param] > rangeQuery['$lte'] @@ -79,11 +112,28 @@ module.exports = MongoManager = return false if rangeQuery?['$gt']? && item[param] <= rangeQuery['$gt'] return true - needMore = false + # create a query which can be used to select the entries BEFORE + # the range because we sometimes need to find extra ones (when the + # boundary falls in the middle of a pack) + extraQuery = _.clone(query) + # The pack uses its first entry for its metadata and v, so the + # only queries where we might not get all the packs are those for + # $gt and $gte (i.e. we need to find packs which start before our + # range but end in it) + if rangeQuery?['$gte']? + extraQuery[param] = {'$lt' : rangeQuery['$gte']} + else if rangeQuery?['$gt'] + extraQuery[param] = {'$lte' : rangeQuery['$gt']} + else + delete extraQuery[param] + + needMore = false # keep track of whether we need to load more data + updates = [] # used to accumulate the set of results cursor.toArray (err, result) -> unpackedSet = MongoManager._unpackResults(result) - updates = unpackedSet.filter(filterFn) - updates = updates.slice(0, limit) if limit? + MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + # check if we need to retrieve more data, because there is a + # pack that crosses into our range last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null if limit? && updates.length == limit needMore = false @@ -92,7 +142,7 @@ module.exports = MongoManager = else if extraQuery[param]? && updates.length == 0 needMore = true if needMore - # need an extra result set + # we do need an extra result set extra = db.docHistory .find(extraQuery) .sort(sort) @@ -101,9 +151,8 @@ module.exports = MongoManager = if err? return callback err, updates else - extraSet = MongoManager._unpackResults(result2).filter(filterFn) - updates = updates.concat extraSet - updates = updates.slice(0, limit) if limit? + extraSet = MongoManager._unpackResults(result2) + MongoManager._filterAndLimit(updates, extraSet, filterFn, limit) callback err, updates return if err? @@ -112,9 +161,9 @@ module.exports = MongoManager = callback err, updates _unpackResults: (updates) -> + # iterate over the updates, if there's a pack, expand it into ops and + # insert it into the array at that point result = [] - # iterate over the updates - # if it's a pack, expand it into ops and insert it into the array at that point updates.forEach (item) -> if item.pack? all = MongoManager._explodePackToOps item @@ -124,6 +173,7 @@ module.exports = MongoManager = return result _explodePackToOps: (packObj) -> + # convert a pack into an array of ops doc_id = packObj.doc_id project_id = packObj.project_id result = packObj.pack.map (item) -> @@ -132,6 +182,13 @@ module.exports = MongoManager = item return result.reverse() + _filterAndLimit: (results, extra, filterFn, limit) + # update results with extra docs, after filtering and limiting + filtered = extra.filter(filterFn) + newResults = results.concat filtered + newResults.slice(0, limit) if limit? + results = newResults + getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) From d8dc56b03156e70c129a4490ed62a770da6d19d4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 9 Feb 2015 16:53:05 +0000 Subject: [PATCH 097/549] don't try to getLastCompressedUpdates from packs --- services/track-changes/app/coffee/MongoManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index efcefe9286..9ca6e1ff35 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -11,6 +11,7 @@ module.exports = MongoManager = .limit(1) .toArray (error, compressedUpdates) -> return callback(error) if error? + return callback null, null if compressedUpdates[0]?.pack? # cannot pop from a pack return callback null, compressedUpdates[0] or null # only used in this module From 7fdce8fc481b7b1c72c252abcd4e4498d5645ffd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 9 Feb 2015 16:53:44 +0000 Subject: [PATCH 098/549] split pack handling code into separate parts for docs and projects need to debug projects --- .../app/coffee/MongoManager.coffee | 122 +++++++++++++++--- 1 file changed, 101 insertions(+), 21 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 9ca6e1ff35..ae0981fcf0 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -84,16 +84,15 @@ module.exports = MongoManager = # are those of the first entry in the pack D1 (this makes it # possible to treat packs and single updates in the same way). - _findResults: (param, query, limit, callback) -> - # param - the field used to select and sort ops within a range, - # either 'v' or 'meta.end_ts' + + _findDocResults: (query, limit, callback) -> # query - the mongo query selector, includes both the doc_id/project_id and - # the range on v or meta.end_ts + # the range on v # limit - the mongo limit, we need to apply it after unpacking any # packs sort = {} - sort[param] = -1; + sort['v'] = -1; cursor = db.docHistory .find( query ) .sort( sort ) @@ -102,15 +101,15 @@ module.exports = MongoManager = cursor.limit(limit) # take the part of the query which selects the range over the parameter - rangeQuery = query[param] + rangeQuery = query['v'] # helper function to check if an item from a pack is inside the # desired range filterFn = (item) -> - return false if rangeQuery?['$gte']? && item[param] < rangeQuery['$gte'] - return false if rangeQuery?['$lte']? && item[param] > rangeQuery['$lte'] - return false if rangeQuery?['$lt']? && item[param] >= rangeQuery['$lt'] - return false if rangeQuery?['$gt']? && item[param] <= rangeQuery['$gt'] + return false if rangeQuery?['$gte']? && item['v'] < rangeQuery['$gte'] + return false if rangeQuery?['$lte']? && item['v'] > rangeQuery['$lte'] + return false if rangeQuery?['$lt']? && item['v'] >= rangeQuery['$lt'] + return false if rangeQuery?['$gt']? && item['v'] <= rangeQuery['$gt'] return true # create a query which can be used to select the entries BEFORE @@ -122,25 +121,25 @@ module.exports = MongoManager = # $gt and $gte (i.e. we need to find packs which start before our # range but end in it) if rangeQuery?['$gte']? - extraQuery[param] = {'$lt' : rangeQuery['$gte']} + extraQuery['v'] = {'$lt' : rangeQuery['$gte']} else if rangeQuery?['$gt'] - extraQuery[param] = {'$lte' : rangeQuery['$gt']} + extraQuery['v'] = {'$lte' : rangeQuery['$gt']} else - delete extraQuery[param] + delete extraQuery['v'] needMore = false # keep track of whether we need to load more data updates = [] # used to accumulate the set of results cursor.toArray (err, result) -> unpackedSet = MongoManager._unpackResults(result) - MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + updates = MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) # check if we need to retrieve more data, because there is a # pack that crosses into our range last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null if limit? && updates.length == limit needMore = false - else if extraQuery[param]? && last? && filterFn(last) + else if extraQuery['v']? && last? && filterFn(last) needMore = true - else if extraQuery[param]? && updates.length == 0 + else if extraQuery['v']? && updates.length == 0 needMore = true if needMore # we do need an extra result set @@ -153,7 +152,7 @@ module.exports = MongoManager = return callback err, updates else extraSet = MongoManager._unpackResults(result2) - MongoManager._filterAndLimit(updates, extraSet, filterFn, limit) + updates = MongoManager._filterAndLimit(updates, extraSet, filterFn, limit) callback err, updates return if err? @@ -161,6 +160,87 @@ module.exports = MongoManager = else callback err, updates + _findProjectResults: (query, limit, callback) -> + # query - the mongo query selector, includes both the doc_id/project_id and + # the range on v or meta.end_ts + # limit - the mongo limit, we need to apply it after unpacking any + # packs + + sort = {} + sort['meta.end_ts'] = -1; + cursor = db.docHistory + .find( query, {"op":false, "pack.op": false} ) # no need to return the op only need version info + .sort( sort ) + # if we have packs, we will trim the results more later after expanding them + if limit? + cursor.limit(limit) + + # take the part of the query which selects the range over the parameter + before = query['meta.end_ts']?['$lt'] # may be null + + updates = [] # used to accumulate the set of results + extraQuery = _.clone(query) + + cursor.toArray (err, result) -> + if err? + return callback err, result + if result.length == 0 && not before? # no results and no time range specified + return callback err, result + + unpackedSet = MongoManager._unpackResults(result) + if limit? + unpackedSet = unpackedSet.slice(0, limit) + # find the end time of the last result, we will take all the + # results up to this, and then all the changes at that time + # (without imposing a limit) + cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else before + console.log 'before is', before + console.log 'cutoff is', cutoff + console.log 'limit is', limit + + filterFn = (item) -> + ts = item?.meta?.end_ts + return false if before? && ts >= before + return false if cutoff? && ts < cutoff + return true + + updates = MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + console.log 'initial updates are', updates + # now find any extra entries which are exactly at the last time (cutoff) + # or in packs that overlap it + extraQuery['meta.end_ts'] = {"$gte": +cutoff} + extraQuery['meta.start_ts'] = {"$lte": +cutoff} + # we don't specify a limit here, as there could be any number of + # docs at that timestamp + # + # NB. need to catch items in original query and followup query for duplicates + console.log 'extraQuery is', extraQuery + extra = db.docHistory + .find(extraQuery, {"op": false, "pack.op": false}) + .sort(sort) + extra.toArray (err, result2) -> + console.log 'got extra result', err, result + if err? + return callback err, updates + else + extraSet = MongoManager._unpackResults(result2) + # note: final argument is null, no limit applied because we + # need all the updates at the final time to avoid breaking + # the changeset into parts + updates = MongoManager._filterAndLimit(updates, extraSet, filterFn, null) + # remove duplicates + seen = {} + updates = updates.filter (item) -> + key = item.doc_id + ' ' + item.v + console.log 'key is', key + if seen[key] + return false + else + seen[key] = true + console.log 'extra updates are', updates + callback err, updates + + _unpackResults: (updates) -> # iterate over the updates, if there's a pack, expand it into ops and # insert it into the array at that point @@ -183,12 +263,12 @@ module.exports = MongoManager = item return result.reverse() - _filterAndLimit: (results, extra, filterFn, limit) + _filterAndLimit: (results, extra, filterFn, limit) -> # update results with extra docs, after filtering and limiting filtered = extra.filter(filterFn) newResults = results.concat filtered newResults.slice(0, limit) if limit? - results = newResults + return newResults getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = @@ -200,7 +280,7 @@ module.exports = MongoManager = query["v"] ||= {} query["v"]["$lte"] = options.to - MongoManager._findResults('v', query, options.limit, callback) + MongoManager._findDocResults(query, options.limit, callback) getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> query = @@ -209,7 +289,7 @@ module.exports = MongoManager = if options.before? query["meta.end_ts"] = { $lt: options.before } - MongoManager._findResults('meta.end_ts', query, options.limit, callback) + MongoManager._findProjectResults(query, options.limit, callback) backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { From 2d25d545728cab2e1445ba4d838fa4a2148e5f14 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 10 Feb 2015 13:18:12 +0000 Subject: [PATCH 099/549] Release version 0.1.2 --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 4b03ee789a..73d88675af 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -1,6 +1,6 @@ { "name": "history-sharelatex", - "version": "0.1.0", + "version": "0.1.2", "description": "An API for saving and compressing individual document updates into a browsable history", "repository": { "type": "git", From 1d7f0919a4b94522232a194b333a4e289b26b21d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 10 Feb 2015 16:54:45 +0000 Subject: [PATCH 100/549] fix mongo logic for project search with packs --- .../app/coffee/MongoManager.coffee | 97 ++++++++++++------- 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index ae0981fcf0..4e1372bb2c 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -168,8 +168,10 @@ module.exports = MongoManager = sort = {} sort['meta.end_ts'] = -1; + + projection = {"op":false, "pack.op": false} cursor = db.docHistory - .find( query, {"op":false, "pack.op": false} ) # no need to return the op only need version info + .find( query, projection ) # no need to return the op only need version info .sort( sort ) # if we have packs, we will trim the results more later after expanding them if limit? @@ -179,7 +181,6 @@ module.exports = MongoManager = before = query['meta.end_ts']?['$lt'] # may be null updates = [] # used to accumulate the set of results - extraQuery = _.clone(query) cursor.toArray (err, result) -> if err? @@ -192,54 +193,84 @@ module.exports = MongoManager = unpackedSet = unpackedSet.slice(0, limit) # find the end time of the last result, we will take all the # results up to this, and then all the changes at that time - # (without imposing a limit) - cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else before + # (without imposing a limit) and any overlapping packs + cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else null console.log 'before is', before console.log 'cutoff is', cutoff console.log 'limit is', limit filterFn = (item) -> ts = item?.meta?.end_ts + console.log 'checking', ts, before, cutoff return false if before? && ts >= before return false if cutoff? && ts < cutoff return true updates = MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) console.log 'initial updates are', updates - # now find any extra entries which are exactly at the last time (cutoff) - # or in packs that overlap it - extraQuery['meta.end_ts'] = {"$gte": +cutoff} - extraQuery['meta.start_ts'] = {"$lte": +cutoff} - # we don't specify a limit here, as there could be any number of - # docs at that timestamp - # - # NB. need to catch items in original query and followup query for duplicates - console.log 'extraQuery is', extraQuery - extra = db.docHistory - .find(extraQuery, {"op": false, "pack.op": false}) + + # get all elements on the lower bound (cutoff) + tailQuery = _.clone(query) + tailQuery['meta.end_ts'] = cutoff + tail = db.docHistory + .find(tailQuery, projection) .sort(sort) - extra.toArray (err, result2) -> - console.log 'got extra result', err, result + + console.log 'tailQuery is', tailQuery + + # now find any packs that overlap with the time window + overlapQuery = _.clone(query) + if before? && cutoff? + overlapQuery['meta.end_ts'] = {"$gte": before} + overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } + else if before? && not cutoff? + overlapQuery['meta.end_ts'] = {"$gte": before} + overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } + else if not before? && cutoff? + overlapQuery['meta.end_ts'] = {"$gte": cutoff} + overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } + else if not before? && not cutoff? + overlapQuery['meta.end_ts'] = {"$gte": 0 } + overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } + overlap = db.docHistory + .find(overlapQuery, projection) + .sort(sort) + + console.log 'overlapQuery is', overlapQuery + + # we don't specify a limit here, as there could be any number of overlaps + # NB. need to catch items in original query and followup query for duplicates + + applyAndUpdate = (result) -> + extraSet = MongoManager._unpackResults(result) + # note: final argument is null, no limit applied because we + # need all the updates at the final time to avoid breaking + # the changeset into parts + updates = MongoManager._filterAndLimit(updates, extraSet, filterFn, null) + console.log 'extra updates after filterandlimit', updates + # remove duplicates + seen = {} + updates = updates.filter (item) -> + key = item.doc_id + ' ' + item.v + console.log 'key is', key + if seen[key] + return false + else + seen[key] = true + return true + console.log 'extra updates are', updates + + tail.toArray (err, result2) -> if err? return callback err, updates else - extraSet = MongoManager._unpackResults(result2) - # note: final argument is null, no limit applied because we - # need all the updates at the final time to avoid breaking - # the changeset into parts - updates = MongoManager._filterAndLimit(updates, extraSet, filterFn, null) - # remove duplicates - seen = {} - updates = updates.filter (item) -> - key = item.doc_id + ' ' + item.v - console.log 'key is', key - if seen[key] - return false + applyAndUpdate result2 + overlap.toArray (err, result3) -> + if err? + return callback err, updates else - seen[key] = true - console.log 'extra updates are', updates - callback err, updates - + applyAndUpdate result3 + callback err, updates _unpackResults: (updates) -> # iterate over the updates, if there's a pack, expand it into ops and From 682e8e8ab6e2596fe968d752f9139f6fb072eddd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 10 Feb 2015 16:54:58 +0000 Subject: [PATCH 101/549] add a sparse mongo index for finding packs --- services/track-changes/app/coffee/MongoManager.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 4e1372bb2c..014f43223f 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -353,6 +353,8 @@ module.exports = MongoManager = db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } # For finding all updates that affect a project (getProjectUpdates meta.end_ts < before db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } + # For finding all packs that affect a project (use a sparse index so only packs are included) + db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1} , { background: true, sparse: true } # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data From 4f36ccd519675fd4814d991c516195e266fc8733 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 13 Feb 2015 16:18:15 +0000 Subject: [PATCH 102/549] moved pack code into MongoPackManager --- .../app/coffee/MongoManager.coffee | 252 +----------------- .../app/coffee/MongoPackManager.coffee | 250 +++++++++++++++++ 2 files changed, 253 insertions(+), 249 deletions(-) create mode 100644 services/track-changes/app/coffee/MongoPackManager.coffee diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 014f43223f..693e669a07 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,4 +1,5 @@ {db, ObjectId} = require "./mongojs" +MongoPackManager = require "./MongoPackManager" async = require "async" _ = require "underscore" @@ -54,253 +55,6 @@ module.exports = MongoManager = # may need to roll over a pack here if we are inserting packs db.docHistory.insert update, callback - # The following function implements a method like a mongo find, but - # which expands any documents containing a 'pack' field into - # multiple values - # - # e.g. a single update looks like - # - # { - # "doc_id" : 549dae9e0a2a615c0c7f0c98, - # "project_id" : 549dae9c0a2a615c0c7f0c8c, - # "op" : [ {"p" : 6981, "d" : "?" } ], - # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, - # "v" : 17082 - # } - # - # and a pack looks like this - # - # { - # "doc_id" : 549dae9e0a2a615c0c7f0c98, - # "project_id" : 549dae9c0a2a615c0c7f0c8c, - # "pack" : [ D1, D2, D3, ....], - # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, - # "v" : 17082 - # } - # - # where D1, D2, D3, .... are single updates stripped of their - # doc_id and project_id fields (which are the same for all the - # updates in the pack). The meta and v fields of the pack itself - # are those of the first entry in the pack D1 (this makes it - # possible to treat packs and single updates in the same way). - - - _findDocResults: (query, limit, callback) -> - # query - the mongo query selector, includes both the doc_id/project_id and - # the range on v - # limit - the mongo limit, we need to apply it after unpacking any - # packs - - sort = {} - sort['v'] = -1; - cursor = db.docHistory - .find( query ) - .sort( sort ) - # if we have packs, we will trim the results more later after expanding them - if limit? - cursor.limit(limit) - - # take the part of the query which selects the range over the parameter - rangeQuery = query['v'] - - # helper function to check if an item from a pack is inside the - # desired range - filterFn = (item) -> - return false if rangeQuery?['$gte']? && item['v'] < rangeQuery['$gte'] - return false if rangeQuery?['$lte']? && item['v'] > rangeQuery['$lte'] - return false if rangeQuery?['$lt']? && item['v'] >= rangeQuery['$lt'] - return false if rangeQuery?['$gt']? && item['v'] <= rangeQuery['$gt'] - return true - - # create a query which can be used to select the entries BEFORE - # the range because we sometimes need to find extra ones (when the - # boundary falls in the middle of a pack) - extraQuery = _.clone(query) - # The pack uses its first entry for its metadata and v, so the - # only queries where we might not get all the packs are those for - # $gt and $gte (i.e. we need to find packs which start before our - # range but end in it) - if rangeQuery?['$gte']? - extraQuery['v'] = {'$lt' : rangeQuery['$gte']} - else if rangeQuery?['$gt'] - extraQuery['v'] = {'$lte' : rangeQuery['$gt']} - else - delete extraQuery['v'] - - needMore = false # keep track of whether we need to load more data - updates = [] # used to accumulate the set of results - cursor.toArray (err, result) -> - unpackedSet = MongoManager._unpackResults(result) - updates = MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) - # check if we need to retrieve more data, because there is a - # pack that crosses into our range - last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null - if limit? && updates.length == limit - needMore = false - else if extraQuery['v']? && last? && filterFn(last) - needMore = true - else if extraQuery['v']? && updates.length == 0 - needMore = true - if needMore - # we do need an extra result set - extra = db.docHistory - .find(extraQuery) - .sort(sort) - .limit(1) - extra.toArray (err, result2) -> - if err? - return callback err, updates - else - extraSet = MongoManager._unpackResults(result2) - updates = MongoManager._filterAndLimit(updates, extraSet, filterFn, limit) - callback err, updates - return - if err? - callback err, result - else - callback err, updates - - _findProjectResults: (query, limit, callback) -> - # query - the mongo query selector, includes both the doc_id/project_id and - # the range on v or meta.end_ts - # limit - the mongo limit, we need to apply it after unpacking any - # packs - - sort = {} - sort['meta.end_ts'] = -1; - - projection = {"op":false, "pack.op": false} - cursor = db.docHistory - .find( query, projection ) # no need to return the op only need version info - .sort( sort ) - # if we have packs, we will trim the results more later after expanding them - if limit? - cursor.limit(limit) - - # take the part of the query which selects the range over the parameter - before = query['meta.end_ts']?['$lt'] # may be null - - updates = [] # used to accumulate the set of results - - cursor.toArray (err, result) -> - if err? - return callback err, result - if result.length == 0 && not before? # no results and no time range specified - return callback err, result - - unpackedSet = MongoManager._unpackResults(result) - if limit? - unpackedSet = unpackedSet.slice(0, limit) - # find the end time of the last result, we will take all the - # results up to this, and then all the changes at that time - # (without imposing a limit) and any overlapping packs - cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else null - console.log 'before is', before - console.log 'cutoff is', cutoff - console.log 'limit is', limit - - filterFn = (item) -> - ts = item?.meta?.end_ts - console.log 'checking', ts, before, cutoff - return false if before? && ts >= before - return false if cutoff? && ts < cutoff - return true - - updates = MongoManager._filterAndLimit(updates, unpackedSet, filterFn, limit) - console.log 'initial updates are', updates - - # get all elements on the lower bound (cutoff) - tailQuery = _.clone(query) - tailQuery['meta.end_ts'] = cutoff - tail = db.docHistory - .find(tailQuery, projection) - .sort(sort) - - console.log 'tailQuery is', tailQuery - - # now find any packs that overlap with the time window - overlapQuery = _.clone(query) - if before? && cutoff? - overlapQuery['meta.end_ts'] = {"$gte": before} - overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } - else if before? && not cutoff? - overlapQuery['meta.end_ts'] = {"$gte": before} - overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } - else if not before? && cutoff? - overlapQuery['meta.end_ts'] = {"$gte": cutoff} - overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } - else if not before? && not cutoff? - overlapQuery['meta.end_ts'] = {"$gte": 0 } - overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } - overlap = db.docHistory - .find(overlapQuery, projection) - .sort(sort) - - console.log 'overlapQuery is', overlapQuery - - # we don't specify a limit here, as there could be any number of overlaps - # NB. need to catch items in original query and followup query for duplicates - - applyAndUpdate = (result) -> - extraSet = MongoManager._unpackResults(result) - # note: final argument is null, no limit applied because we - # need all the updates at the final time to avoid breaking - # the changeset into parts - updates = MongoManager._filterAndLimit(updates, extraSet, filterFn, null) - console.log 'extra updates after filterandlimit', updates - # remove duplicates - seen = {} - updates = updates.filter (item) -> - key = item.doc_id + ' ' + item.v - console.log 'key is', key - if seen[key] - return false - else - seen[key] = true - return true - console.log 'extra updates are', updates - - tail.toArray (err, result2) -> - if err? - return callback err, updates - else - applyAndUpdate result2 - overlap.toArray (err, result3) -> - if err? - return callback err, updates - else - applyAndUpdate result3 - callback err, updates - - _unpackResults: (updates) -> - # iterate over the updates, if there's a pack, expand it into ops and - # insert it into the array at that point - result = [] - updates.forEach (item) -> - if item.pack? - all = MongoManager._explodePackToOps item - result = result.concat all - else - result.push item - return result - - _explodePackToOps: (packObj) -> - # convert a pack into an array of ops - doc_id = packObj.doc_id - project_id = packObj.project_id - result = packObj.pack.map (item) -> - item.doc_id = doc_id - item.project_id = project_id - item - return result.reverse() - - _filterAndLimit: (results, extra, filterFn, limit) -> - # update results with extra docs, after filtering and limiting - filtered = extra.filter(filterFn) - newResults = results.concat filtered - newResults.slice(0, limit) if limit? - return newResults - getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> query = doc_id: ObjectId(doc_id.toString()) @@ -311,7 +65,7 @@ module.exports = MongoManager = query["v"] ||= {} query["v"]["$lte"] = options.to - MongoManager._findDocResults(query, options.limit, callback) + MongoPackManager.findDocResults(db.docHistory, query, options.limit, callback) getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> query = @@ -320,7 +74,7 @@ module.exports = MongoManager = if options.before? query["meta.end_ts"] = { $lt: options.before } - MongoManager._findProjectResults(query, options.limit, callback) + MongoPackManager.findProjectResults(db.docHistory, query, options.limit, callback) backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { diff --git a/services/track-changes/app/coffee/MongoPackManager.coffee b/services/track-changes/app/coffee/MongoPackManager.coffee new file mode 100644 index 0000000000..4db59749fd --- /dev/null +++ b/services/track-changes/app/coffee/MongoPackManager.coffee @@ -0,0 +1,250 @@ +async = require "async" +_ = require "underscore" + +module.exports = MongoPackManager = + # The following functions implement methods like a mongo find, but + # expands any documents containing a 'pack' field into multiple + # values + # + # e.g. a single update looks like + # + # { + # "doc_id" : 549dae9e0a2a615c0c7f0c98, + # "project_id" : 549dae9c0a2a615c0c7f0c8c, + # "op" : [ {"p" : 6981, "d" : "?" } ], + # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, + # "v" : 17082 + # } + # + # and a pack looks like this + # + # { + # "doc_id" : 549dae9e0a2a615c0c7f0c98, + # "project_id" : 549dae9c0a2a615c0c7f0c8c, + # "pack" : [ D1, D2, D3, ....], + # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, + # "v" : 17082 + # } + # + # where D1, D2, D3, .... are single updates stripped of their + # doc_id and project_id fields (which are the same for all the + # updates in the pack). The meta and v fields of the pack itself + # are those of the first entry in the pack D1 (this makes it + # possible to treat packs and single updates in the same way). + + + findDocResults: (collection, query, limit, callback) -> + # query - the mongo query selector, includes both the doc_id/project_id and + # the range on v + # limit - the mongo limit, we need to apply it after unpacking any + # packs + + sort = {} + sort['v'] = -1; + cursor = collection + .find( query ) + .sort( sort ) + # if we have packs, we will trim the results more later after expanding them + if limit? + cursor.limit(limit) + + # take the part of the query which selects the range over the parameter + rangeQuery = query['v'] + + # helper function to check if an item from a pack is inside the + # desired range + filterFn = (item) -> + return false if rangeQuery?['$gte']? && item['v'] < rangeQuery['$gte'] + return false if rangeQuery?['$lte']? && item['v'] > rangeQuery['$lte'] + return false if rangeQuery?['$lt']? && item['v'] >= rangeQuery['$lt'] + return false if rangeQuery?['$gt']? && item['v'] <= rangeQuery['$gt'] + return true + + # create a query which can be used to select the entries BEFORE + # the range because we sometimes need to find extra ones (when the + # boundary falls in the middle of a pack) + extraQuery = _.clone(query) + # The pack uses its first entry for its metadata and v, so the + # only queries where we might not get all the packs are those for + # $gt and $gte (i.e. we need to find packs which start before our + # range but end in it) + if rangeQuery?['$gte']? + extraQuery['v'] = {'$lt' : rangeQuery['$gte']} + else if rangeQuery?['$gt'] + extraQuery['v'] = {'$lte' : rangeQuery['$gt']} + else + delete extraQuery['v'] + + needMore = false # keep track of whether we need to load more data + updates = [] # used to accumulate the set of results + cursor.toArray (err, result) -> + unpackedSet = MongoPackManager._unpackResults(result) + updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + # check if we need to retrieve more data, because there is a + # pack that crosses into our range + last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null + if limit? && updates.length == limit + needMore = false + else if extraQuery['v']? && last? && filterFn(last) + needMore = true + else if extraQuery['v']? && updates.length == 0 + needMore = true + if needMore + # we do need an extra result set + extra = collection + .find(extraQuery) + .sort(sort) + .limit(1) + extra.toArray (err, result2) -> + if err? + return callback err, updates + else + extraSet = MongoPackManager._unpackResults(result2) + updates = MongoPackManager._filterAndLimit(updates, extraSet, filterFn, limit) + callback err, updates + return + if err? + callback err, result + else + callback err, updates + + findProjectResults: (collection, query, limit, callback) -> + # query - the mongo query selector, includes both the doc_id/project_id and + # the range on v or meta.end_ts + # limit - the mongo limit, we need to apply it after unpacking any + # packs + + sort = {} + sort['meta.end_ts'] = -1; + + projection = {"op":false, "pack.op": false} + cursor = collection + .find( query, projection ) # no need to return the op only need version info + .sort( sort ) + # if we have packs, we will trim the results more later after expanding them + if limit? + cursor.limit(limit) + + # take the part of the query which selects the range over the parameter + before = query['meta.end_ts']?['$lt'] # may be null + + updates = [] # used to accumulate the set of results + + cursor.toArray (err, result) -> + if err? + return callback err, result + if result.length == 0 && not before? # no results and no time range specified + return callback err, result + + unpackedSet = MongoPackManager._unpackResults(result) + if limit? + unpackedSet = unpackedSet.slice(0, limit) + # find the end time of the last result, we will take all the + # results up to this, and then all the changes at that time + # (without imposing a limit) and any overlapping packs + cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else null + #console.log 'before is', before + #console.log 'cutoff is', cutoff + #console.log 'limit is', limit + + filterFn = (item) -> + ts = item?.meta?.end_ts + #console.log 'checking', ts, before, cutoff + return false if before? && ts >= before + return false if cutoff? && ts < cutoff + return true + + updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + #console.log 'initial updates are', updates + + # get all elements on the lower bound (cutoff) + tailQuery = _.clone(query) + tailQuery['meta.end_ts'] = cutoff + tail = collection + .find(tailQuery, projection) + .sort(sort) + + #console.log 'tailQuery is', tailQuery + + # now find any packs that overlap with the time window + overlapQuery = _.clone(query) + if before? && cutoff? + overlapQuery['meta.end_ts'] = {"$gte": before} + overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } + else if before? && not cutoff? + overlapQuery['meta.end_ts'] = {"$gte": before} + overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } + else if not before? && cutoff? + overlapQuery['meta.end_ts'] = {"$gte": cutoff} + overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } + else if not before? && not cutoff? + overlapQuery['meta.end_ts'] = {"$gte": 0 } + overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } + overlap = collection + .find(overlapQuery, projection) + .sort(sort) + + #console.log 'overlapQuery is', overlapQuery + + # we don't specify a limit here, as there could be any number of overlaps + # NB. need to catch items in original query and followup query for duplicates + + applyAndUpdate = (result) -> + extraSet = MongoPackManager._unpackResults(result) + # note: final argument is null, no limit applied because we + # need all the updates at the final time to avoid breaking + # the changeset into parts + updates = MongoPackManager._filterAndLimit(updates, extraSet, filterFn, null) + #console.log 'extra updates after filterandlimit', updates + # remove duplicates + seen = {} + updates = updates.filter (item) -> + key = item.doc_id + ' ' + item.v + #console.log 'key is', key + if seen[key] + return false + else + seen[key] = true + return true + #console.log 'extra updates are', updates + + tail.toArray (err, result2) -> + if err? + return callback err, updates + else + applyAndUpdate result2 + overlap.toArray (err, result3) -> + if err? + return callback err, updates + else + applyAndUpdate result3 + callback err, updates + + _unpackResults: (updates) -> + # iterate over the updates, if there's a pack, expand it into ops and + # insert it into the array at that point + result = [] + updates.forEach (item) -> + if item.pack? + all = MongoPackManager._explodePackToOps item + result = result.concat all + else + result.push item + return result + + _explodePackToOps: (packObj) -> + # convert a pack into an array of ops + doc_id = packObj.doc_id + project_id = packObj.project_id + result = packObj.pack.map (item) -> + item.doc_id = doc_id + item.project_id = project_id + item + return result.reverse() + + _filterAndLimit: (results, extra, filterFn, limit) -> + # update results with extra docs, after filtering and limiting + filtered = extra.filter(filterFn) + newResults = results.concat filtered + newResults.slice(0, limit) if limit? + return newResults From cafda577105794dd00a414a879b0b25475e53abb Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 13 Feb 2015 16:23:36 +0000 Subject: [PATCH 103/549] command-line packing script --- services/track-changes/pack.coffee | 72 ++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 services/track-changes/pack.coffee diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee new file mode 100644 index 0000000000..4bc10c829d --- /dev/null +++ b/services/track-changes/pack.coffee @@ -0,0 +1,72 @@ +mongojs = require "mongojs" +async = require "async" +db = mongojs.connect("localhost/sharelatex", ["docHistory"]) +BSON=db.bson.BSON +util = require 'util' +_ = require 'underscore' +MAX_SIZE = 1024*1024 +MAX_COUNT = 1024 + +packOps = (doc_id, callback) -> + console.log 'packing', doc_id + db.docHistory.find({doc_id:mongojs.ObjectId(doc_id),pack:{$exists:false}}).sort {v:1}, (err, docs) -> + packs = [] + top = null + if docs.length < 100 + console.log 'only', docs.length, 'ops, skipping' + return + docs.forEach (d,i) -> + sz = BSON.calculateObjectSize(d) + if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE + top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} + top.sz += sz + top.v_end = d.v + top.meta.end_ts = d.meta.end_ts + return + else + # create a new pack + top = _.clone(d) + top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] + top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } + top.sz = sz + delete top.op + delete top._id + packs.push top + # never store the last pack, keep some unpacked ops + packs.pop() + # + if packs.length > docs.length + console.log 'not enough compression', packs.length, 'vs', docs.length, 'skipping' + return + + console.log 'docs', docs.length, 'packs', packs.length + tasks = [] + for pack, i in packs + console.log 'storing pack', i + do (pack) -> + task = (cb) -> + bulk = db.docHistory.initializeOrderedBulkOp(); + console.log 'insert', pack + bulk.insert pack + pack.pack.forEach (op) -> + console.log 'will remove', op._id + bulk.find({_id:op._id}).removeOne() + bulk.execute (err, result) -> + console.log 'ok?', result.ok, 'nInserted', result.nInserted, 'nRemoved', result.nRemoved + cb(err, result) + tasks.push task + + async.series tasks, (err, result) -> + console.log 'writing packs', err, result.length + callback err, result + +packOpsTask = (doc_id) -> + return (callback) -> + packOps doc_id, callback + +tasks = (packOpsTask id for id in process.argv.slice(2)) + +async.series tasks, (err, results) -> + console.log 'closing db' + #console.log util.inspect(results,{depth:null}) + db.close() From 18b34d1bcbe11c654fe419b0fe138c5952d2dc59 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 13 Feb 2015 17:01:37 +0000 Subject: [PATCH 104/549] clean up packing script --- services/track-changes/pack.coffee | 78 +++++++++++++++--------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index 4bc10c829d..aa0c079261 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -6,15 +6,31 @@ util = require 'util' _ = require 'underscore' MAX_SIZE = 1024*1024 MAX_COUNT = 1024 +MIN_COUNT = 100 +KEEP_OPS = 100 -packOps = (doc_id, callback) -> - console.log 'packing', doc_id +insertPack = (packObj, callback) -> + bulk = db.docHistory.initializeOrderedBulkOp(); + expect_nInserted = 1 + expect_nRemoved = packObj.pack.length + console.log 'inserting', expect_nInserted, 'pack, will remove', expect_nRemoved, 'ops' + bulk.insert packObj + packObj.pack.forEach (op) -> + bulk.find({_id:op._id}).removeOne() + bulk.execute (err, result) -> + if err? or result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved + console.log err, result + console.log 'nInserted', result.nInserted, 'nRemoved', result.nRemoved + callback(err, result) + +packDocHistory = (doc_id, callback) -> + console.log 'packing doc_id', doc_id db.docHistory.find({doc_id:mongojs.ObjectId(doc_id),pack:{$exists:false}}).sort {v:1}, (err, docs) -> packs = [] top = null - if docs.length < 100 - console.log 'only', docs.length, 'ops, skipping' - return + origDocs = docs.length + # keep the last KEEP_OPS as individual ops + docs = docs.slice(0,-KEEP_OPS) docs.forEach (d,i) -> sz = BSON.calculateObjectSize(d) if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE @@ -23,7 +39,7 @@ packOps = (doc_id, callback) -> top.v_end = d.v top.meta.end_ts = d.meta.end_ts return - else + else if sz < MAX_SIZE # create a new pack top = _.clone(d) top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] @@ -32,41 +48,23 @@ packOps = (doc_id, callback) -> delete top.op delete top._id packs.push top - # never store the last pack, keep some unpacked ops - packs.pop() - # - if packs.length > docs.length - console.log 'not enough compression', packs.length, 'vs', docs.length, 'skipping' - return + else + # keep the op + console.log 'keeping large op unchanged' - console.log 'docs', docs.length, 'packs', packs.length - tasks = [] - for pack, i in packs - console.log 'storing pack', i - do (pack) -> - task = (cb) -> - bulk = db.docHistory.initializeOrderedBulkOp(); - console.log 'insert', pack - bulk.insert pack - pack.pack.forEach (op) -> - console.log 'will remove', op._id - bulk.find({_id:op._id}).removeOne() - bulk.execute (err, result) -> - console.log 'ok?', result.ok, 'nInserted', result.nInserted, 'nRemoved', result.nRemoved - cb(err, result) - tasks.push task + # only store packs with a sufficient number of ops, discard others + packs = packs.filter (packObj) -> + packObj.pack.length > MIN_COUNT - async.series tasks, (err, result) -> - console.log 'writing packs', err, result.length + console.log 'docs', origDocs, 'packs', packs.length + async.each packs, insertPack, (err, result) -> + if err? + console.log 'err', err + console.log 'done writing packs' callback err, result -packOpsTask = (doc_id) -> - return (callback) -> - packOps doc_id, callback - -tasks = (packOpsTask id for id in process.argv.slice(2)) - -async.series tasks, (err, results) -> - console.log 'closing db' - #console.log util.inspect(results,{depth:null}) - db.close() +async.each process.argv.slice(2), (doc_id, callback) -> + packDocHistory(doc_id, callback) + , (err, results) -> + console.log 'closing db' + db.close() From 3ab8238013ce7312e7a130dff822f7a2c546ddf7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 13 Feb 2015 17:01:55 +0000 Subject: [PATCH 105/549] update mongojs version to allow use of bulk updates --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e6f006d739..3b8311ba1a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -9,7 +9,7 @@ "dependencies": { "async": "~0.2.10", "express": "3.3.5", - "mongojs": "~0.9.11", + "mongojs": "^0.18.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", From 8615b7a08611a99a15b9feaec8089373bd00d650 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 13:43:11 +0000 Subject: [PATCH 106/549] fix project results order, sort by time --- .../track-changes/app/coffee/MongoPackManager.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/MongoPackManager.coffee b/services/track-changes/app/coffee/MongoPackManager.coffee index 4db59749fd..62fe466d5a 100644 --- a/services/track-changes/app/coffee/MongoPackManager.coffee +++ b/services/track-changes/app/coffee/MongoPackManager.coffee @@ -154,6 +154,9 @@ module.exports = MongoPackManager = return false if cutoff? && ts < cutoff return true + timeOrder = (a, b) -> + b.meta.end_ts - a.meta.end_ts + updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) #console.log 'initial updates are', updates @@ -210,15 +213,15 @@ module.exports = MongoPackManager = tail.toArray (err, result2) -> if err? - return callback err, updates + return callback err, updates.sort timeOrder else applyAndUpdate result2 overlap.toArray (err, result3) -> if err? - return callback err, updates + return callback err, updates.sort timeOrder else applyAndUpdate result3 - callback err, updates + callback err, updates.sort timeOrder _unpackResults: (updates) -> # iterate over the updates, if there's a pack, expand it into ops and From 0516e9429d07b5c6b3fcc40626454039e51d8178 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 13:43:27 +0000 Subject: [PATCH 107/549] ensure document results are in version order --- .../track-changes/app/coffee/MongoPackManager.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/MongoPackManager.coffee b/services/track-changes/app/coffee/MongoPackManager.coffee index 62fe466d5a..2067b24ec4 100644 --- a/services/track-changes/app/coffee/MongoPackManager.coffee +++ b/services/track-changes/app/coffee/MongoPackManager.coffee @@ -60,6 +60,9 @@ module.exports = MongoPackManager = return false if rangeQuery?['$gt']? && item['v'] <= rangeQuery['$gt'] return true + versionOrder = (a, b) -> + b.v - a.v + # create a query which can be used to select the entries BEFORE # the range because we sometimes need to find extra ones (when the # boundary falls in the middle of a pack) @@ -97,16 +100,16 @@ module.exports = MongoPackManager = .limit(1) extra.toArray (err, result2) -> if err? - return callback err, updates + return callback err, updates.sort versionOrder else extraSet = MongoPackManager._unpackResults(result2) updates = MongoPackManager._filterAndLimit(updates, extraSet, filterFn, limit) - callback err, updates + callback err, updates.sort versionOrder return if err? callback err, result else - callback err, updates + callback err, updates.sort versionOrder findProjectResults: (collection, query, limit, callback) -> # query - the mongo query selector, includes both the doc_id/project_id and From fa1e890282aed17e8cd72789991d6146d5ff0dcd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 15:37:47 +0000 Subject: [PATCH 108/549] move update deduplication to filterAndLimit method --- .../app/coffee/MongoPackManager.coffee | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/services/track-changes/app/coffee/MongoPackManager.coffee b/services/track-changes/app/coffee/MongoPackManager.coffee index 2067b24ec4..ed5bad6249 100644 --- a/services/track-changes/app/coffee/MongoPackManager.coffee +++ b/services/track-changes/app/coffee/MongoPackManager.coffee @@ -202,18 +202,6 @@ module.exports = MongoPackManager = # the changeset into parts updates = MongoPackManager._filterAndLimit(updates, extraSet, filterFn, null) #console.log 'extra updates after filterandlimit', updates - # remove duplicates - seen = {} - updates = updates.filter (item) -> - key = item.doc_id + ' ' + item.v - #console.log 'key is', key - if seen[key] - return false - else - seen[key] = true - return true - #console.log 'extra updates are', updates - tail.toArray (err, result2) -> if err? return callback err, updates.sort timeOrder @@ -252,5 +240,15 @@ module.exports = MongoPackManager = # update results with extra docs, after filtering and limiting filtered = extra.filter(filterFn) newResults = results.concat filtered + # remove duplicates + seen = {} + newResults = newResults.filter (item) -> + key = item.doc_id + ' ' + item.v + #console.log 'key is', key + if seen[key] + return false + else + seen[key] = true + return true newResults.slice(0, limit) if limit? return newResults From f5a9184b57bd10e1559bbf231e801c65602b1463 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 15:38:40 +0000 Subject: [PATCH 109/549] update the tests to work with packs --- .../MongoManager/MongoManagerTests.coffee | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index fa1ec9232f..2cac1fea0f 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -163,12 +163,26 @@ describe "MongoManager", -> describe "getDocUpdates", -> beforeEach -> - @updates = ["mock-update"] + @results = [ + {foo: "mock-update", v: 56}, + {foo: "mock-update", v: 55}, + {foo: "mock-update", v: 42}, + {foo: "mock-update", v: 41} + ] + @updates_between = [ + {foo: "mock-update", v: 55}, + {foo: "mock-update", v: 42} + ] + @updates_after = [ + {foo: "mock-update", v: 56}, + {foo: "mock-update", v: 55}, + {foo: "mock-update", v: 42} + ] @db.docHistory = {} @db.docHistory.find = sinon.stub().returns @db.docHistory @db.docHistory.sort = sinon.stub().returns @db.docHistory @db.docHistory.limit = sinon.stub().returns @db.docHistory - @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates) + @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) @from = 42 @to = 55 @@ -190,12 +204,12 @@ describe "MongoManager", -> .calledWith("v": -1) .should.equal true - it "should not limit the results", -> - @db.docHistory.limit - .called.should.equal false + #it "should not limit the results", -> + # @db.docHistory.limit + # .called.should.equal false - it "should call the call back with the updates", -> - @callback.calledWith(null, @updates).should.equal true + it "should call the call back with the results", -> + @callback.calledWith(null, @updates_between).should.equal true describe "without a to version", -> beforeEach -> @@ -210,7 +224,7 @@ describe "MongoManager", -> .should.equal true it "should call the call back with the updates", -> - @callback.calledWith(null, @updates).should.equal true + @callback.calledWith(null, @updates_after).should.equal true describe "with a limit", -> beforeEach -> @@ -224,14 +238,22 @@ describe "MongoManager", -> describe "getDocUpdates", -> beforeEach -> - @updates = ["mock-update"] + @results = [ + {foo: "mock-update", v: 56, meta: {end_ts: 110}}, + {foo: "mock-update", v: 55, meta: {end_ts: 100}}, + {foo: "mock-update", v: 42, meta:{end_ts: 90}} + ] + @updates = [ + {foo: "mock-update", v: 55, meta: {end_ts: 100}}, + {foo: "mock-update", v: 42, meta:{end_ts: 90}} + ] @db.docHistory = {} @db.docHistory.find = sinon.stub().returns @db.docHistory @db.docHistory.sort = sinon.stub().returns @db.docHistory @db.docHistory.limit = sinon.stub().returns @db.docHistory - @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @updates) + @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) - @before = Date.now() + @before = 101 describe "with a before timestamp", -> beforeEach -> @@ -269,7 +291,7 @@ describe "MongoManager", -> .should.equal true it "should call the call back with the updates", -> - @callback.calledWith(null, @updates).should.equal true + @callback.calledWith(null, @results).should.equal true describe "with a limit", -> beforeEach -> From e55f2a0ff7ac025756f928173e6803bc0b163ef0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 15:59:58 +0000 Subject: [PATCH 110/549] test pack for getDocUpdates with version --- .../MongoManager/MongoManagerTests.coffee | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 2cac1fea0f..d0d1223d52 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -164,19 +164,29 @@ describe "MongoManager", -> describe "getDocUpdates", -> beforeEach -> @results = [ - {foo: "mock-update", v: 56}, - {foo: "mock-update", v: 55}, - {foo: "mock-update", v: 42}, - {foo: "mock-update", v: 41} + {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, + {pack: [ {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 52, doc_id: 100, project_id: 1} ] + , v: 52, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 42, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 41, doc_id: 100, project_id: 1} ] @updates_between = [ - {foo: "mock-update", v: 55}, - {foo: "mock-update", v: 42} + {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} ] @updates_after = [ - {foo: "mock-update", v: 56}, - {foo: "mock-update", v: 55}, - {foo: "mock-update", v: 42} + {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} ] @db.docHistory = {} @db.docHistory.find = sinon.stub().returns @db.docHistory From 7d6811559c8dd0c01eed9555314cfd709f1113ae Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 16:43:27 +0000 Subject: [PATCH 111/549] test pack for getDocUpdates with time --- .../MongoManager/MongoManagerTests.coffee | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index d0d1223d52..bb9162bb6c 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -249,14 +249,41 @@ describe "MongoManager", -> describe "getDocUpdates", -> beforeEach -> @results = [ - {foo: "mock-update", v: 56, meta: {end_ts: 110}}, - {foo: "mock-update", v: 55, meta: {end_ts: 100}}, - {foo: "mock-update", v: 42, meta:{end_ts: 90}} + {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, + {pack: [ + {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1} ] + , v: 52, meta: {end_ts: 100}, doc_id: 300, project_id: 1}, + {pack: [ + {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1} ] + , v: 52, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 42, meta:{end_ts: 90}, doc_id: 100, project_id: 1} ] - @updates = [ - {foo: "mock-update", v: 55, meta: {end_ts: 100}}, - {foo: "mock-update", v: 42, meta:{end_ts: 90}} + @updates_before = [ + {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1}, ] + @updates_all = [ + {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, + {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, + {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, + {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1} + ] + + @db.docHistory = {} @db.docHistory.find = sinon.stub().returns @db.docHistory @db.docHistory.sort = sinon.stub().returns @db.docHistory @@ -287,7 +314,7 @@ describe "MongoManager", -> .called.should.equal false it "should call the call back with the updates", -> - @callback.calledWith(null, @updates).should.equal true + @callback.calledWith(null, @updates_before).should.equal true describe "without a before timestamp", -> beforeEach -> @@ -301,7 +328,7 @@ describe "MongoManager", -> .should.equal true it "should call the call back with the updates", -> - @callback.calledWith(null, @results).should.equal true + @callback.calledWith(null, @updates_all).should.equal true describe "with a limit", -> beforeEach -> From 92e67511d57bd67e3d2f6788eed20b03f9e2ed68 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 16 Feb 2015 16:44:06 +0000 Subject: [PATCH 112/549] sort by doc_id to ensure consistent order for all changes with the same timestamp --- services/track-changes/app/coffee/MongoPackManager.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoPackManager.coffee b/services/track-changes/app/coffee/MongoPackManager.coffee index ed5bad6249..18e596fcf0 100644 --- a/services/track-changes/app/coffee/MongoPackManager.coffee +++ b/services/track-changes/app/coffee/MongoPackManager.coffee @@ -158,7 +158,12 @@ module.exports = MongoPackManager = return true timeOrder = (a, b) -> - b.meta.end_ts - a.meta.end_ts + (b.meta.end_ts - a.meta.end_ts) || documentOrder(a, b) + + documentOrder = (a, b) -> + x = a.doc_id.valueOf() + y = b.doc_id.valueOf() + if x > y then 1 else if x < y then -1 else 0 updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) #console.log 'initial updates are', updates From 448a7b7f36a6b8dea713979214edc582c0ba2951 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Feb 2015 11:14:13 +0000 Subject: [PATCH 113/549] rename MongoPackManager to PackManager --- .../app/coffee/MongoManager.coffee | 6 +++--- ...oPackManager.coffee => PackManager.coffee} | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename services/track-changes/app/coffee/{MongoPackManager.coffee => PackManager.coffee} (92%) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 693e669a07..2c7e3d94f3 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,5 +1,5 @@ {db, ObjectId} = require "./mongojs" -MongoPackManager = require "./MongoPackManager" +PackManager = require "./PackManager" async = require "async" _ = require "underscore" @@ -65,7 +65,7 @@ module.exports = MongoManager = query["v"] ||= {} query["v"]["$lte"] = options.to - MongoPackManager.findDocResults(db.docHistory, query, options.limit, callback) + PackManager.findDocResults(db.docHistory, query, options.limit, callback) getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> query = @@ -74,7 +74,7 @@ module.exports = MongoManager = if options.before? query["meta.end_ts"] = { $lt: options.before } - MongoPackManager.findProjectResults(db.docHistory, query, options.limit, callback) + PackManager.findProjectResults(db.docHistory, query, options.limit, callback) backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { diff --git a/services/track-changes/app/coffee/MongoPackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee similarity index 92% rename from services/track-changes/app/coffee/MongoPackManager.coffee rename to services/track-changes/app/coffee/PackManager.coffee index 18e596fcf0..4803fd4182 100644 --- a/services/track-changes/app/coffee/MongoPackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -1,7 +1,7 @@ async = require "async" _ = require "underscore" -module.exports = MongoPackManager = +module.exports = PackManager = # The following functions implement methods like a mongo find, but # expands any documents containing a 'pack' field into multiple # values @@ -81,8 +81,8 @@ module.exports = MongoPackManager = needMore = false # keep track of whether we need to load more data updates = [] # used to accumulate the set of results cursor.toArray (err, result) -> - unpackedSet = MongoPackManager._unpackResults(result) - updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + unpackedSet = PackManager._unpackResults(result) + updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) # check if we need to retrieve more data, because there is a # pack that crosses into our range last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null @@ -102,8 +102,8 @@ module.exports = MongoPackManager = if err? return callback err, updates.sort versionOrder else - extraSet = MongoPackManager._unpackResults(result2) - updates = MongoPackManager._filterAndLimit(updates, extraSet, filterFn, limit) + extraSet = PackManager._unpackResults(result2) + updates = PackManager._filterAndLimit(updates, extraSet, filterFn, limit) callback err, updates.sort versionOrder return if err? @@ -139,7 +139,7 @@ module.exports = MongoPackManager = if result.length == 0 && not before? # no results and no time range specified return callback err, result - unpackedSet = MongoPackManager._unpackResults(result) + unpackedSet = PackManager._unpackResults(result) if limit? unpackedSet = unpackedSet.slice(0, limit) # find the end time of the last result, we will take all the @@ -165,7 +165,7 @@ module.exports = MongoPackManager = y = b.doc_id.valueOf() if x > y then 1 else if x < y then -1 else 0 - updates = MongoPackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) + updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) #console.log 'initial updates are', updates # get all elements on the lower bound (cutoff) @@ -201,11 +201,11 @@ module.exports = MongoPackManager = # NB. need to catch items in original query and followup query for duplicates applyAndUpdate = (result) -> - extraSet = MongoPackManager._unpackResults(result) + extraSet = PackManager._unpackResults(result) # note: final argument is null, no limit applied because we # need all the updates at the final time to avoid breaking # the changeset into parts - updates = MongoPackManager._filterAndLimit(updates, extraSet, filterFn, null) + updates = PackManager._filterAndLimit(updates, extraSet, filterFn, null) #console.log 'extra updates after filterandlimit', updates tail.toArray (err, result2) -> if err? @@ -225,7 +225,7 @@ module.exports = MongoPackManager = result = [] updates.forEach (item) -> if item.pack? - all = MongoPackManager._explodePackToOps item + all = PackManager._explodePackToOps item result = result.concat all else result.push item From 76cbf32e6a83151dc0e57404df736c7143c63518 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Feb 2015 13:41:31 +0000 Subject: [PATCH 114/549] cleanup remove unnecessary comments, whitespace and unused modules --- .../track-changes/app/coffee/MongoManager.coffee | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 2c7e3d94f3..db17cdfc1c 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,10 +1,8 @@ {db, ObjectId} = require "./mongojs" PackManager = require "./PackManager" async = require "async" -_ = require "underscore" module.exports = MongoManager = - # only used in this module getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> db.docHistory .find(doc_id: ObjectId(doc_id.toString())) @@ -15,11 +13,9 @@ module.exports = MongoManager = return callback null, null if compressedUpdates[0]?.pack? # cannot pop from a pack return callback null, compressedUpdates[0] or null - # only used in this module deleteCompressedUpdate: (id, callback = (error) ->) -> db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) - # used in UpdatesManager popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? @@ -30,7 +26,6 @@ module.exports = MongoManager = else callback null, null - # used in UpdatesManager insertCompressedUpdates: (project_id, doc_id, updates, permanent, callback = (error) ->) -> jobs = [] for update in updates @@ -65,7 +60,7 @@ module.exports = MongoManager = query["v"] ||= {} query["v"]["$lte"] = options.to - PackManager.findDocResults(db.docHistory, query, options.limit, callback) + PackManager.findDocResults(db.docHistory, query, options.limit, callback) getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> query = @@ -74,7 +69,7 @@ module.exports = MongoManager = if options.before? query["meta.end_ts"] = { $lt: options.before } - PackManager.findProjectResults(db.docHistory, query, options.limit, callback) + PackManager.findProjectResults(db.docHistory, query, options.limit, callback) backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { @@ -103,12 +98,12 @@ module.exports = MongoManager = }, callback ensureIndices: () -> - # For finding all updates that go into a diff for a doc (getLastCompressedUpdate, getDocUpdates v > from && v < to) + # For finding all updates that go into a diff for a doc db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } - # For finding all updates that affect a project (getProjectUpdates meta.end_ts < before + # For finding all updates that affect a project db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding all packs that affect a project (use a sparse index so only packs are included) - db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1} , { background: true, sparse: true } + db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, { background: true, sparse: true } # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data From 24701219ffe31d7e30f15bd39e662504887f55fc Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Feb 2015 13:47:55 +0000 Subject: [PATCH 115/549] clean up comments and remove console.logs --- .../app/coffee/PackManager.coffee | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 4803fd4182..0b72ea158c 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -21,17 +21,20 @@ module.exports = PackManager = # { # "doc_id" : 549dae9e0a2a615c0c7f0c98, # "project_id" : 549dae9c0a2a615c0c7f0c8c, - # "pack" : [ D1, D2, D3, ....], + # "pack" : [ U1, U2, U3, ...., UN], # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, # "v" : 17082 # } # - # where D1, D2, D3, .... are single updates stripped of their + # where U1, U2, U3, .... are single updates stripped of their # doc_id and project_id fields (which are the same for all the - # updates in the pack). The meta and v fields of the pack itself - # are those of the first entry in the pack D1 (this makes it - # possible to treat packs and single updates in the same way). - + # updates in the pack). + # + # The pack itself has v and meta fields, this makes it possible to + # treat packs and single updates in the same way. + # + # The v field of the pack itself is from the first entry U1 + # The meta.end_ts field of the pack itself is from the last entry UN. findDocResults: (collection, query, limit, callback) -> # query - the mongo query selector, includes both the doc_id/project_id and @@ -113,7 +116,7 @@ module.exports = PackManager = findProjectResults: (collection, query, limit, callback) -> # query - the mongo query selector, includes both the doc_id/project_id and - # the range on v or meta.end_ts + # the range on meta.end_ts # limit - the mongo limit, we need to apply it after unpacking any # packs @@ -146,13 +149,9 @@ module.exports = PackManager = # results up to this, and then all the changes at that time # (without imposing a limit) and any overlapping packs cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else null - #console.log 'before is', before - #console.log 'cutoff is', cutoff - #console.log 'limit is', limit filterFn = (item) -> ts = item?.meta?.end_ts - #console.log 'checking', ts, before, cutoff return false if before? && ts >= before return false if cutoff? && ts < cutoff return true @@ -166,7 +165,6 @@ module.exports = PackManager = if x > y then 1 else if x < y then -1 else 0 updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) - #console.log 'initial updates are', updates # get all elements on the lower bound (cutoff) tailQuery = _.clone(query) @@ -175,8 +173,6 @@ module.exports = PackManager = .find(tailQuery, projection) .sort(sort) - #console.log 'tailQuery is', tailQuery - # now find any packs that overlap with the time window overlapQuery = _.clone(query) if before? && cutoff? @@ -195,8 +191,6 @@ module.exports = PackManager = .find(overlapQuery, projection) .sort(sort) - #console.log 'overlapQuery is', overlapQuery - # we don't specify a limit here, as there could be any number of overlaps # NB. need to catch items in original query and followup query for duplicates @@ -206,7 +200,6 @@ module.exports = PackManager = # need all the updates at the final time to avoid breaking # the changeset into parts updates = PackManager._filterAndLimit(updates, extraSet, filterFn, null) - #console.log 'extra updates after filterandlimit', updates tail.toArray (err, result2) -> if err? return callback err, updates.sort timeOrder @@ -249,7 +242,6 @@ module.exports = PackManager = seen = {} newResults = newResults.filter (item) -> key = item.doc_id + ' ' + item.v - #console.log 'key is', key if seen[key] return false else From 4c0eea99168a1d04daf14a4744b46ee284341f3a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 17 Feb 2015 14:53:50 +0000 Subject: [PATCH 116/549] return an error if trying to pop the last update from a pack --- services/track-changes/app/coffee/MongoManager.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index db17cdfc1c..b1ea5cb180 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -10,7 +10,10 @@ module.exports = MongoManager = .limit(1) .toArray (error, compressedUpdates) -> return callback(error) if error? - return callback null, null if compressedUpdates[0]?.pack? # cannot pop from a pack + if compressedUpdates[0]?.pack? + # cannot pop from a pack, throw error + error = new Error("last compressed update is a pack") + return callback error, null return callback null, compressedUpdates[0] or null deleteCompressedUpdate: (id, callback = (error) ->) -> From b19f3835d53f70eb47d53f08176ed1deedaa0798 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 23 Feb 2015 12:13:38 +0000 Subject: [PATCH 117/549] added checkpointing and logging to pack script --- services/track-changes/pack.coffee | 77 +++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index aa0c079261..c22a68fb02 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -1,19 +1,27 @@ -mongojs = require "mongojs" -async = require "async" -db = mongojs.connect("localhost/sharelatex", ["docHistory"]) +Settings = require "settings-sharelatex" +fs = require("fs") +mongojs = require("mongojs") +ObjectId = mongojs.ObjectId +db = mongojs(Settings.mongo.url, ['docHistory']) +async = require("async") BSON=db.bson.BSON util = require 'util' _ = require 'underscore' + +lineReader = require "line-reader" + MAX_SIZE = 1024*1024 MAX_COUNT = 1024 MIN_COUNT = 100 KEEP_OPS = 100 insertPack = (packObj, callback) -> + if shutdownRequested + return callback('shutdown') bulk = db.docHistory.initializeOrderedBulkOp(); expect_nInserted = 1 expect_nRemoved = packObj.pack.length - console.log 'inserting', expect_nInserted, 'pack, will remove', expect_nRemoved, 'ops' + util.log "insert #{expect_nInserted} pack, remove #{expect_nRemoved} ops" bulk.insert packObj packObj.pack.forEach (op) -> bulk.find({_id:op._id}).removeOne() @@ -24,7 +32,7 @@ insertPack = (packObj, callback) -> callback(err, result) packDocHistory = (doc_id, callback) -> - console.log 'packing doc_id', doc_id + util.log "starting pack operation for #{doc_id}" db.docHistory.find({doc_id:mongojs.ObjectId(doc_id),pack:{$exists:false}}).sort {v:1}, (err, docs) -> packs = [] top = null @@ -50,21 +58,56 @@ packDocHistory = (doc_id, callback) -> packs.push top else # keep the op - console.log 'keeping large op unchanged' + util.log 'keeping large op unchanged (#{sz} bytes)' # only store packs with a sufficient number of ops, discard others packs = packs.filter (packObj) -> packObj.pack.length > MIN_COUNT - console.log 'docs', origDocs, 'packs', packs.length - async.each packs, insertPack, (err, result) -> - if err? - console.log 'err', err - console.log 'done writing packs' - callback err, result + util.log "docs #{origDocs} packs #{packs.length}" + if packs.length + async.each packs, insertPack, (err, result) -> + if err? + console.log doc_id, err + else + util.log "done writing packs" + callback err, result + else + util.log "no packs to write" + callback null, null -async.each process.argv.slice(2), (doc_id, callback) -> - packDocHistory(doc_id, callback) - , (err, results) -> - console.log 'closing db' - db.close() +readFile = (file, callback) -> + ids = [] + lineReader.eachLine file, (line) -> + result = line.match(/[0-9a-f]{24}/) + if result? + ids.push result[0] + .then () -> + callback(null, ids) + +todoFile = process.argv[2] +doneFile = process.argv[3] +fs.appendFileSync doneFile, '# starting pack run at ' + new Date() + '\n' + +shutdownRequested = false +process.on 'SIGINT', () -> + util.log "Gracefully shutting down from SIGINT" + shutdownRequested = true + +readFile todoFile, (err, todo) -> + readFile doneFile, (err, done) -> + pending = _.difference todo, done + async.eachSeries pending, (doc_id, callback) -> + packDocHistory doc_id, (err, result) -> + if err? + return callback(err) + else + fs.appendFileSync doneFile, doc_id + '\n' + if shutdownRequested + return callback('shutdown') + callback(err, result) + , (err, results) -> + if err? + console.log 'error:', err + util.log 'closing db' + db.close() From ff7860b6dc5276dc790a7df059b71eb294376784 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 23 Feb 2015 14:10:09 +0000 Subject: [PATCH 118/549] added a time delay to the pack migration script --- services/track-changes/pack.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index c22a68fb02..461174ab77 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -105,7 +105,9 @@ readFile todoFile, (err, todo) -> fs.appendFileSync doneFile, doc_id + '\n' if shutdownRequested return callback('shutdown') - callback(err, result) + setTimeout () -> + callback(err, result) + , 1000 , (err, results) -> if err? console.log 'error:', err From 09e24413e61b40a9e2f9c9c06c1c47f06c617106 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 23 Feb 2015 17:00:43 +0000 Subject: [PATCH 119/549] fix pack migration to run sequentially not in parallel --- services/track-changes/pack.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index 461174ab77..0b4703a057 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -66,7 +66,7 @@ packDocHistory = (doc_id, callback) -> util.log "docs #{origDocs} packs #{packs.length}" if packs.length - async.each packs, insertPack, (err, result) -> + async.eachSeries packs, insertPack, (err, result) -> if err? console.log doc_id, err else From 34fa398cf6c070b79f2489302b1ec34306f303f4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 23 Feb 2015 17:00:54 +0000 Subject: [PATCH 120/549] increase pack migrate delay to 2 seconds --- services/track-changes/pack.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index 0b4703a057..8d31329ffb 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -29,7 +29,9 @@ insertPack = (packObj, callback) -> if err? or result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved console.log err, result console.log 'nInserted', result.nInserted, 'nRemoved', result.nRemoved - callback(err, result) + setTimeout () -> + callback(err, result) + , 2000 packDocHistory = (doc_id, callback) -> util.log "starting pack operation for #{doc_id}" From 79b9c48efda071986fd018518fbc3135aab9c398 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 26 Feb 2015 11:35:14 +0000 Subject: [PATCH 121/549] Release version 0.1.3 --- services/track-changes/config/settings.defaults.coffee | 5 +++++ services/track-changes/package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 8fdfa918e8..0a3e8f18a5 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -14,3 +14,8 @@ module.exports = url: "http://localhost:3000" user: "sharelatex" pass: "password" + redis: + web: + host: "localhost" + port: 6379 + pass: "" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 3a1ee8b1d4..67f2f367af 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -1,6 +1,6 @@ { "name": "history-sharelatex", - "version": "0.1.2", + "version": "0.1.3", "description": "An API for saving and compressing individual document updates into a browsable history", "repository": { "type": "git", From 24319f6d50fd24f4518a05074b4edff492c9b6fc Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 20 Mar 2015 15:27:34 +0000 Subject: [PATCH 122/549] Release version 0.1.4 --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 67f2f367af..5cee855801 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -1,6 +1,6 @@ { "name": "history-sharelatex", - "version": "0.1.3", + "version": "0.1.4", "description": "An API for saving and compressing individual document updates into a browsable history", "repository": { "type": "git", From b37d0dd08afe4a100fd6806b97a4125a5b7afe96 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 30 Apr 2015 15:07:03 +0100 Subject: [PATCH 123/549] make startup message consistent --- services/track-changes/app.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 779351975c..08a8b66113 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -36,5 +36,5 @@ app.listen port, host, (error) -> if error? logger.error err: error, "could not start track-changes server" else - logger.log "track changes api listening on http://#{host}:#{port}" + logger.info "trackchanges starting up, listening on #{host}:#{port}" From 8d3e0b2353fd8e378a8a1246c786912523f5d145 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 21 May 2015 15:46:29 +0100 Subject: [PATCH 124/549] added consistency check to pack migration script --- services/track-changes/pack.coffee | 162 ++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 48 deletions(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index 8d31329ffb..27e9a347b8 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -9,12 +9,94 @@ util = require 'util' _ = require 'underscore' lineReader = require "line-reader" +cli = require "cli" +options = cli.parse { + 'dry-run': ['n', 'do not write to database'], + 'fast': [false, 'no delays on writes'] +} MAX_SIZE = 1024*1024 MAX_COUNT = 1024 MIN_COUNT = 100 KEEP_OPS = 100 +DB_WRITE_DELAY = if options.fast then 0 else 2000 +DOCUMENT_PACK_DELAY = if options.fast then 0 else 1000 + +packDocHistory = (doc_id, callback) -> + util.log "starting pack operation for #{doc_id}" + getDocHistory doc_id, (err, docs) -> + return callback(err) if err? + origDocs = docs.length + convertDocsToPacks docs, (err, packs) -> + return callback(err) if err? + util.log "docs #{origDocs} packs #{packs.length}" + if packs.length + if options['dry-run'] + util.log 'dry-run, skipping write packs' + return callback() + savePacks packs, (err) -> + return callback(err) if err? + # check the history again + getDocHistory doc_id, callback + else + util.log "no packs to write" + callback null, null + +# retrieve document ops/packs and check them +getDocHistory = (doc_id, callback) -> + db.docHistory.find({doc_id:mongojs.ObjectId(doc_id)}).sort {v:1}, (err, docs) -> + return callback(err) if err? + # for safety, do a consistency check of the history + checkHistory doc_id, docs, (err) -> + return callback(err) if err? + callback err, docs + +convertDocsToPacks = (docs, callback) -> + packs = [] + top = null + # keep the last KEEP_OPS as individual ops + docs = docs.slice(0,-KEEP_OPS) + + docs.forEach (d,i) -> + if d.pack? + util.log "skipping existing pack of #{d.pack.length}" + top = null if top? # flush the current pack + return # and try next + sz = BSON.calculateObjectSize(d) + if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE + top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} + top.sz += sz + top.v_end = d.v + top.meta.end_ts = d.meta.end_ts + return + else if sz < MAX_SIZE + # create a new pack + top = _.clone(d) + top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] + top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } + top.sz = sz + delete top.op + delete top._id + packs.push top + else + # keep the op + util.log "keeping large op unchanged (#{sz} bytes)" + + # only store packs with a sufficient number of ops, discard others + packs = packs.filter (packObj) -> + packObj.pack.length > MIN_COUNT + callback(null, packs) + +savePacks = (packs, callback) -> + async.eachSeries packs, insertPack, (err, result) -> + if err? + console.log err + callback err, result + else + util.log "done writing packs" + callback() + insertPack = (packObj, callback) -> if shutdownRequested return callback('shutdown') @@ -31,52 +113,34 @@ insertPack = (packObj, callback) -> console.log 'nInserted', result.nInserted, 'nRemoved', result.nRemoved setTimeout () -> callback(err, result) - , 2000 + , DB_WRITE_DELAY -packDocHistory = (doc_id, callback) -> - util.log "starting pack operation for #{doc_id}" - db.docHistory.find({doc_id:mongojs.ObjectId(doc_id),pack:{$exists:false}}).sort {v:1}, (err, docs) -> - packs = [] - top = null - origDocs = docs.length - # keep the last KEEP_OPS as individual ops - docs = docs.slice(0,-KEEP_OPS) - docs.forEach (d,i) -> - sz = BSON.calculateObjectSize(d) - if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE - top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} - top.sz += sz - top.v_end = d.v - top.meta.end_ts = d.meta.end_ts - return - else if sz < MAX_SIZE - # create a new pack - top = _.clone(d) - top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] - top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } - top.sz = sz - delete top.op - delete top._id - packs.push top - else - # keep the op - util.log 'keeping large op unchanged (#{sz} bytes)' - - # only store packs with a sufficient number of ops, discard others - packs = packs.filter (packObj) -> - packObj.pack.length > MIN_COUNT - - util.log "docs #{origDocs} packs #{packs.length}" - if packs.length - async.eachSeries packs, insertPack, (err, result) -> - if err? - console.log doc_id, err - else - util.log "done writing packs" - callback err, result +checkHistory = (doc_id, docs, callback) -> + util.log "checking history for #{doc_id}" + errors = 0 + prev = null + error = (args...) -> + errors++ + console.log.apply(null, args) + docs.forEach (d,i) -> + if d.pack? + n = d.pack.length + last = d.pack[n-1] + error('bad pack v_end', d) if d.v_end != last.v + error('bad pack start_ts', d) if d.meta.start_ts != d.pack[0].meta.start_ts + error('bad pack end_ts', d) if d.meta.end_ts != last.meta.end_ts + d.pack.forEach (p, i) -> + prev = v + v = p.v + error('bad version', v, 'in', p) if v <= prev else - util.log "no packs to write" - callback null, null + prev = v + v = d.v + error('bad version', v, 'in', d) if v <= prev + if errors + callback({errcount: errors}) + else + callback() readFile = (file, callback) -> ids = [] @@ -87,8 +151,10 @@ readFile = (file, callback) -> .then () -> callback(null, ids) -todoFile = process.argv[2] -doneFile = process.argv[3] +todoFile = cli.args[1] +doneFile = cli.args[2] +util.log "reading from #{todoFile}" +util.log "logging progress to #{doneFile}" fs.appendFileSync doneFile, '# starting pack run at ' + new Date() + '\n' shutdownRequested = false @@ -103,13 +169,13 @@ readFile todoFile, (err, todo) -> packDocHistory doc_id, (err, result) -> if err? return callback(err) - else + else if not options['dry-run'] fs.appendFileSync doneFile, doc_id + '\n' if shutdownRequested return callback('shutdown') setTimeout () -> callback(err, result) - , 1000 + , DOCUMENT_PACK_DELAY , (err, results) -> if err? console.log 'error:', err From ffeb1cccb6d699cd4b20c1bc32a49aec4718997c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 21 May 2015 16:56:05 +0100 Subject: [PATCH 125/549] move pack migration code into PackManager --- .../app/coffee/PackManager.coffee | 85 ++++++++++++++ services/track-changes/pack.coffee | 109 +++--------------- 2 files changed, 101 insertions(+), 93 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 0b72ea158c..3ff39040c1 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -1,5 +1,7 @@ async = require "async" _ = require "underscore" +{db, ObjectId} = require "./mongojs" +BSON=db.bson.BSON module.exports = PackManager = # The following functions implement methods like a mongo find, but @@ -249,3 +251,86 @@ module.exports = PackManager = return true newResults.slice(0, limit) if limit? return newResults + + convertDocsToPacks: (docs, callback) -> + MAX_SIZE = 1024*1024 # make these configurable parameters + MAX_COUNT = 1024 + MIN_COUNT = 100 + KEEP_OPS = 100 + packs = [] + top = null + # keep the last KEEP_OPS as individual ops + docs = docs.slice(0,-KEEP_OPS) + + docs.forEach (d,i) -> + if d.pack? + # util.log "skipping existing pack of #{d.pack.length}" + top = null if top? # flush the current pack + return # and try next + sz = BSON.calculateObjectSize(d) + if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE + top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} + top.sz += sz + top.v_end = d.v + top.meta.end_ts = d.meta.end_ts + return + else if sz < MAX_SIZE + # create a new pack + top = _.clone(d) + top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] + top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } + top.sz = sz + delete top.op + delete top._id + packs.push top + else + # keep the op + # util.log "keeping large op unchanged (#{sz} bytes)" + + # only store packs with a sufficient number of ops, discard others + packs = packs.filter (packObj) -> + packObj.pack.length > MIN_COUNT + callback(null, packs) + + checkHistory: (docs, callback) -> + errors = [] + prev = null + error = (args...) -> + errors.push args + docs.forEach (d,i) -> + if d.pack? + n = d.pack.length + last = d.pack[n-1] + error('bad pack v_end', d) if d.v_end != last.v + error('bad pack start_ts', d) if d.meta.start_ts != d.pack[0].meta.start_ts + error('bad pack end_ts', d) if d.meta.end_ts != last.meta.end_ts + d.pack.forEach (p, i) -> + prev = v + v = p.v + error('bad version', v, 'in', p) if v <= prev + else + prev = v + v = d.v + error('bad version', v, 'in', d) if v <= prev + if errors.length + callback(errors) + else + callback() + + insertPack: (packObj, callback) -> + bulk = db.docHistory.initializeOrderedBulkOp() + expect_nInserted = 1 + expect_nRemoved = packObj.pack.length + bulk.insert packObj + packObj.pack.forEach (op) -> + bulk.find({_id:op._id}).removeOne() + bulk.execute (err, result) -> + if err? + callback(err, result) + else if result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved + callback(new Error( + msg: 'unexpected result' + expected: {expect_nInserted, expect_nRemoved} + ), result) + else + callback(err, result) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index 27e9a347b8..d32b36ad9d 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -1,12 +1,11 @@ Settings = require "settings-sharelatex" fs = require("fs") -mongojs = require("mongojs") -ObjectId = mongojs.ObjectId -db = mongojs(Settings.mongo.url, ['docHistory']) +{db, ObjectId} = require "./app/coffee/mongojs" async = require("async") BSON=db.bson.BSON util = require 'util' _ = require 'underscore' +PackManager = require "./app/coffee/PackManager.coffee" lineReader = require "line-reader" cli = require "cli" @@ -15,11 +14,6 @@ options = cli.parse { 'fast': [false, 'no delays on writes'] } -MAX_SIZE = 1024*1024 -MAX_COUNT = 1024 -MIN_COUNT = 100 -KEEP_OPS = 100 - DB_WRITE_DELAY = if options.fast then 0 else 2000 DOCUMENT_PACK_DELAY = if options.fast then 0 else 1000 @@ -28,7 +22,7 @@ packDocHistory = (doc_id, callback) -> getDocHistory doc_id, (err, docs) -> return callback(err) if err? origDocs = docs.length - convertDocsToPacks docs, (err, packs) -> + PackManager.convertDocsToPacks docs, (err, packs) -> return callback(err) if err? util.log "docs #{origDocs} packs #{packs.length}" if packs.length @@ -45,103 +39,31 @@ packDocHistory = (doc_id, callback) -> # retrieve document ops/packs and check them getDocHistory = (doc_id, callback) -> - db.docHistory.find({doc_id:mongojs.ObjectId(doc_id)}).sort {v:1}, (err, docs) -> + db.docHistory.find({doc_id:ObjectId(doc_id)}).sort {v:1}, (err, docs) -> return callback(err) if err? # for safety, do a consistency check of the history - checkHistory doc_id, docs, (err) -> + util.log "checking history for #{doc_id}" + PackManager.checkHistory docs, (err) -> return callback(err) if err? callback err, docs -convertDocsToPacks = (docs, callback) -> - packs = [] - top = null - # keep the last KEEP_OPS as individual ops - docs = docs.slice(0,-KEEP_OPS) - - docs.forEach (d,i) -> - if d.pack? - util.log "skipping existing pack of #{d.pack.length}" - top = null if top? # flush the current pack - return # and try next - sz = BSON.calculateObjectSize(d) - if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE - top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} - top.sz += sz - top.v_end = d.v - top.meta.end_ts = d.meta.end_ts - return - else if sz < MAX_SIZE - # create a new pack - top = _.clone(d) - top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] - top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } - top.sz = sz - delete top.op - delete top._id - packs.push top - else - # keep the op - util.log "keeping large op unchanged (#{sz} bytes)" - - # only store packs with a sufficient number of ops, discard others - packs = packs.filter (packObj) -> - packObj.pack.length > MIN_COUNT - callback(null, packs) +safeInsert = (packObj, callback) -> + if shutdownRequested + return callback('shutdown') + PackManager.insertPack packObj, (err, result) -> + setTimeout () -> + callback(err,result) + , DB_WRITE_DELAY savePacks = (packs, callback) -> - async.eachSeries packs, insertPack, (err, result) -> + async.eachSeries packs, safeInsert, (err, result) -> if err? - console.log err + util.log "error writing packs" callback err, result else util.log "done writing packs" callback() -insertPack = (packObj, callback) -> - if shutdownRequested - return callback('shutdown') - bulk = db.docHistory.initializeOrderedBulkOp(); - expect_nInserted = 1 - expect_nRemoved = packObj.pack.length - util.log "insert #{expect_nInserted} pack, remove #{expect_nRemoved} ops" - bulk.insert packObj - packObj.pack.forEach (op) -> - bulk.find({_id:op._id}).removeOne() - bulk.execute (err, result) -> - if err? or result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved - console.log err, result - console.log 'nInserted', result.nInserted, 'nRemoved', result.nRemoved - setTimeout () -> - callback(err, result) - , DB_WRITE_DELAY - -checkHistory = (doc_id, docs, callback) -> - util.log "checking history for #{doc_id}" - errors = 0 - prev = null - error = (args...) -> - errors++ - console.log.apply(null, args) - docs.forEach (d,i) -> - if d.pack? - n = d.pack.length - last = d.pack[n-1] - error('bad pack v_end', d) if d.v_end != last.v - error('bad pack start_ts', d) if d.meta.start_ts != d.pack[0].meta.start_ts - error('bad pack end_ts', d) if d.meta.end_ts != last.meta.end_ts - d.pack.forEach (p, i) -> - prev = v - v = p.v - error('bad version', v, 'in', p) if v <= prev - else - prev = v - v = d.v - error('bad version', v, 'in', d) if v <= prev - if errors - callback({errcount: errors}) - else - callback() - readFile = (file, callback) -> ids = [] lineReader.eachLine file, (line) -> @@ -168,6 +90,7 @@ readFile todoFile, (err, todo) -> async.eachSeries pending, (doc_id, callback) -> packDocHistory doc_id, (err, result) -> if err? + console.log "ERROR:", err, result return callback(err) else if not options['dry-run'] fs.appendFileSync doneFile, doc_id + '\n' From adc2866a7d6255d6332a5b0b3488bbb841654478 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 22 May 2015 11:15:47 +0100 Subject: [PATCH 126/549] add check to exclude temporary ops from packs --- services/track-changes/app/coffee/PackManager.coffee | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 3ff39040c1..51ccecbc8e 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -263,10 +263,14 @@ module.exports = PackManager = docs = docs.slice(0,-KEEP_OPS) docs.forEach (d,i) -> + # skip existing packs if d.pack? - # util.log "skipping existing pack of #{d.pack.length}" - top = null if top? # flush the current pack - return # and try next + top = null + return + # skip temporary ops (we could pack these into temporary packs in future) + if d.expiresAt? + top = null + return sz = BSON.calculateObjectSize(d) if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} @@ -308,6 +312,7 @@ module.exports = PackManager = prev = v v = p.v error('bad version', v, 'in', p) if v <= prev + #error('expired op', p, 'in pack') if p.expiresAt? else prev = v v = d.v From 78f0bdbae3933b50174acaf9ac45328d155c894f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 22 May 2015 14:12:29 +0100 Subject: [PATCH 127/549] fix name of temporary parameter to match other methods --- services/track-changes/app/coffee/MongoManager.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index b1ea5cb180..1b99c53ab8 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -29,11 +29,11 @@ module.exports = MongoManager = else callback null, null - insertCompressedUpdates: (project_id, doc_id, updates, permanent, callback = (error) ->) -> + insertCompressedUpdates: (project_id, doc_id, updates, temporary, callback = (error) ->) -> jobs = [] for update in updates do (update) -> - jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, permanent, callback + jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, temporary, callback async.series jobs, callback insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> From 5c4afd5303df3e8b63e37e1a5119413097b16a4e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 22 May 2015 14:13:34 +0100 Subject: [PATCH 128/549] add docHistoryStats collection to keep track of updates to docs --- .../track-changes/app/coffee/MongoManager.coffee | 12 +++++++++++- services/track-changes/app/coffee/mongojs.coffee | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1b99c53ab8..1beb2b8983 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -34,7 +34,14 @@ module.exports = MongoManager = for update in updates do (update) -> jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, temporary, callback - async.series jobs, callback + async.series jobs, (err, results) -> + if not temporary + # keep track of updates to be packed + db.docHistoryStats.update {doc_id:ObjectId(doc_id)}, {$inc:{updates:updates.length}}, {upsert:true}, () -> + callback(err,results) + else + callback(err,results) + insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> update = { @@ -113,3 +120,6 @@ module.exports = MongoManager = db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } # TTL index for auto deleting week old temporary ops db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true } + # For finding documents which need packing + db.docHistoryStats.ensureIndex { doc_id: 1 }, { background: true } + db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index 8c2971945c..32efbc9a1d 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" -db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData"]) +db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats"]) module.exports = db: db ObjectId: mongojs.ObjectId From 1811ac2145f5ca611b9c08b407da8752ea2a54de Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 22 May 2015 15:27:15 +0100 Subject: [PATCH 129/549] added support for cleaning old expired ops in packs --- .../app/coffee/PackManager.coffee | 31 +++++++++++++++++++ services/track-changes/pack.coffee | 5 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 51ccecbc8e..c41a1e6bd8 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -339,3 +339,34 @@ module.exports = PackManager = ), result) else callback(err, result) + + deleteExpiredPackOps: (docs, callback) -> + now = Date.now() + toRemove = [] + toUpdate = [] + docs.forEach (d,i) -> + if d.pack? + newPack = d.pack.filter (op) -> + if op.expiresAt? then op.expiresAt > now else true + if newPack.length == 0 + toRemove.push d + else if newPack.length < d.pack.length + # adjust the pack properties + d.pack = newPack + first = d.pack[0] + last = d.pack[d.pack.length - 1] + d.v_end = last.v + d.meta.start_ts = first.meta.start_ts + d.meta.end_ts = last.meta.end_ts + toUpdate.push d + if toRemove.length or toUpdate.length + bulk = db.docHistory.initializeOrderedBulkOp() + toRemove.forEach (pack) -> + console.log "would remove", pack + #bulk.find({_id:pack._id}).removeOne() + toUpdate.forEach (pack) -> + console.log "would update", pack + #bulk.find({_id:pack._id}).updateOne(pack); + bulk.execute callback + else + callback() diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index d32b36ad9d..a9fb76f23a 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -45,7 +45,10 @@ getDocHistory = (doc_id, callback) -> util.log "checking history for #{doc_id}" PackManager.checkHistory docs, (err) -> return callback(err) if err? - callback err, docs + callback(err, docs) + #PackManager.deleteExpiredPackOps docs, (err) -> + # return callback(err) if err? + # callback err, docs safeInsert = (packObj, callback) -> if shutdownRequested From 66bca8d05cbfcf4df6d5ee594831d0d44b193995 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 May 2015 11:00:55 +0100 Subject: [PATCH 130/549] include the current date in the updates to docHistoryStats --- services/track-changes/app/coffee/MongoManager.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1beb2b8983..b322a5d878 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -37,7 +37,10 @@ module.exports = MongoManager = async.series jobs, (err, results) -> if not temporary # keep track of updates to be packed - db.docHistoryStats.update {doc_id:ObjectId(doc_id)}, {$inc:{updates:updates.length}}, {upsert:true}, () -> + db.docHistoryStats.update {doc_id:ObjectId(doc_id)}, { + $inc:{update_count:updates.length}, + $currentDate:{last_update:true} + }, {upsert:true}, () -> callback(err,results) else callback(err,results) From 2521c8f7515963e487036ab7e90ec7292e7a4609 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 May 2015 14:14:51 +0100 Subject: [PATCH 131/549] added npm packages for pack.coffee script --- services/track-changes/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 5cee855801..89b2bd8496 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -8,7 +8,9 @@ }, "dependencies": { "async": "~0.2.10", + "cli": "^0.6.6", "express": "3.3.5", + "line-reader": "^0.2.4", "mongojs": "^0.18.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", From 19d812734e1c32fcd6fc70755a589bbc68337f48 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 Jun 2015 10:35:36 +0100 Subject: [PATCH 132/549] make PackManager parameters configurable --- .../track-changes/app/coffee/PackManager.coffee | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index c41a1e6bd8..a7b7363ade 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -252,15 +252,16 @@ module.exports = PackManager = newResults.slice(0, limit) if limit? return newResults + MAX_SIZE: 1024*1024 # make these configurable parameters + MAX_COUNT: 1024 + MIN_COUNT: 100 + KEEP_OPS: 100 + convertDocsToPacks: (docs, callback) -> - MAX_SIZE = 1024*1024 # make these configurable parameters - MAX_COUNT = 1024 - MIN_COUNT = 100 - KEEP_OPS = 100 packs = [] top = null # keep the last KEEP_OPS as individual ops - docs = docs.slice(0,-KEEP_OPS) + docs = docs.slice(0,-PackManager.KEEP_OPS) docs.forEach (d,i) -> # skip existing packs @@ -272,13 +273,13 @@ module.exports = PackManager = top = null return sz = BSON.calculateObjectSize(d) - if top? && top.pack.length < MAX_COUNT && top.sz + sz < MAX_SIZE + if top? && top.pack.length < PackManager.MAX_COUNT && top.sz + sz < PackManager.MAX_SIZE top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} top.sz += sz top.v_end = d.v top.meta.end_ts = d.meta.end_ts return - else if sz < MAX_SIZE + else if sz < PackManager.MAX_SIZE # create a new pack top = _.clone(d) top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] @@ -293,7 +294,7 @@ module.exports = PackManager = # only store packs with a sufficient number of ops, discard others packs = packs.filter (packObj) -> - packObj.pack.length > MIN_COUNT + packObj.pack.length > PackManager.MIN_COUNT callback(null, packs) checkHistory: (docs, callback) -> From 27a3511b37b8cb8f27aa9967c9a1fe0e087c3567 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 Jun 2015 10:36:07 +0100 Subject: [PATCH 133/549] update docHistoryStats after packing ops --- services/track-changes/app/coffee/PackManager.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index a7b7363ade..43de0dfc87 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -339,7 +339,11 @@ module.exports = PackManager = expected: {expect_nInserted, expect_nRemoved} ), result) else - callback(err, result) + db.docHistoryStats.update {doc_id:packObj.doc_id}, { + $inc:{update_count:-expect_nRemoved}, + $currentDate:{last_packed:true} + }, {upsert:true}, () -> + callback(err, result) deleteExpiredPackOps: (docs, callback) -> now = Date.now() From 8668bb9be1dfb4d53d7e4d3fbda0924f11c002b0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 Jun 2015 10:36:31 +0100 Subject: [PATCH 134/549] use docHistoryStats to drive the packing script --- services/track-changes/pack.coffee | 77 ++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee index a9fb76f23a..59e64aadf4 100644 --- a/services/track-changes/pack.coffee +++ b/services/track-changes/pack.coffee @@ -10,6 +10,7 @@ PackManager = require "./app/coffee/PackManager.coffee" lineReader = require "line-reader" cli = require "cli" options = cli.parse { + 'update': ['u', 'find documents to pack from database'] 'dry-run': ['n', 'do not write to database'], 'fast': [false, 'no delays on writes'] } @@ -21,10 +22,16 @@ packDocHistory = (doc_id, callback) -> util.log "starting pack operation for #{doc_id}" getDocHistory doc_id, (err, docs) -> return callback(err) if err? - origDocs = docs.length + origDocs = 0 + origPacks = 0 + for d in docs + if d.pack? then origPacks++ else origDocs++ PackManager.convertDocsToPacks docs, (err, packs) -> return callback(err) if err? - util.log "docs #{origDocs} packs #{packs.length}" + total = 0 + for p in packs + total = total + p.pack.length + util.log "docs #{origDocs} packs #{origPacks} => packs #{packs.length} of #{total} ops" if packs.length if options['dry-run'] util.log 'dry-run, skipping write packs' @@ -76,34 +83,52 @@ readFile = (file, callback) -> .then () -> callback(null, ids) -todoFile = cli.args[1] -doneFile = cli.args[2] -util.log "reading from #{todoFile}" -util.log "logging progress to #{doneFile}" -fs.appendFileSync doneFile, '# starting pack run at ' + new Date() + '\n' - shutdownRequested = false process.on 'SIGINT', () -> util.log "Gracefully shutting down from SIGINT" shutdownRequested = true -readFile todoFile, (err, todo) -> - readFile doneFile, (err, done) -> - pending = _.difference todo, done - async.eachSeries pending, (doc_id, callback) -> - packDocHistory doc_id, (err, result) -> - if err? - console.log "ERROR:", err, result - return callback(err) - else if not options['dry-run'] - fs.appendFileSync doneFile, doc_id + '\n' - if shutdownRequested - return callback('shutdown') - setTimeout () -> - callback(err, result) - , DOCUMENT_PACK_DELAY - , (err, results) -> +processUpdates = (pending) -> + async.eachSeries pending, (doc_id, callback) -> + packDocHistory doc_id, (err, result) -> if err? - console.log 'error:', err - util.log 'closing db' + console.log "ERROR:", err, result + return callback(err) + else if not options['dry-run'] && doneFile? + fs.appendFileSync doneFile, doc_id + '\n' + if shutdownRequested + return callback('shutdown') + setTimeout () -> + callback(err, result) + , DOCUMENT_PACK_DELAY + , (err, results) -> + if err? + console.log 'error:', err + util.log 'closing db' + db.close() + +if options['update'] + util.log 'checking for updates' + db.docHistoryStats.find({ + update_count: {$gt : PackManager.MIN_COUNT} + }).sort({ + update_count:-1 + }).limit 1000, (error, results) -> + if err? + utils.log 'error', error db.close() + return + util.log "found #{results.length} documents to pack" + pending = _.pluck results, 'doc_id' + processUpdates pending +else + todoFile = cli.args[1] + doneFile = cli.args[2] + util.log "reading from #{todoFile}" + util.log "logging progress to #{doneFile}" + fs.appendFileSync doneFile, '# starting pack run at ' + new Date() + '\n' + + readFile todoFile, (err, todo) -> + readFile doneFile, (err, done) -> + pending = _.difference todo, done + processUpdates pending From 3f2e4b0c11b3ea29e7f411c53683c45d299c7269 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 4 Jun 2015 16:21:40 +0100 Subject: [PATCH 135/549] move pack script functionality into PackManager --- .../app/coffee/PackManager.coffee | 79 ++++++++++- services/track-changes/pack.coffee | 134 ------------------ 2 files changed, 78 insertions(+), 135 deletions(-) delete mode 100644 services/track-changes/pack.coffee diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 43de0dfc87..3311dbbb5a 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -2,6 +2,8 @@ async = require "async" _ = require "underscore" {db, ObjectId} = require "./mongojs" BSON=db.bson.BSON +logger = require "logger-sharelatex" +LockManager = require "./LockManager" module.exports = PackManager = # The following functions implement methods like a mongo find, but @@ -325,26 +327,101 @@ module.exports = PackManager = insertPack: (packObj, callback) -> bulk = db.docHistory.initializeOrderedBulkOp() + doc_id = packObj.doc_id expect_nInserted = 1 expect_nRemoved = packObj.pack.length + logger.log {doc_id: doc_id}, "adding pack, removing #{expect_nRemoved} ops" bulk.insert packObj packObj.pack.forEach (op) -> bulk.find({_id:op._id}).removeOne() bulk.execute (err, result) -> if err? + logger.error {doc_id: doc_id}, "error adding pack" callback(err, result) else if result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved + logger.error {doc_id: doc_id, result}, "unexpected result adding pack" callback(new Error( msg: 'unexpected result' expected: {expect_nInserted, expect_nRemoved} ), result) else - db.docHistoryStats.update {doc_id:packObj.doc_id}, { + db.docHistoryStats.update {doc_id:doc_id}, { $inc:{update_count:-expect_nRemoved}, $currentDate:{last_packed:true} }, {upsert:true}, () -> callback(err, result) + # retrieve document ops/packs and check them + getDocHistory: (doc_id, callback) -> + db.docHistory.find({doc_id:ObjectId(doc_id)}).sort {v:1}, (err, docs) -> + return callback(err) if err? + # for safety, do a consistency check of the history + logger.log {doc_id}, "checking history for document" + PackManager.checkHistory docs, (err) -> + return callback(err) if err? + callback(err, docs) + #PackManager.deleteExpiredPackOps docs, (err) -> + # return callback(err) if err? + # callback err, docs + + packDocHistory: (doc_id, options, callback) -> + if typeof callback == "undefined" and typeof options == 'function' + callback = options + options = {} + LockManager.runWithLock( + "HistoryLock:#{doc_id}", + (releaseLock) -> + PackManager._packDocHistory(doc_id, options, releaseLock) + , callback + ) + + _packDocHistory: (doc_id, options, callback) -> + logger.log {doc_id},"starting pack operation for document history" + + PackManager.getDocHistory doc_id, (err, docs) -> + return callback(err) if err? + origDocs = 0 + origPacks = 0 + for d in docs + if d.pack? then origPacks++ else origDocs++ + PackManager.convertDocsToPacks docs, (err, packs) -> + return callback(err) if err? + total = 0 + for p in packs + total = total + p.pack.length + logger.log {doc_id, origDocs, origPacks, newPacks: packs.length, totalOps: total}, "document stats" + if packs.length + if options['dry-run'] + logger.log {doc_id}, 'dry-run, skipping write packs' + return callback() + PackManager.savePacks packs, (err) -> + return callback(err) if err? + # check the history again + PackManager.getDocHistory doc_id, callback + else + logger.log {doc_id}, "no packs to write" + # keep a record that we checked this one to avoid rechecking it + db.docHistoryStats.update {doc_id:doc_id}, { + $currentDate:{last_checked:true} + }, {upsert:true}, () -> + callback null, null + + DB_WRITE_DELAY: 2000 + + savePacks: (packs, callback) -> + async.eachSeries packs, PackManager.safeInsert, (err, result) -> + if err? + logger.log {err, result}, "error writing packs" + callback err, result + else + callback() + + safeInsert: (packObj, callback) -> + PackManager.insertPack packObj, (err, result) -> + setTimeout () -> + callback(err,result) + , PackManager.DB_WRITE_DELAY + deleteExpiredPackOps: (docs, callback) -> now = Date.now() toRemove = [] diff --git a/services/track-changes/pack.coffee b/services/track-changes/pack.coffee deleted file mode 100644 index 59e64aadf4..0000000000 --- a/services/track-changes/pack.coffee +++ /dev/null @@ -1,134 +0,0 @@ -Settings = require "settings-sharelatex" -fs = require("fs") -{db, ObjectId} = require "./app/coffee/mongojs" -async = require("async") -BSON=db.bson.BSON -util = require 'util' -_ = require 'underscore' -PackManager = require "./app/coffee/PackManager.coffee" - -lineReader = require "line-reader" -cli = require "cli" -options = cli.parse { - 'update': ['u', 'find documents to pack from database'] - 'dry-run': ['n', 'do not write to database'], - 'fast': [false, 'no delays on writes'] -} - -DB_WRITE_DELAY = if options.fast then 0 else 2000 -DOCUMENT_PACK_DELAY = if options.fast then 0 else 1000 - -packDocHistory = (doc_id, callback) -> - util.log "starting pack operation for #{doc_id}" - getDocHistory doc_id, (err, docs) -> - return callback(err) if err? - origDocs = 0 - origPacks = 0 - for d in docs - if d.pack? then origPacks++ else origDocs++ - PackManager.convertDocsToPacks docs, (err, packs) -> - return callback(err) if err? - total = 0 - for p in packs - total = total + p.pack.length - util.log "docs #{origDocs} packs #{origPacks} => packs #{packs.length} of #{total} ops" - if packs.length - if options['dry-run'] - util.log 'dry-run, skipping write packs' - return callback() - savePacks packs, (err) -> - return callback(err) if err? - # check the history again - getDocHistory doc_id, callback - else - util.log "no packs to write" - callback null, null - -# retrieve document ops/packs and check them -getDocHistory = (doc_id, callback) -> - db.docHistory.find({doc_id:ObjectId(doc_id)}).sort {v:1}, (err, docs) -> - return callback(err) if err? - # for safety, do a consistency check of the history - util.log "checking history for #{doc_id}" - PackManager.checkHistory docs, (err) -> - return callback(err) if err? - callback(err, docs) - #PackManager.deleteExpiredPackOps docs, (err) -> - # return callback(err) if err? - # callback err, docs - -safeInsert = (packObj, callback) -> - if shutdownRequested - return callback('shutdown') - PackManager.insertPack packObj, (err, result) -> - setTimeout () -> - callback(err,result) - , DB_WRITE_DELAY - -savePacks = (packs, callback) -> - async.eachSeries packs, safeInsert, (err, result) -> - if err? - util.log "error writing packs" - callback err, result - else - util.log "done writing packs" - callback() - -readFile = (file, callback) -> - ids = [] - lineReader.eachLine file, (line) -> - result = line.match(/[0-9a-f]{24}/) - if result? - ids.push result[0] - .then () -> - callback(null, ids) - -shutdownRequested = false -process.on 'SIGINT', () -> - util.log "Gracefully shutting down from SIGINT" - shutdownRequested = true - -processUpdates = (pending) -> - async.eachSeries pending, (doc_id, callback) -> - packDocHistory doc_id, (err, result) -> - if err? - console.log "ERROR:", err, result - return callback(err) - else if not options['dry-run'] && doneFile? - fs.appendFileSync doneFile, doc_id + '\n' - if shutdownRequested - return callback('shutdown') - setTimeout () -> - callback(err, result) - , DOCUMENT_PACK_DELAY - , (err, results) -> - if err? - console.log 'error:', err - util.log 'closing db' - db.close() - -if options['update'] - util.log 'checking for updates' - db.docHistoryStats.find({ - update_count: {$gt : PackManager.MIN_COUNT} - }).sort({ - update_count:-1 - }).limit 1000, (error, results) -> - if err? - utils.log 'error', error - db.close() - return - util.log "found #{results.length} documents to pack" - pending = _.pluck results, 'doc_id' - processUpdates pending -else - todoFile = cli.args[1] - doneFile = cli.args[2] - util.log "reading from #{todoFile}" - util.log "logging progress to #{doneFile}" - fs.appendFileSync doneFile, '# starting pack run at ' + new Date() + '\n' - - readFile todoFile, (err, todo) -> - readFile doneFile, (err, done) -> - pending = _.difference todo, done - processUpdates pending From 289616d3db831f2cce78c9536e05cdf8e0e81b7c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 4 Jun 2015 16:23:49 +0100 Subject: [PATCH 136/549] added a /doc/:doc_id/pack endpoint --- services/track-changes/app.coffee | 2 ++ services/track-changes/app/coffee/HttpController.coffee | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 08a8b66113..6d12e37497 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -23,6 +23,8 @@ app.post "/project/:project_id/flush", HttpController.flushProject app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore +app.post "/doc/:doc_id/pack", HttpController.packDoc + app.get "/status", (req, res, next) -> res.send "track-changes is alive" diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 71df05a8f8..d802f193bc 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -1,5 +1,6 @@ UpdatesManager = require "./UpdatesManager" DiffManager = require "./DiffManager" +PackManager = require "./PackManager" RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" @@ -19,6 +20,13 @@ module.exports = HttpController = return next(error) if error? res.send 204 + packDoc: (req, res, next = (error) ->) -> + doc_id = req.params.doc_id + logger.log doc_id: doc_id, "packing doc history" + PackManager.packDocHistory doc_id, (error) -> + return next(error) if error? + res.send 204 + getDiff: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id From 23d2518ebbaa8fbf78faa77956874a19cec36cc2 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 4 Jun 2015 16:24:35 +0100 Subject: [PATCH 137/549] added a /pack endpoint to fork a pack worker --- services/track-changes/app.coffee | 15 +++++ .../app/coffee/PackWorker.coffee | 60 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 services/track-changes/app/coffee/PackWorker.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 6d12e37497..b0ae08dca5 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -7,6 +7,8 @@ Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) +child_process = require "child_process" + HttpController = require "./app/js/HttpController" express = require "express" app = express() @@ -25,6 +27,19 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post "/doc/:doc_id/pack", HttpController.packDoc +packWorker = null # use a single packing worker + +app.post "/pack", (req, res, next) -> + if packWorker? + res.send "pack already running" + else + logger.log "running pack" + packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js') + packWorker.on 'exit', (code, signal) -> + logger.log {code, signal}, "history auto pack exited" + packWorker = null + res.send "pack started" + app.get "/status", (req, res, next) -> res.send "track-changes is alive" diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee new file mode 100644 index 0000000000..8366343de5 --- /dev/null +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -0,0 +1,60 @@ +async = require "async" +_ = require "underscore" +{db, ObjectId} = require "./mongojs" +BSON=db.bson.BSON +logger = require "logger-sharelatex" +logger.initialize("track-changes-packworker") +LockManager = require "./LockManager" +PackManager = require "./PackManager" + +# this worker script is forked by the main process to look for +# document histories which can be packed + +DOCUMENT_PACK_DELAY = 1000 + +logger.log 'checking for updates' + +finish = () -> + logger.log 'closing db' + db.close () -> + logger.log 'exiting from pack worker' + process.exit() + +processUpdates = (pending) -> + async.eachSeries pending, (doc_id, callback) -> + PackManager.packDocHistory doc_id, (err, result) -> + if err? + logger.error {err, result}, "error in pack worker" + return callback(err) + setTimeout () -> + callback(err, result) + , DOCUMENT_PACK_DELAY + , (err, results) -> + if err? + logger.error {err}, 'error in pack worker processUpdates' + finish() + +# find the documents which can be packed, by checking the number of +# unpacked updates in the docHistoryStats collection + +db.docHistoryStats.find({ + update_count: {$gt : PackManager.MIN_COUNT} +}).sort({ + update_count:-1 +}).limit 1000, (err, results) -> + if err? + logger.log {err}, 'error checking for updates' + finish() + return + results = _.filter results, (doc) -> + if doc.last_checked? and doc.last_checked > doc.last_update + # skip documents which don't have any updates since last check + return false + else if doc.last_packed? and doc.last_packed > doc.last_update + # skip documents which don't have any updates since last pack + return false + else + return true + pending = _.pluck results, 'doc_id' + logger.log "found #{pending.length} documents to pack" + processUpdates pending From 76d1350593dd0405db16f47f262a1314c05dd700 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 5 Jun 2015 10:59:09 +0100 Subject: [PATCH 138/549] fix unit tests packManager now uses LockManager, which tries to talk to redis and needs to be replaced by a stub in the unit tests. --- .../test/unit/coffee/HttpController/HttpControllerTests.coffee | 1 + .../test/unit/coffee/MongoManager/MongoManagerTests.coffee | 3 +++ 2 files changed, 4 insertions(+) diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index f756cbb0e6..7c559c0937 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -12,6 +12,7 @@ describe "HttpController", -> "./UpdatesManager": @UpdatesManager = {} "./DiffManager": @DiffManager = {} "./RestoreManager": @RestoreManager = {} + "./PackManager": @PackManager = {} @doc_id = "doc-id-123" @project_id = "project-id-123" @next = sinon.stub() diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index bb9162bb6c..24588de6ea 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -3,6 +3,7 @@ chai = require('chai') should = chai.should() expect = chai.expect modulePath = "../../../../app/js/MongoManager.js" +packModulePath = "../../../../app/js/PackManager.js" SandboxedModule = require('sandboxed-module') {ObjectId} = require("mongojs") tk = require "timekeeper" @@ -12,6 +13,8 @@ describe "MongoManager", -> tk.freeze(new Date()) @MongoManager = SandboxedModule.require modulePath, requires: "./mongojs" : { db: @db = {}, ObjectId: ObjectId } + "./PackManager" : SandboxedModule.require packModulePath, requires: + "./LockManager" : {} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From 775f5ebbe1b6b47c5e491c04be6f356e2e67d787 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 5 Jun 2015 13:38:47 +0100 Subject: [PATCH 139/549] add configurable limit, delay and timeout to /pack via query string --- services/track-changes/app.coffee | 3 ++- .../app/coffee/PackWorker.coffee | 23 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index b0ae08dca5..8d5fd9c082 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -34,7 +34,8 @@ app.post "/pack", (req, res, next) -> res.send "pack already running" else logger.log "running pack" - packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js') + packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js', + [req.query.limit, req.query.delay, req.query.timeout]) packWorker.on 'exit', (code, signal) -> logger.log {code, signal}, "history auto pack exited" packWorker = null diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 8366343de5..c9076c268d 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -10,9 +10,23 @@ PackManager = require "./PackManager" # this worker script is forked by the main process to look for # document histories which can be packed -DOCUMENT_PACK_DELAY = 1000 +LIMIT = Number(process.argv[2]) || 1000 +DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 +TIMEOUT = Number(process.argv[4]) || 30*60*1000 -logger.log 'checking for updates' +shutDownRequested = false +setTimeout () -> + logger.log "pack timed out, requesting shutdown" + # start the shutdown on the next pack + shutDownRequested = true + # do a hard shutdown after a further 5 minutes + setTimeout () -> + logger.error "HARD TIMEOUT in pack worker" + process.exit() + , 5*60*1000 +, TIMEOUT + +logger.log "checking for updates, limit=#{LIMIT}, delay=#{DOCUMENT_PACK_DELAY}, timeout=#{TIMEOUT}" finish = () -> logger.log 'closing db' @@ -26,6 +40,9 @@ processUpdates = (pending) -> if err? logger.error {err, result}, "error in pack worker" return callback(err) + if shutDownRequested + logger.error "shutting down pack worker" + return callback(new Error("shutdown")) setTimeout () -> callback(err, result) , DOCUMENT_PACK_DELAY @@ -41,7 +58,7 @@ db.docHistoryStats.find({ update_count: {$gt : PackManager.MIN_COUNT} }).sort({ update_count:-1 -}).limit 1000, (err, results) -> +}).limit LIMIT, (err, results) -> if err? logger.log {err}, 'error checking for updates' finish() From ae047ecf76c36fc44023d9c1ab565038ffe0f44d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 6 Aug 2015 10:00:09 -0300 Subject: [PATCH 140/549] init s3 feature --- .../app/coffee/DocArquiveManager.coffee | 24 +++++++++++++++++++ .../config/settings.defaults.coffee | 8 +++++++ 2 files changed, 32 insertions(+) create mode 100644 services/track-changes/app/coffee/DocArquiveManager.coffee diff --git a/services/track-changes/app/coffee/DocArquiveManager.coffee b/services/track-changes/app/coffee/DocArquiveManager.coffee new file mode 100644 index 0000000000..f088a5d57e --- /dev/null +++ b/services/track-changes/app/coffee/DocArquiveManager.coffee @@ -0,0 +1,24 @@ +MongoManager = require "./MongoManager" +Errors = require "./Errors" +logger = require "logger-sharelatex" +_ = require "underscore" +async = require "async" +settings = require("settings-sharelatex") +request = require("request") +crypto = require("crypto") +thirtySeconds = 30 * 1000 + +module.exports = DocArchiveManager = + + buildS3Options: (content, key)-> + return { + aws: + key: settings.filestore.s3.key + secret: settings.filestore.s3.secret + bucket: settings.filestore.stores.user_files + timeout: thirtySeconds + json: content + #headers: + # 'content-md5': crypto.createHash("md5").update(JSON.stringify(content)).digest("hex") + uri:"https://#{settings.filestore.stores.user_files}.s3.amazonaws.com/#{key}" + } \ No newline at end of file diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 0a3e8f18a5..24e9a7e976 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -19,3 +19,11 @@ module.exports = host: "localhost" port: 6379 pass: "" + + #filestore: + # backend: "s3" + # stores: + # user_files: "sharelatex-dev" + # s3: + # key: "" + # secret: "" From 028fe2fa030b3f5cb28e31dc454e491162583063 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 6 Aug 2015 11:11:43 -0300 Subject: [PATCH 141/549] archive docChanges list to s3 --- services/track-changes/app.coffee | 3 ++ .../app/coffee/DocArchiveManager.coffee | 49 +++++++++++++++++++ .../app/coffee/DocArquiveManager.coffee | 24 --------- .../app/coffee/HttpController.coffee | 8 +++ .../app/coffee/MongoManager.coffee | 6 +++ .../track-changes/app/coffee/mongojs.coffee | 2 +- 6 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 services/track-changes/app/coffee/DocArchiveManager.coffee delete mode 100644 services/track-changes/app/coffee/DocArquiveManager.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 8d5fd9c082..63ed85df86 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -27,6 +27,9 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post "/doc/:doc_id/pack", HttpController.packDoc +if Settings.filestore?.backend == "s3" + app.get '/project/:project_id/archive', HttpController.archiveProject + packWorker = null # use a single packing worker app.post "/pack", (req, res, next) -> diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee new file mode 100644 index 0000000000..1d8af8b8c0 --- /dev/null +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -0,0 +1,49 @@ +MongoManager = require "./MongoManager" +logger = require "logger-sharelatex" +_ = require "underscore" +async = require "async" +settings = require("settings-sharelatex") +request = require("request") +crypto = require("crypto") +thirtySeconds = 30 * 1000 + +module.exports = DocArchiveManager = + + archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> + MongoManager.getProjectsDocs project_id, (error, docs) -> + if error? + return callback(error) + else if !docs? + return callback new Error("No docs for project #{project_id}") + jobs = _.map docs, (doc) -> + (cb)-> DocArchiveManager.archiveDocChanges project_id, doc._id, cb + async.series jobs, callback + + + archiveDocChanges: (project_id, doc_id, callback)-> + MongoManager.getDocChanges doc_id, (error, docChanges) -> + logger.log project_id: project_id, doc_id: doc_id, "sending doc changes to s3" + options = DocArchiveManager.buildS3Options(docChanges, project_id+"/changes-"+doc_id) + request.put options, (err, res)-> + md5lines = crypto.createHash("md5").update(JSON.stringify(docChanges)).digest("hex") + md5response = res.headers.etag.toString().replace(/\"/g, '') + if err? || res.statusCode != 200 + logger.err err:err, res:res, "something went wrong archiving doc changes in aws" + return callback new Error("Error in S3 request") + if md5lines != md5response + logger.err responseMD5:md5response, linesMD5:md5lines, "error in response md5 from s3" + return callback new Error("Error in S3 md5 response") + #MongoManager.markDocAsArchived doc._id, doc.rev, (error) -> + # return callback(error) if error? + callback() + + buildS3Options: (content, key)-> + return { + aws: + key: settings.filestore.s3.key + secret: settings.filestore.s3.secret + bucket: settings.filestore.stores.user_files + timeout: thirtySeconds + json: content + uri:"https://#{settings.filestore.stores.user_files}.s3.amazonaws.com/#{key}" + } \ No newline at end of file diff --git a/services/track-changes/app/coffee/DocArquiveManager.coffee b/services/track-changes/app/coffee/DocArquiveManager.coffee deleted file mode 100644 index f088a5d57e..0000000000 --- a/services/track-changes/app/coffee/DocArquiveManager.coffee +++ /dev/null @@ -1,24 +0,0 @@ -MongoManager = require "./MongoManager" -Errors = require "./Errors" -logger = require "logger-sharelatex" -_ = require "underscore" -async = require "async" -settings = require("settings-sharelatex") -request = require("request") -crypto = require("crypto") -thirtySeconds = 30 * 1000 - -module.exports = DocArchiveManager = - - buildS3Options: (content, key)-> - return { - aws: - key: settings.filestore.s3.key - secret: settings.filestore.s3.secret - bucket: settings.filestore.stores.user_files - timeout: thirtySeconds - json: content - #headers: - # 'content-md5': crypto.createHash("md5").update(JSON.stringify(content)).digest("hex") - uri:"https://#{settings.filestore.stores.user_files}.s3.amazonaws.com/#{key}" - } \ No newline at end of file diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index d802f193bc..901d96e5de 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -3,6 +3,7 @@ DiffManager = require "./DiffManager" PackManager = require "./PackManager" RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" +DocArchiveManager = require "./DocArchiveManager" module.exports = HttpController = flushDoc: (req, res, next = (error) ->) -> @@ -66,3 +67,10 @@ module.exports = HttpController = RestoreManager.restoreToBeforeVersion project_id, doc_id, version, user_id, (error) -> return next(error) if error? res.send 204 + + archiveProject: (req, res, next = (error) ->) -> + project_id = req.params.project_id + logger.log project_id: project_id, "archiving all track changes" + DocArchiveManager.archiveAllDocsChanges project_id, (error) -> + return next(error) if error? + res.send 204 \ No newline at end of file diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index b322a5d878..4465ba215c 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -126,3 +126,9 @@ module.exports = MongoManager = # For finding documents which need packing db.docHistoryStats.ensureIndex { doc_id: 1 }, { background: true } db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } + + getProjectsDocs: (project_id, callback)-> + db.docs.find project_id: ObjectId(project_id.toString()), {}, callback + + getDocChanges: (doc_id, callback)-> + db.docHistory.find doc_id: ObjectId(doc_id.toString()), {}, callback diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index 32efbc9a1d..5c1e9ea252 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" -db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats"]) +db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats", "docs"]) module.exports = db: db ObjectId: mongojs.ObjectId From 438c4f4d0c114b47e8e3b14acb8fd2ce0ab486a9 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 6 Aug 2015 15:46:44 -0300 Subject: [PATCH 142/549] using mongoexport for s3 archive --- .../app/coffee/DocArchiveManager.coffee | 30 ++------ .../track-changes/app/coffee/MongoAWS.coffee | 69 +++++++++++++++++++ .../config/settings.defaults.coffee | 8 ++- services/track-changes/package.json | 4 +- 4 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 services/track-changes/app/coffee/MongoAWS.coffee diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 1d8af8b8c0..3a308d03d8 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -1,4 +1,5 @@ MongoManager = require "./MongoManager" +MongoAWS = require "./MongoAWS" logger = require "logger-sharelatex" _ = require "underscore" async = require "async" @@ -21,29 +22,6 @@ module.exports = DocArchiveManager = archiveDocChanges: (project_id, doc_id, callback)-> - MongoManager.getDocChanges doc_id, (error, docChanges) -> - logger.log project_id: project_id, doc_id: doc_id, "sending doc changes to s3" - options = DocArchiveManager.buildS3Options(docChanges, project_id+"/changes-"+doc_id) - request.put options, (err, res)-> - md5lines = crypto.createHash("md5").update(JSON.stringify(docChanges)).digest("hex") - md5response = res.headers.etag.toString().replace(/\"/g, '') - if err? || res.statusCode != 200 - logger.err err:err, res:res, "something went wrong archiving doc changes in aws" - return callback new Error("Error in S3 request") - if md5lines != md5response - logger.err responseMD5:md5response, linesMD5:md5lines, "error in response md5 from s3" - return callback new Error("Error in S3 md5 response") - #MongoManager.markDocAsArchived doc._id, doc.rev, (error) -> - # return callback(error) if error? - callback() - - buildS3Options: (content, key)-> - return { - aws: - key: settings.filestore.s3.key - secret: settings.filestore.s3.secret - bucket: settings.filestore.stores.user_files - timeout: thirtySeconds - json: content - uri:"https://#{settings.filestore.stores.user_files}.s3.amazonaws.com/#{key}" - } \ No newline at end of file + MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + logger.log doc_id:doc_id, error: error, "mongoexport" + callback() \ No newline at end of file diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee new file mode 100644 index 0000000000..775a4a9a22 --- /dev/null +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -0,0 +1,69 @@ +settings = require "settings-sharelatex" +child_process = require "child_process" +mongoUri = require "mongo-uri"; +logger = require "logger-sharelatex" +AWS = require 'aws-sdk' +fs = require 'fs' + + +module.exports = MongoAWS = + + archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + MongoAWS.mongoDumpDocHistory doc_id, (error,filepath) -> + MongoAWS.s3upload project_id, doc_id, filepath, callback + + mongoDumpDocHistory: (doc_id, callback = (error, filepath) ->) -> + uriData = mongoUri.parse(settings.mongo.url); + filepath = settings.path.dumpFolder + '/' + doc_id + '.json' + + args = [] + args.push '-h' + args.push uriData.hosts[0] + args.push '-d' + args.push uriData.database + args.push '-c' + args.push 'docHistory' + args.push '-q' + args.push "{doc_id: ObjectId('#{doc_id}') }" + args.push '-o' + args.push filepath + + proc = child_process.spawn "mongoexport", args + + proc.on "error", callback + + stderr = "" + proc.stderr.on "data", (chunk) -> stderr += chunk.toString() + + proc.on "close", (code) -> + if code == 0 + return callback(null,filepath) + else + return callback(new Error("mongodump failed: #{stderr}"),null) + + s3upload: (project_id, doc_id, filepath, callback = (error) ->) -> + + AWS.config.update { + accessKeyId: settings.filestore.s3.key + secretAccessKey: settings.filestore.s3.secret + } + + s3Stream = require('s3-upload-stream')(new AWS.S3()); + + upload = s3Stream.upload { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id + } + + read = fs.createReadStream filepath + + #Handle errors. + upload.on 'error', callback + + #Handle upload completion. + upload.on 'uploaded', (details) -> + return callback(null) + + #Pipe the incoming filestream and up to S3. + read.pipe(upload); + diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 24e9a7e976..84c048dced 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -1,3 +1,6 @@ +Path = require('path') +TMP_DIR = Path.resolve(Path.join(__dirname, "../../", "tmp")) + module.exports = mongo: url: 'mongodb://127.0.0.1/sharelatex' @@ -23,7 +26,10 @@ module.exports = #filestore: # backend: "s3" # stores: - # user_files: "sharelatex-dev" + # user_files: "" # s3: # key: "" # secret: "" + + path: + dumpFolder: Path.join(TMP_DIR, "dumpFolder") \ No newline at end of file diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 89b2bd8496..79d2fa1254 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -18,7 +18,9 @@ "request": "~2.33.0", "redis-sharelatex": "~0.0.4", "redis": "~0.10.1", - "underscore": "~1.7.0" + "underscore": "~1.7.0", + "mongo-uri": "^0.1.2", + "s3-upload-stream": "^1.0.7" }, "devDependencies": { "chai": "~1.9.0", From bca48ac117923fae320b3cf55149629394abbc95 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 6 Aug 2015 17:09:36 -0300 Subject: [PATCH 143/549] add unarchive doc track from s3 --- services/track-changes/app.coffee | 1 + .../app/coffee/DocArchiveManager.coffee | 25 +++++++- .../app/coffee/HttpController.coffee | 9 ++- .../track-changes/app/coffee/MongoAWS.coffee | 63 +++++++++++++++++-- .../app/coffee/MongoManager.coffee | 3 + .../config/settings.defaults.coffee | 16 ++--- 6 files changed, 101 insertions(+), 16 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 63ed85df86..f7af275d62 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -29,6 +29,7 @@ app.post "/doc/:doc_id/pack", HttpController.packDoc if Settings.filestore?.backend == "s3" app.get '/project/:project_id/archive', HttpController.archiveProject + app.get '/project/:project_id/unarchive', HttpController.unArchiveProject packWorker = null # use a single packing worker diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 3a308d03d8..d7b71a8901 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -22,6 +22,25 @@ module.exports = DocArchiveManager = archiveDocChanges: (project_id, doc_id, callback)-> - MongoAWS.archiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "mongoexport" - callback() \ No newline at end of file + MongoManager.getDocChangesCount doc_id, (error, count) -> + if count == 0 + callback() + else + MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + logger.log doc_id:doc_id, error: error, "mongoexport" + callback() + + unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> + MongoManager.getProjectsDocs project_id, (error, docs) -> + if error? + return callback(error) + else if !docs? + return callback new Error("No docs for project #{project_id}") + jobs = _.map docs, (doc) -> + (cb)-> DocArchiveManager.unArchiveDocChanges project_id, doc._id, cb + async.series jobs, callback + + unArchiveDocChanges: (project_id, doc_id, callback)-> + MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> + logger.log doc_id:doc_id, error: error, "mongoimport" + callback() diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 901d96e5de..298311c180 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -70,7 +70,14 @@ module.exports = HttpController = archiveProject: (req, res, next = (error) ->) -> project_id = req.params.project_id - logger.log project_id: project_id, "archiving all track changes" + logger.log project_id: project_id, "archiving all track changes to s3" DocArchiveManager.archiveAllDocsChanges project_id, (error) -> + return next(error) if error? + res.send 204 + + unArchiveProject: (req, res, next = (error) ->) -> + project_id = req.params.project_id + logger.log project_id: project_id, "unarchiving all track changes from s3" + DocArchiveManager.unArchiveAllDocsChanges project_id, (error) -> return next(error) if error? res.send 204 \ No newline at end of file diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 775a4a9a22..3092209113 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -5,16 +5,22 @@ logger = require "logger-sharelatex" AWS = require 'aws-sdk' fs = require 'fs' - module.exports = MongoAWS = archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> - MongoAWS.mongoDumpDocHistory doc_id, (error,filepath) -> + MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> MongoAWS.s3upload project_id, doc_id, filepath, callback - mongoDumpDocHistory: (doc_id, callback = (error, filepath) ->) -> + unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + MongoAWS.s3download project_id, doc_id, (error, filepath) -> + if error == null + MongoAWS.mongoImportDocHistory filepath, callback + else + callback + + mongoExportDocHistory: (doc_id, callback = (error, filepath) ->) -> uriData = mongoUri.parse(settings.mongo.url); - filepath = settings.path.dumpFolder + '/' + doc_id + '.json' + filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonUp' args = [] args.push '-h' @@ -41,6 +47,33 @@ module.exports = MongoAWS = else return callback(new Error("mongodump failed: #{stderr}"),null) + mongoImportDocHistory: (filepath, callback = (error) ->) -> + + uriData = mongoUri.parse(settings.mongo.url); + + args = [] + args.push '-h' + args.push uriData.hosts[0] + args.push '-d' + args.push uriData.database + args.push '-c' + args.push 'docHistory' + args.push '--file' + args.push filepath + + proc = child_process.spawn "mongoimport", args + + proc.on "error", callback + + stderr = "" + proc.stderr.on "data", (chunk) -> stderr += chunk.toString() + + proc.on "close", (code) -> + if code == 0 + return callback(null,filepath) + else + return callback(new Error("mongodump failed: #{stderr}"),null) + s3upload: (project_id, doc_id, filepath, callback = (error) ->) -> AWS.config.update { @@ -67,3 +100,25 @@ module.exports = MongoAWS = #Pipe the incoming filestream and up to S3. read.pipe(upload); + s3download: (project_id, doc_id, callback = (error, filepath) ->) -> + + filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonDown' + + AWS.config.update { + accessKeyId: settings.filestore.s3.key + secretAccessKey: settings.filestore.s3.secret + } + + params = { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id + } + + s3 = new AWS.S3() + + s3.getObject params, (err, data) -> + if !err && data.ContentLength > 0 + fs.writeFile filepath, data.Body, (err) -> + return callback(null,filepath) + else + return callback(new Error("s3download failed: #{err}"),null) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 4465ba215c..a7898b79c4 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -132,3 +132,6 @@ module.exports = MongoManager = getDocChanges: (doc_id, callback)-> db.docHistory.find doc_id: ObjectId(doc_id.toString()), {}, callback + + getDocChangesCount: (doc_id, callback)-> + db.docHistory.count doc_id: ObjectId(doc_id.toString()), {}, callback diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 84c048dced..59fb94ea6a 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -23,13 +23,13 @@ module.exports = port: 6379 pass: "" - #filestore: - # backend: "s3" - # stores: - # user_files: "" - # s3: - # key: "" - # secret: "" + filestore: + backend: "s3" + stores: + user_files: "" + s3: + key: "" + secret: "" path: - dumpFolder: Path.join(TMP_DIR, "dumpFolder") \ No newline at end of file + dumpFolder: Path.join(TMP_DIR, "dumpFolder") From daa42bcea0f7278a21e5fccfbcbb6db6e6febab8 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 9 Aug 2015 15:47:47 -0300 Subject: [PATCH 144/549] change s3Stream lib --- .../app/coffee/DocArchiveManager.coffee | 14 +++-- .../track-changes/app/coffee/MongoAWS.coffee | 58 +++++++++---------- services/track-changes/package.json | 2 +- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index d7b71a8901..de769efa4f 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -24,11 +24,11 @@ module.exports = DocArchiveManager = archiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getDocChangesCount doc_id, (error, count) -> if count == 0 - callback() + return callback() else MongoAWS.archiveDocHistory project_id, doc_id, (error) -> logger.log doc_id:doc_id, error: error, "mongoexport" - callback() + return callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> MongoManager.getProjectsDocs project_id, (error, docs) -> @@ -41,6 +41,10 @@ module.exports = DocArchiveManager = async.series jobs, callback unArchiveDocChanges: (project_id, doc_id, callback)-> - MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "mongoimport" - callback() + MongoManager.getDocChangesCount doc_id, (error, count) -> + if count == 0 + return callback() + else + MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> + logger.log doc_id:doc_id, error: error, "mongoimport" + return callback() diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 3092209113..9a63ddab4d 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -4,15 +4,16 @@ mongoUri = require "mongo-uri"; logger = require "logger-sharelatex" AWS = require 'aws-sdk' fs = require 'fs' +S3S = require 's3-streams' module.exports = MongoAWS = archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> - MongoAWS.s3upload project_id, doc_id, filepath, callback + MongoAWS.s3upStream project_id, doc_id, filepath, callback unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> - MongoAWS.s3download project_id, doc_id, (error, filepath) -> + MongoAWS.s3downStream project_id, doc_id, (error, filepath) -> if error == null MongoAWS.mongoImportDocHistory filepath, callback else @@ -74,33 +75,28 @@ module.exports = MongoAWS = else return callback(new Error("mongodump failed: #{stderr}"),null) - s3upload: (project_id, doc_id, filepath, callback = (error) ->) -> + s3upStream: (project_id, doc_id, filepath, callback = (error) ->) -> AWS.config.update { accessKeyId: settings.filestore.s3.key secretAccessKey: settings.filestore.s3.secret } - - s3Stream = require('s3-upload-stream')(new AWS.S3()); - - upload = s3Stream.upload { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id + + upload = S3S.WriteStream new AWS.S3(), { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id } - read = fs.createReadStream filepath + fs.createReadStream(filepath) + .on 'open', (obj) -> + return 1 + .pipe(upload) + .on 'finish', () -> + return callback(null) + .on 'error', (err) -> + return callback(err) - #Handle errors. - upload.on 'error', callback - - #Handle upload completion. - upload.on 'uploaded', (details) -> - return callback(null) - - #Pipe the incoming filestream and up to S3. - read.pipe(upload); - - s3download: (project_id, doc_id, callback = (error, filepath) ->) -> + s3downStream: (project_id, doc_id, callback = (error, filepath) ->) -> filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonDown' @@ -108,17 +104,17 @@ module.exports = MongoAWS = accessKeyId: settings.filestore.s3.key secretAccessKey: settings.filestore.s3.secret } - - params = { + + download = S3S.ReadStream new AWS.S3(), { "Bucket": settings.filestore.stores.user_files, "Key": project_id+"/changes-"+doc_id } - s3 = new AWS.S3() - - s3.getObject params, (err, data) -> - if !err && data.ContentLength > 0 - fs.writeFile filepath, data.Body, (err) -> - return callback(null,filepath) - else - return callback(new Error("s3download failed: #{err}"),null) + download + .on 'open', (obj) -> + return 1 + .pipe(fs.createWriteStream(filepath)) + .on 'finish', () -> + return callback(null, filepath) + .on 'error', (err) -> + return callback(err, null) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 79d2fa1254..f82b618359 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -20,7 +20,7 @@ "redis": "~0.10.1", "underscore": "~1.7.0", "mongo-uri": "^0.1.2", - "s3-upload-stream": "^1.0.7" + "s3-streams": "^0.3.0" }, "devDependencies": { "chai": "~1.9.0", From 3bc538046886d39be704c56cfcd9533f9d7e1cbe Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 9 Aug 2015 17:50:15 -0300 Subject: [PATCH 145/549] handle inS3 flag --- .../app/coffee/DocArchiveManager.coffee | 8 ++++++-- services/track-changes/app/coffee/MongoAWS.coffee | 2 ++ .../track-changes/app/coffee/MongoManager.coffee | 15 ++++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index de769efa4f..6ab60f7b72 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -28,7 +28,9 @@ module.exports = DocArchiveManager = else MongoAWS.archiveDocHistory project_id, doc_id, (error) -> logger.log doc_id:doc_id, error: error, "mongoexport" - return callback() + MongoManager.markDocHistoryAsArchived doc_id, (error) -> + return callback(error) if error? + callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> MongoManager.getProjectsDocs project_id, (error, docs) -> @@ -47,4 +49,6 @@ module.exports = DocArchiveManager = else MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> logger.log doc_id:doc_id, error: error, "mongoimport" - return callback() + MongoManager.markDocHistoryAsUnarchived doc_id, (error) -> + return callback(error) if error? + callback() diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 9a63ddab4d..43dd59591b 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -11,11 +11,13 @@ module.exports = MongoAWS = archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> MongoAWS.s3upStream project_id, doc_id, filepath, callback + #delete temp file? unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> MongoAWS.s3downStream project_id, doc_id, (error, filepath) -> if error == null MongoAWS.mongoImportDocHistory filepath, callback + #delete temp file? else callback diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index a7898b79c4..a4011c1af3 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -130,8 +130,17 @@ module.exports = MongoManager = getProjectsDocs: (project_id, callback)-> db.docs.find project_id: ObjectId(project_id.toString()), {}, callback - getDocChanges: (doc_id, callback)-> - db.docHistory.find doc_id: ObjectId(doc_id.toString()), {}, callback - getDocChangesCount: (doc_id, callback)-> db.docHistory.count doc_id: ObjectId(doc_id.toString()), {}, callback + + markDocHistoryAsArchived: (doc_id, callback)-> + MongoManager.getLastCompressedUpdate doc_id, (error, update) -> + db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> + return callback(error) if error? + db.docHistory.remove { doc_id : doc_id, inS3 : { $exists : false } }, (error)-> + return callback(error) if error? + callback(error) + + markDocHistoryAsUnarchived: (doc_id, callback)-> + db.docHistory.update { doc_id: doc_id }, { $unset : { inS3 : true } }, { multi: true }, (error)-> + callback(error) From 6bc9c9010a9e1171dd40002d1e1047b336c71425 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 9 Aug 2015 19:52:32 -0300 Subject: [PATCH 146/549] handle auto unarchive track changes --- .../track-changes/app/coffee/DocArchiveManager.coffee | 6 +++++- services/track-changes/app/coffee/MongoManager.coffee | 9 ++++++++- services/track-changes/app/coffee/UpdatesManager.coffee | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 6ab60f7b72..9ac6e1163f 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -11,6 +11,8 @@ thirtySeconds = 30 * 1000 module.exports = DocArchiveManager = archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> + if settings.filestore?.backend != "s3" + return callback(null) MongoManager.getProjectsDocs project_id, (error, docs) -> if error? return callback(error) @@ -33,6 +35,8 @@ module.exports = DocArchiveManager = callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> + if settings.filestore?.backend != "s3" + return callback(null) MongoManager.getProjectsDocs project_id, (error, docs) -> if error? return callback(error) @@ -43,7 +47,7 @@ module.exports = DocArchiveManager = async.series jobs, callback unArchiveDocChanges: (project_id, doc_id, callback)-> - MongoManager.getDocChangesCount doc_id, (error, count) -> + MongoManager.getArchivedDocChanges doc_id, (error, count) -> if count == 0 return callback() else diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index a4011c1af3..536b94f876 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -47,6 +47,7 @@ module.exports = MongoManager = insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> + inS3 = update.inS3? update = { doc_id: ObjectId(doc_id.toString()) project_id: ObjectId(project_id.toString()) @@ -54,6 +55,9 @@ module.exports = MongoManager = meta: update.meta v: update.v } + if inS3? + update.inS3 = true + if temporary seconds = 1000 minutes = 60 * seconds @@ -117,7 +121,7 @@ module.exports = MongoManager = db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding all packs that affect a project (use a sparse index so only packs are included) db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, { background: true, sparse: true } - # For finding updates that don't yet have a project_id and need it inserting + # For finding updates that dont yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } @@ -133,6 +137,9 @@ module.exports = MongoManager = getDocChangesCount: (doc_id, callback)-> db.docHistory.count doc_id: ObjectId(doc_id.toString()), {}, callback + getArchivedDocChanges: (doc_id, callback)-> + db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback + markDocHistoryAsArchived: (doc_id, callback)-> MongoManager.getLastCompressedUpdate doc_id, (error, update) -> db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 92357fc2dc..1fbfe53d7e 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -6,6 +6,7 @@ WebApiManager = require "./WebApiManager" UpdateTrimmer = require "./UpdateTrimmer" logger = require "logger-sharelatex" async = require "async" +DocArchiveManager = require "./DocArchiveManager" module.exports = UpdatesManager = compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> @@ -94,7 +95,9 @@ module.exports = UpdatesManager = getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> return callback(error) if error? - MongoManager.getProjectUpdates project_id, options, callback + DocArchiveManager.unArchiveAllDocsChanges project_id, (error) -> + return callback(error,null) if error? + MongoManager.getProjectUpdates project_id, options, callback getProjectUpdatesWithUserInfo: (project_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.getProjectUpdates project_id, options, (error, updates) -> From fd4afb357449c7b5c1a56c94105463e8c3be36dc Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 14 Aug 2015 15:07:16 -0300 Subject: [PATCH 147/549] Archive changes, care about: versioin, expiresAt and Lock --- .../app/coffee/DocArchiveManager.coffee | 21 +++++++++++++------ .../track-changes/app/coffee/MongoAWS.coffee | 2 +- .../app/coffee/MongoManager.coffee | 11 +++++----- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 9ac6e1163f..54d05cab96 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -1,5 +1,6 @@ MongoManager = require "./MongoManager" MongoAWS = require "./MongoAWS" +LockManager = require "./LockManager" logger = require "logger-sharelatex" _ = require "underscore" async = require "async" @@ -19,20 +20,28 @@ module.exports = DocArchiveManager = else if !docs? return callback new Error("No docs for project #{project_id}") jobs = _.map docs, (doc) -> - (cb)-> DocArchiveManager.archiveDocChanges project_id, doc._id, cb + (cb)-> DocArchiveManager.archiveDocChangesWithLock project_id, doc._id, cb async.series jobs, callback + archiveDocChangesWithLock: (project_id, doc_id, callback = (error) ->) -> + LockManager.runWithLock( + "HistoryArchiveLock:#{doc_id}", + (releaseLock) -> + DocArchiveManager.archiveDocChanges project_id, doc_id, releaseLock + callback + ) archiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getDocChangesCount doc_id, (error, count) -> if count == 0 return callback() else - MongoAWS.archiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "mongoexport" - MongoManager.markDocHistoryAsArchived doc_id, (error) -> - return callback(error) if error? - callback() + MongoManager.getLastCompressedUpdate doc_id, (error, update) -> + MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + logger.log doc_id:doc_id, error: error, "mongoexport" + MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> + return callback(error) if error? + callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> if settings.filestore?.backend != "s3" diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 43dd59591b..96435fd064 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -33,7 +33,7 @@ module.exports = MongoAWS = args.push '-c' args.push 'docHistory' args.push '-q' - args.push "{doc_id: ObjectId('#{doc_id}') }" + args.push "{doc_id: ObjectId('#{doc_id}') , expiresAt: {$exists : false} }" args.push '-o' args.push filepath diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 536b94f876..9163da8009 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -140,13 +140,12 @@ module.exports = MongoManager = getArchivedDocChanges: (doc_id, callback)-> db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback - markDocHistoryAsArchived: (doc_id, callback)-> - MongoManager.getLastCompressedUpdate doc_id, (error, update) -> - db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> + markDocHistoryAsArchived: (doc_id, update, callback)-> + db.docHistory.update { v: update.v }, { $set : { inS3 : true } }, (error)-> + return callback(error) if error? + db.docHistory.remove { doc_id : doc_id, inS3 : { $exists : false }, v: { $lt : update.v }, expiresAt: {$exists : false} }, (error)-> return callback(error) if error? - db.docHistory.remove { doc_id : doc_id, inS3 : { $exists : false } }, (error)-> - return callback(error) if error? - callback(error) + callback(error) markDocHistoryAsUnarchived: (doc_id, callback)-> db.docHistory.update { doc_id: doc_id }, { $unset : { inS3 : true } }, { multi: true }, (error)-> From 26c8048729da0013ad51b16e12e2662004a41784 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 14 Aug 2015 19:19:54 -0300 Subject: [PATCH 148/549] change mongo stream method (still have a bug in bulk insert limit) --- .../track-changes/app/coffee/MongoAWS.coffee | 79 ++++++++++++++++++- services/track-changes/package.json | 4 +- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 96435fd064..d5d243cb2d 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -5,15 +5,92 @@ logger = require "logger-sharelatex" AWS = require 'aws-sdk' fs = require 'fs' S3S = require 's3-streams' +{db, ObjectId} = require "./mongojs" +JSONStream = require "JSONStream" +ReadlineStream = require "readline-stream" module.exports = MongoAWS = + bulkLimit: 10 + archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + query = { + doc_id: ObjectId(doc_id) + expiresAt: {$exists : false} + } + + AWS.config.update { + accessKeyId: settings.filestore.s3.key + secretAccessKey: settings.filestore.s3.secret + } + + upload = S3S.WriteStream new AWS.S3(), { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id + } + + db.docHistory.find(query) + .pipe JSONStream.stringify() + .pipe upload + .on 'finish', () -> + return callback(null) + + unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + + AWS.config.update { + accessKeyId: settings.filestore.s3.key + secretAccessKey: settings.filestore.s3.secret + } + + download = S3S.ReadStream new AWS.S3(), { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id + }, { + encoding: "utf8" + } + + lineStream = new ReadlineStream(); + ops = [] + + download + .on 'open', (obj) -> + return 1 + .pipe lineStream + .on 'data', (line) -> + if line.length > 2 + ops.push(JSON.parse(line)) + if ops.length > MongoAWS.bulkLimit + MongoAWS.handleBulk ops, () -> + ops.splice(0,ops.length) + .on 'end', () -> + MongoAWS.handleBulk ops, callback + .on 'error', (err) -> + return callback(err) + + handleBulk: (ops, cb) -> + bulk = db.docHistory.initializeUnorderedBulkOp(); + + for op in ops + op._id = ObjectId(op._id) + op.doc_id = ObjectId(op.doc_id) + op.project_id = ObjectId(op.project_id) + bulk.find({_id:op._id}).upsert().updateOne(op) + + bulk.execute (err, result) -> + if err? + logger.error err:err, "error bulking ReadlineStream" + else + logger.log result:result, "bulked ReadlineStream" + cb(err) + + + archiveDocHistoryExternal: (project_id, doc_id, callback = (error) ->) -> MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> MongoAWS.s3upStream project_id, doc_id, filepath, callback #delete temp file? - unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + + unArchiveDocHistoryExternal: (project_id, doc_id, callback = (error) ->) -> MongoAWS.s3downStream project_id, doc_id, (error, filepath) -> if error == null MongoAWS.mongoImportDocHistory filepath, callback diff --git a/services/track-changes/package.json b/services/track-changes/package.json index f82b618359..4fe112262a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -20,7 +20,9 @@ "redis": "~0.10.1", "underscore": "~1.7.0", "mongo-uri": "^0.1.2", - "s3-streams": "^0.3.0" + "s3-streams": "^0.3.0", + "JSONStream": "^1.0.4", + "readline-stream": "^1.0.1" }, "devDependencies": { "chai": "~1.9.0", From 20c3e15f930a0c7f4ff39b9f0acb21b04e75efb0 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 14 Aug 2015 19:58:38 -0300 Subject: [PATCH 149/549] fix bulk insert limit --- services/track-changes/app/coffee/MongoAWS.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index d5d243cb2d..9b4271c167 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -59,9 +59,9 @@ module.exports = MongoAWS = .on 'data', (line) -> if line.length > 2 ops.push(JSON.parse(line)) - if ops.length > MongoAWS.bulkLimit - MongoAWS.handleBulk ops, () -> - ops.splice(0,ops.length) + if ops.length == MongoAWS.bulkLimit + MongoAWS.handleBulk ops.slice(0), () -> + ops.splice(0,ops.length) .on 'end', () -> MongoAWS.handleBulk ops, callback .on 'error', (err) -> @@ -80,7 +80,7 @@ module.exports = MongoAWS = if err? logger.error err:err, "error bulking ReadlineStream" else - logger.log result:result, "bulked ReadlineStream" + logger.log count:ops.length, result:result, "bulked ReadlineStream" cb(err) From 04ec45529fa96a7dd485e6baab1703726cded0f3 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Tue, 18 Aug 2015 17:11:19 -0300 Subject: [PATCH 150/549] restore updates from S3 when exists fix: avoid rearchiving --- .../track-changes/app/coffee/MongoManager.coffee | 2 +- .../track-changes/app/coffee/UpdatesManager.coffee | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 9163da8009..44e1335e8c 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -135,7 +135,7 @@ module.exports = MongoManager = db.docs.find project_id: ObjectId(project_id.toString()), {}, callback getDocChangesCount: (doc_id, callback)-> - db.docHistory.count doc_id: ObjectId(doc_id.toString()), {}, callback + db.docHistory.count { doc_id : doc_id, inS3 : { $exists : false }}, {}, callback getArchivedDocChanges: (doc_id, callback)-> db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 1fbfe53d7e..c104ceed7a 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -95,9 +95,17 @@ module.exports = UpdatesManager = getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> return callback(error) if error? - DocArchiveManager.unArchiveAllDocsChanges project_id, (error) -> - return callback(error,null) if error? - MongoManager.getProjectUpdates project_id, options, callback + MongoManager.getProjectUpdates project_id, options, (error, updates) -> + jobs = [] + for update in updates + if update.inS3? + do (update) -> + jobs.push (callback) -> DocArchiveManager.unArchiveDocChanges update.project_id, update.doc_id, callback + if jobs.length? + async.series jobs, (err) -> + MongoManager.getProjectUpdates project_id, options, callback + else + callback(error, updates) getProjectUpdatesWithUserInfo: (project_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.getProjectUpdates project_id, options, (error, updates) -> From 98ce03b2f2ebf6523d9a299ba969fe1286fb71d8 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Aug 2015 10:38:31 -0300 Subject: [PATCH 151/549] replace docs collection to DocstoreHandler --- .../app/coffee/DocArchiveManager.coffee | 11 +++++----- .../app/coffee/DocstoreHandler.coffee | 20 +++++++++++++++++++ .../app/coffee/MongoManager.coffee | 3 --- .../track-changes/app/coffee/mongojs.coffee | 2 +- 4 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 services/track-changes/app/coffee/DocstoreHandler.coffee diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 54d05cab96..e4aab49f40 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -1,20 +1,18 @@ MongoManager = require "./MongoManager" MongoAWS = require "./MongoAWS" LockManager = require "./LockManager" +DocstoreHandler = require "./DocstoreHandler" logger = require "logger-sharelatex" _ = require "underscore" async = require "async" settings = require("settings-sharelatex") -request = require("request") -crypto = require("crypto") -thirtySeconds = 30 * 1000 module.exports = DocArchiveManager = archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> if settings.filestore?.backend != "s3" return callback(null) - MongoManager.getProjectsDocs project_id, (error, docs) -> + DocstoreHandler.getAllDocs project_id, (error, docs) -> if error? return callback(error) else if !docs? @@ -46,7 +44,7 @@ module.exports = DocArchiveManager = unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> if settings.filestore?.backend != "s3" return callback(null) - MongoManager.getProjectsDocs project_id, (error, docs) -> + DocstoreHandler.getAllDocs project_id, (error, docs) -> if error? return callback(error) else if !docs? @@ -65,3 +63,6 @@ module.exports = DocArchiveManager = MongoManager.markDocHistoryAsUnarchived doc_id, (error) -> return callback(error) if error? callback() + + getProjectsDocs: (project_id, callback)-> + db.docs.find project_id: ObjectId(project_id.toString()), {}, callback diff --git a/services/track-changes/app/coffee/DocstoreHandler.coffee b/services/track-changes/app/coffee/DocstoreHandler.coffee new file mode 100644 index 0000000000..7ca270fb0f --- /dev/null +++ b/services/track-changes/app/coffee/DocstoreHandler.coffee @@ -0,0 +1,20 @@ +request = require("request").defaults(jar: false) +logger = require "logger-sharelatex" +settings = require "settings-sharelatex" + +module.exports = DocstoreHandler = + + getAllDocs: (project_id, callback = (error) ->) -> + logger.log project_id: project_id, "getting all docs for project in docstore api" + url = "#{settings.apis.docstore.url}/project/#{project_id}/doc" + request.get { + url: url + json: true + }, (error, res, docs) -> + return callback(error) if error? + if 200 <= res.statusCode < 300 + callback(null, docs) + else + error = new Error("docstore api responded with non-success code: #{res.statusCode}") + logger.error err: error, project_id: project_id, "error getting all docs from docstore" + callback(error) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 44e1335e8c..a18fe0f2ae 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -131,9 +131,6 @@ module.exports = MongoManager = db.docHistoryStats.ensureIndex { doc_id: 1 }, { background: true } db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } - getProjectsDocs: (project_id, callback)-> - db.docs.find project_id: ObjectId(project_id.toString()), {}, callback - getDocChangesCount: (doc_id, callback)-> db.docHistory.count { doc_id : doc_id, inS3 : { $exists : false }}, {}, callback diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index 5c1e9ea252..32efbc9a1d 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" -db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats", "docs"]) +db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats"]) module.exports = db: db ObjectId: mongojs.ObjectId From 1ccba422c81074385d1285a10d6bd8c518c443bc Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Aug 2015 10:55:27 -0300 Subject: [PATCH 152/549] remove unused function --- services/track-changes/app/coffee/DocArchiveManager.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index e4aab49f40..e0d0d1bf1a 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -63,6 +63,3 @@ module.exports = DocArchiveManager = MongoManager.markDocHistoryAsUnarchived doc_id, (error) -> return callback(error) if error? callback() - - getProjectsDocs: (project_id, callback)-> - db.docs.find project_id: ObjectId(project_id.toString()), {}, callback From fcbe4aa92576b5a3d842a161724159eef285e3df Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Aug 2015 12:19:19 -0300 Subject: [PATCH 153/549] fix inS3 propagation --- .../app/coffee/MongoManager.coffee | 19 ++++++++++++------- .../app/coffee/UpdatesManager.coffee | 5 ++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index a18fe0f2ae..b366ce51b9 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,6 +1,7 @@ {db, ObjectId} = require "./mongojs" PackManager = require "./PackManager" async = require "async" +logger = require "logger-sharelatex" module.exports = MongoManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> @@ -47,7 +48,6 @@ module.exports = MongoManager = insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> - inS3 = update.inS3? update = { doc_id: ObjectId(doc_id.toString()) project_id: ObjectId(project_id.toString()) @@ -55,8 +55,6 @@ module.exports = MongoManager = meta: update.meta v: update.v } - if inS3? - update.inS3 = true if temporary seconds = 1000 @@ -132,18 +130,25 @@ module.exports = MongoManager = db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } getDocChangesCount: (doc_id, callback)-> - db.docHistory.count { doc_id : doc_id, inS3 : { $exists : false }}, {}, callback + db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, {}, callback getArchivedDocChanges: (doc_id, callback)-> db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback + remarkDocHistoryAsArchived: (doc_id, callback)-> + logger.log doc_id: doc_id, "remarkDocHistoryAsArchived" + MongoManager.getLastCompressedUpdate doc_id, (error, update) -> + db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> + return callback(error) if error? + callback(error) + markDocHistoryAsArchived: (doc_id, update, callback)-> - db.docHistory.update { v: update.v }, { $set : { inS3 : true } }, (error)-> + db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> return callback(error) if error? - db.docHistory.remove { doc_id : doc_id, inS3 : { $exists : false }, v: { $lt : update.v }, expiresAt: {$exists : false} }, (error)-> + db.docHistory.remove { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }, v: { $lt : update.v }, expiresAt: {$exists : false} }, (error)-> return callback(error) if error? callback(error) markDocHistoryAsUnarchived: (doc_id, callback)-> - db.docHistory.update { doc_id: doc_id }, { $unset : { inS3 : true } }, { multi: true }, (error)-> + db.docHistory.update { doc_id: ObjectId(doc_id.toString()) }, { $unset : { inS3 : true } }, { multi: true }, (error)-> callback(error) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index c104ceed7a..7e89be9ea3 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -36,7 +36,10 @@ module.exports = UpdatesManager = MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, temporary,(error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" - callback() + if lastCompressedUpdate.inS3? + MongoManager.remarkDocHistoryAsArchived doc_id, callback + else + callback() REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> From f910e63e903af890307acbb17be1ec9587c38782 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 24 Aug 2015 12:22:17 -0300 Subject: [PATCH 154/549] fix null case --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 7e89be9ea3..38c97144a0 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -36,7 +36,7 @@ module.exports = UpdatesManager = MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, temporary,(error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" - if lastCompressedUpdate.inS3? + if lastCompressedUpdate?.inS3? MongoManager.remarkDocHistoryAsArchived doc_id, callback else callback() From efff026a79ed97b820df64fbdd499a075f71eafc Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Tue, 25 Aug 2015 16:52:28 -0300 Subject: [PATCH 155/549] handle easier propagation --- services/track-changes/app/coffee/MongoManager.coffee | 10 +++------- .../track-changes/app/coffee/UpdatesManager.coffee | 9 +++++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index b366ce51b9..1077199c2c 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -48,6 +48,7 @@ module.exports = MongoManager = insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> + inS3 = update.inS3? update = { doc_id: ObjectId(doc_id.toString()) project_id: ObjectId(project_id.toString()) @@ -55,6 +56,8 @@ module.exports = MongoManager = meta: update.meta v: update.v } + if inS3 + update.inS3 = true if temporary seconds = 1000 @@ -135,13 +138,6 @@ module.exports = MongoManager = getArchivedDocChanges: (doc_id, callback)-> db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback - remarkDocHistoryAsArchived: (doc_id, callback)-> - logger.log doc_id: doc_id, "remarkDocHistoryAsArchived" - MongoManager.getLastCompressedUpdate doc_id, (error, update) -> - db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> - return callback(error) if error? - callback(error) - markDocHistoryAsArchived: (doc_id, update, callback)-> db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> return callback(error) if error? diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 38c97144a0..b500d5581c 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -7,6 +7,7 @@ UpdateTrimmer = require "./UpdateTrimmer" logger = require "logger-sharelatex" async = require "async" DocArchiveManager = require "./DocArchiveManager" +_ = require "underscore" module.exports = UpdatesManager = compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> @@ -33,13 +34,13 @@ module.exports = UpdatesManager = return compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates + if lastCompressedUpdate?.inS3? and not _.some(compressedUpdates, (update) -> update.inS3) + compressedUpdates[compressedUpdates.length-1].inS3 = lastCompressedUpdate.inS3 + MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, temporary,(error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" - if lastCompressedUpdate?.inS3? - MongoManager.remarkDocHistoryAsArchived doc_id, callback - else - callback() + callback() REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> From 1abcea1a66f4354e84ed16577d0dcbe05055e808 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Mon, 31 Aug 2015 18:13:18 -0300 Subject: [PATCH 156/549] add some unit test --- .../app/coffee/MongoManager.coffee | 2 +- .../DocArchive/DocArchiveManager.coffee | 44 ++++++++++ .../coffee/DocArchive/DocstoreHandler.coffee | 55 ++++++++++++ .../unit/coffee/DocArchive/MongoAWS.coffee | 78 +++++++++++++++++ .../HttpController/HttpControllerTests.coffee | 37 ++++++++ .../MongoManager/MongoManagerTests.coffee | 86 +++++++++++++++++++ 6 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee create mode 100644 services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee create mode 100644 services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 1077199c2c..3bcf50af2a 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -122,7 +122,7 @@ module.exports = MongoManager = db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding all packs that affect a project (use a sparse index so only packs are included) db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, { background: true, sparse: true } - # For finding updates that dont yet have a project_id and need it inserting + # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee new file mode 100644 index 0000000000..c76afab988 --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -0,0 +1,44 @@ +chai = require('chai') +sinon = require("sinon") +should = chai.should() +modulePath = "../../../../app/js/DocArchiveManager.js" +SandboxedModule = require('sandboxed-module') +ObjectId = require("mongojs").ObjectId + +describe "DocArchiveManager", -> + beforeEach -> + @DocArchiveManager = SandboxedModule.require modulePath, requires: + "./MongoManager" : @MongoManager = sinon.stub() + "./MongoAWS" : @MongoAWS = sinon.stub() + "./LockManager" : @LockManager = sinon.stub() + "./DocstoreHandler" : @DocstoreHandler = sinon.stub() + "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} + "settings-sharelatex": @settings = + filestore: + backend: 's3' + + @mongoDocs = [{ + _id: ObjectId() + }, { + _id: ObjectId() + }, { + _id: ObjectId() + }] + + @project_id = "project-id-123" + @doc_id = "doc-id-123" + @callback = sinon.stub() + + describe "archiveAllDocsChanges", -> + + it "should archive all project docs change", (done)-> + + @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) + @DocArchiveManager.archiveDocChangesWithLock = sinon.stub().callsArgWith(2, null) + + @DocArchiveManager.archiveAllDocsChanges @project_id, (err)=> + @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[0]._id).should.equal true + @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[1]._id).should.equal true + @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[2]._id).should.equal true + should.not.exist err + done() diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee new file mode 100644 index 0000000000..279f2a979b --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee @@ -0,0 +1,55 @@ +chai = require('chai') +chai.should() +sinon = require("sinon") +modulePath = "../../../../app/js/DocstoreHandler.js" +SandboxedModule = require('sandboxed-module') + +describe "DocstoreHandler", -> + beforeEach -> + @requestDefaults = sinon.stub().returns(@request = sinon.stub()) + @DocstoreHandler = SandboxedModule.require modulePath, requires: + "request" : defaults: @requestDefaults + "settings-sharelatex": @settings = + apis: + docstore: + url: "docstore.sharelatex.com" + "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} + + @requestDefaults.calledWith(jar: false).should.equal true + + @project_id = "project-id-123" + @doc_id = "doc-id-123" + @callback = sinon.stub() + + describe "getAllDocs", -> + describe "with a successful response code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, statusCode: 204, @docs = [{ _id: "mock-doc-id" }]) + @DocstoreHandler.getAllDocs @project_id, @callback + + it "should get all the project docs in the docstore api", -> + @request.get + .calledWith({ + url: "#{@settings.apis.docstore.url}/project/#{@project_id}/doc" + json: true + }) + .should.equal true + + it "should call the callback with the docs", -> + @callback.calledWith(null, @docs).should.equal true + + describe "with a failed response code", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, statusCode: 500, "") + @DocstoreHandler.getAllDocs @project_id, @callback + + it "should call the callback with an error", -> + @callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true + + it "should log the error", -> + @logger.error + .calledWith({ + err: new Error("docstore api responded with a non-success code: 500") + project_id: @project_id + }, "error getting all docs from docstore") + .should.equal true \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee new file mode 100644 index 0000000000..88c9f4b7da --- /dev/null +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -0,0 +1,78 @@ +chai = require('chai') +chai.should() +sinon = require("sinon") +modulePath = "../../../../app/js/MongoAWS.js" +SandboxedModule = require('sandboxed-module') +{ObjectId} = require("mongojs") + +describe "MongoAWS", -> + beforeEach -> + @MongoAWS = SandboxedModule.require modulePath, requires: + "settings-sharelatex": @settings = + filestore: + s3: + secret: "s3-secret" + key: "s3-key" + stores: + user_files: "s3-bucket" + "child_process": @child_process = {} + "mongo-uri": @mongouri = {} + "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} + "aws-sdk": @awssdk = {} + "fs": @fs = {} + "s3-streams": @s3streams = {} + "./mongojs" : { db: @db = {}, ObjectId: ObjectId } + "JSONStream": @JSONStream = {} + "readline-stream": @readline = sinon.stub() + + @bulkLimit = @MongoAWS.bulkLimit + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @callback = sinon.stub() + + describe "archiveDocHistory", -> + + beforeEach -> + @awssdk.config = { update: sinon.stub() } + @awssdk.S3 = sinon.stub() + @s3streams.WriteStream = sinon.stub() + @db.docHistory = {} + @db.docHistory.pipe = sinon.stub() + @db.docHistory.find = sinon.stub().returns @db.docHistory + @db.docHistory.pipe.returns + pipe:-> + on: (type, cb)-> + if type == "finish" + cb() + @JSONStream.stringify = sinon.stub() + + @MongoAWS.archiveDocHistory @project_id, @doc_id, @callback + + it "should call the callback", -> + @callback.calledWith(null).should.equal true + + describe "unArchiveDocHistory", -> + + beforeEach -> + @awssdk.config = { update: sinon.stub() } + @awssdk.S3 = sinon.stub() + @s3streams.ReadStream = sinon.stub() + + @s3streams.ReadStream.returns + #describe on 'open' behavior + on: (type, cb)-> + pipe:-> + #describe on 'data' behavior + on: (type, cb)-> + cb([]) + #describe on 'end' behavior + on: (type, cb)-> + cb() + #describe on 'error' behavior + on: sinon.stub() + + @MongoAWS.handleBulk = sinon.stub() + @MongoAWS.unArchiveDocHistory @project_id, @doc_id, @callback + + it "should call handleBulk", -> + @MongoAWS.handleBulk.calledWith([],@callback).should.equal true diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index 7c559c0937..e9533664e3 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -13,6 +13,7 @@ describe "HttpController", -> "./DiffManager": @DiffManager = {} "./RestoreManager": @RestoreManager = {} "./PackManager": @PackManager = {} + "./DocArchiveManager": @DocArchiveManager = {} @doc_id = "doc-id-123" @project_id = "project-id-123" @next = sinon.stub() @@ -130,3 +131,39 @@ describe "HttpController", -> it "should return a success code", -> @res.send.calledWith(204).should.equal true + + describe "archiveProject", -> + beforeEach -> + @req = + params: + project_id: @project_id + @res = + send: sinon.stub() + @DocArchiveManager.archiveAllDocsChanges = sinon.stub().callsArg(1) + @HttpController.archiveProject @req, @res, @next + + it "should process archive doc changes", -> + @DocArchiveManager.archiveAllDocsChanges + .calledWith(@project_id) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true + + describe "unArchiveProject", -> + beforeEach -> + @req = + params: + project_id: @project_id + @res = + send: sinon.stub() + @DocArchiveManager.unArchiveAllDocsChanges = sinon.stub().callsArg(1) + @HttpController.unArchiveProject @req, @res, @next + + it "should process unarchive doc changes", -> + @DocArchiveManager.unArchiveAllDocsChanges + .calledWith(@project_id) + .should.equal true + + it "should return a success code", -> + @res.send.calledWith(204).should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 24588de6ea..8db054b40f 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -398,3 +398,89 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true + + describe "getDocChangesCount", -> + beforeEach -> + @db.docHistory = + count: sinon.stub().callsArg(2) + @MongoManager.getDocChangesCount @doc_id, @callback + + it "should return if there is any doc changes", -> + @db.docHistory.count + .calledWith({ + doc_id: ObjectId(@doc_id) + inS3 : { $exists : false } + }, { + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "getArchivedDocChanges", -> + beforeEach -> + @db.docHistory = + count: sinon.stub().callsArg(2) + @MongoManager.getArchivedDocChanges @doc_id, @callback + + it "should return if there is any archived doc changes", -> + @db.docHistory.count + .calledWith({ + doc_id: ObjectId(@doc_id) + inS3 : true + }, { + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "markDocHistoryAsArchived", -> + beforeEach -> + @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} + @db.docHistory = + update: sinon.stub().callsArg(2) + remove: sinon.stub().callsArg(1) + @MongoManager.markDocHistoryAsArchived @doc_id, @update, @callback + + it "should update last doc change with inS3 flag", -> + @db.docHistory.update + .calledWith({ + _id: ObjectId(@update._id) + },{ + $set : { inS3 : true } + }) + .should.equal true + + it "should remove any other doc changes before last update", -> + @db.docHistory.remove + .calledWith({ + doc_id: ObjectId(@doc_id) + inS3 : { $exists : false } + v: { $lt : @update.v } + expiresAt: {$exists : false} + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "markDocHistoryAsUnarchived", -> + beforeEach -> + @db.docHistory = + update: sinon.stub().callsArg(3) + @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback + + it "should remove any doc changes inS3 flag", -> + @db.docHistory.update + .calledWith({ + doc_id: ObjectId(@doc_id) + },{ + $unset : { inS3 : true } + },{ + multi: true + }) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true \ No newline at end of file From 0c16fbed8808c5e74f6207d298aa6c697472b0cb Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 2 Sep 2015 15:39:19 -0300 Subject: [PATCH 157/549] add more unit tests --- .../DocArchive/DocArchiveManager.coffee | 64 ++++++++++++++++++- .../unit/coffee/DocArchive/MongoAWS.coffee | 33 ++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee index c76afab988..ac266132c3 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -30,9 +30,7 @@ describe "DocArchiveManager", -> @callback = sinon.stub() describe "archiveAllDocsChanges", -> - it "should archive all project docs change", (done)-> - @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) @DocArchiveManager.archiveDocChangesWithLock = sinon.stub().callsArgWith(2, null) @@ -42,3 +40,65 @@ describe "DocArchiveManager", -> @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[2]._id).should.equal true should.not.exist err done() + + describe "archiveDocChangesWithLock", -> + beforeEach -> + @DocArchiveManager.archiveDocChanges = sinon.stub().callsArg(2) + @LockManager.runWithLock = sinon.stub().callsArg(2) + @DocArchiveManager.archiveDocChangesWithLock @project_id, @doc_id, @callback + + it "should run archiveDocChangesWithLock with the lock", -> + @LockManager.runWithLock + .calledWith( + "HistoryArchiveLock:#{@doc_id}" + ) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "archiveDocChanges", -> + beforeEach -> + @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} + @MongoManager.getDocChangesCount = sinon.stub().callsArg(1) + @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) + @MongoAWS.archiveDocHistory = sinon.stub().callsArg(2) + @MongoManager.markDocHistoryAsArchived = sinon.stub().callsArg(2) + @DocArchiveManager.archiveDocChanges @project_id, @doc_id, @callback + + it "should run markDocHistoryAsArchived with doc_id and update", -> + @MongoManager.markDocHistoryAsArchived + .calledWith( + @doc_id, @update + ) + .should.equal true + it "should call the callback", -> + @callback.called.should.equal true + + describe "unArchiveAllDocsChanges", -> + it "should unarchive all project docs change", (done)-> + @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) + @DocArchiveManager.unArchiveDocChanges = sinon.stub().callsArgWith(2, null) + + @DocArchiveManager.unArchiveAllDocsChanges @project_id, (err)=> + @DocArchiveManager.unArchiveDocChanges.calledWith(@project_id, @mongoDocs[0]._id).should.equal true + @DocArchiveManager.unArchiveDocChanges.calledWith(@project_id, @mongoDocs[1]._id).should.equal true + @DocArchiveManager.unArchiveDocChanges.calledWith(@project_id, @mongoDocs[2]._id).should.equal true + should.not.exist err + done() + + describe "unArchiveDocChanges", -> + beforeEach -> + @MongoManager.getArchivedDocChanges = sinon.stub().callsArg(1) + @MongoAWS.unArchiveDocHistory = sinon.stub().callsArg(2) + @MongoManager.markDocHistoryAsUnarchived = sinon.stub().callsArg(1) + @DocArchiveManager.unArchiveDocChanges @project_id, @doc_id, @callback + + it "should run markDocHistoryAsUnarchived with doc_id", -> + @MongoManager.markDocHistoryAsUnarchived + .calledWith( + @doc_id + ) + .should.equal true + it "should call the callback", -> + @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 88c9f4b7da..180562dea7 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -76,3 +76,36 @@ describe "MongoAWS", -> it "should call handleBulk", -> @MongoAWS.handleBulk.calledWith([],@callback).should.equal true + + describe "handleBulk", -> + beforeEach -> + @ops = [{ + _id: ObjectId() + doc_id: ObjectId() + project_id: ObjectId() + }, { + _id: ObjectId() + doc_id: ObjectId() + project_id: ObjectId() + }, { + _id: ObjectId() + doc_id: ObjectId() + project_id: ObjectId() + }] + @bulk = + find: sinon.stub().returns + upsert: sinon.stub().returns + updateOne: sinon.stub() + execute: sinon.stub().callsArgWith(0, null, {}) + @db.docHistory = {} + @db.docHistory.initializeUnorderedBulkOp = sinon.stub().returns @bulk + @MongoAWS.handleBulk @ops, @callback + + it "should call updateOne for each operation", -> + @bulk.find.calledWith({_id:@ops[0]._id}).should.equal true + @bulk.find.calledWith({_id:@ops[1]._id}).should.equal true + @bulk.find.calledWith({_id:@ops[2]._id}).should.equal true + + it "should call the callback", -> + @callback.calledWith(null).should.equal true + From d2b1243701af2aeaa89f89fbf00807c25ff5b405 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 2 Sep 2015 15:45:29 -0300 Subject: [PATCH 158/549] split MongoAWS files --- .../track-changes/app/coffee/MongoAWS.coffee | 118 ----------------- .../app/coffee/MongoAWSexternal.coffee | 123 ++++++++++++++++++ 2 files changed, 123 insertions(+), 118 deletions(-) create mode 100644 services/track-changes/app/coffee/MongoAWSexternal.coffee diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 9b4271c167..aedd474c48 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -1,9 +1,6 @@ settings = require "settings-sharelatex" -child_process = require "child_process" -mongoUri = require "mongo-uri"; logger = require "logger-sharelatex" AWS = require 'aws-sdk' -fs = require 'fs' S3S = require 's3-streams' {db, ObjectId} = require "./mongojs" JSONStream = require "JSONStream" @@ -82,118 +79,3 @@ module.exports = MongoAWS = else logger.log count:ops.length, result:result, "bulked ReadlineStream" cb(err) - - - archiveDocHistoryExternal: (project_id, doc_id, callback = (error) ->) -> - MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> - MongoAWS.s3upStream project_id, doc_id, filepath, callback - #delete temp file? - - - unArchiveDocHistoryExternal: (project_id, doc_id, callback = (error) ->) -> - MongoAWS.s3downStream project_id, doc_id, (error, filepath) -> - if error == null - MongoAWS.mongoImportDocHistory filepath, callback - #delete temp file? - else - callback - - mongoExportDocHistory: (doc_id, callback = (error, filepath) ->) -> - uriData = mongoUri.parse(settings.mongo.url); - filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonUp' - - args = [] - args.push '-h' - args.push uriData.hosts[0] - args.push '-d' - args.push uriData.database - args.push '-c' - args.push 'docHistory' - args.push '-q' - args.push "{doc_id: ObjectId('#{doc_id}') , expiresAt: {$exists : false} }" - args.push '-o' - args.push filepath - - proc = child_process.spawn "mongoexport", args - - proc.on "error", callback - - stderr = "" - proc.stderr.on "data", (chunk) -> stderr += chunk.toString() - - proc.on "close", (code) -> - if code == 0 - return callback(null,filepath) - else - return callback(new Error("mongodump failed: #{stderr}"),null) - - mongoImportDocHistory: (filepath, callback = (error) ->) -> - - uriData = mongoUri.parse(settings.mongo.url); - - args = [] - args.push '-h' - args.push uriData.hosts[0] - args.push '-d' - args.push uriData.database - args.push '-c' - args.push 'docHistory' - args.push '--file' - args.push filepath - - proc = child_process.spawn "mongoimport", args - - proc.on "error", callback - - stderr = "" - proc.stderr.on "data", (chunk) -> stderr += chunk.toString() - - proc.on "close", (code) -> - if code == 0 - return callback(null,filepath) - else - return callback(new Error("mongodump failed: #{stderr}"),null) - - s3upStream: (project_id, doc_id, filepath, callback = (error) ->) -> - - AWS.config.update { - accessKeyId: settings.filestore.s3.key - secretAccessKey: settings.filestore.s3.secret - } - - upload = S3S.WriteStream new AWS.S3(), { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id - } - - fs.createReadStream(filepath) - .on 'open', (obj) -> - return 1 - .pipe(upload) - .on 'finish', () -> - return callback(null) - .on 'error', (err) -> - return callback(err) - - s3downStream: (project_id, doc_id, callback = (error, filepath) ->) -> - - filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonDown' - - AWS.config.update { - accessKeyId: settings.filestore.s3.key - secretAccessKey: settings.filestore.s3.secret - } - - download = S3S.ReadStream new AWS.S3(), { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id - } - - download - .on 'open', (obj) -> - return 1 - .pipe(fs.createWriteStream(filepath)) - .on 'finish', () -> - return callback(null, filepath) - .on 'error', (err) -> - return callback(err, null) diff --git a/services/track-changes/app/coffee/MongoAWSexternal.coffee b/services/track-changes/app/coffee/MongoAWSexternal.coffee new file mode 100644 index 0000000000..f422a583b5 --- /dev/null +++ b/services/track-changes/app/coffee/MongoAWSexternal.coffee @@ -0,0 +1,123 @@ +settings = require "settings-sharelatex" +child_process = require "child_process" +mongoUri = require "mongo-uri"; +logger = require "logger-sharelatex" +AWS = require 'aws-sdk' +fs = require 'fs' +S3S = require 's3-streams' + +module.exports = MongoAWSexternal = + + archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> + MongoAWS.s3upStream project_id, doc_id, filepath, callback + #delete temp file? + + + unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + MongoAWS.s3downStream project_id, doc_id, (error, filepath) -> + if error == null + MongoAWS.mongoImportDocHistory filepath, callback + #delete temp file? + else + callback + + mongoExportDocHistory: (doc_id, callback = (error, filepath) ->) -> + uriData = mongoUri.parse(settings.mongo.url); + filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonUp' + + args = [] + args.push '-h' + args.push uriData.hosts[0] + args.push '-d' + args.push uriData.database + args.push '-c' + args.push 'docHistory' + args.push '-q' + args.push "{doc_id: ObjectId('#{doc_id}') , expiresAt: {$exists : false} }" + args.push '-o' + args.push filepath + + proc = child_process.spawn "mongoexport", args + + proc.on "error", callback + + stderr = "" + proc.stderr.on "data", (chunk) -> stderr += chunk.toString() + + proc.on "close", (code) -> + if code == 0 + return callback(null,filepath) + else + return callback(new Error("mongodump failed: #{stderr}"),null) + + mongoImportDocHistory: (filepath, callback = (error) ->) -> + + uriData = mongoUri.parse(settings.mongo.url); + + args = [] + args.push '-h' + args.push uriData.hosts[0] + args.push '-d' + args.push uriData.database + args.push '-c' + args.push 'docHistory' + args.push '--file' + args.push filepath + + proc = child_process.spawn "mongoimport", args + + proc.on "error", callback + + stderr = "" + proc.stderr.on "data", (chunk) -> stderr += chunk.toString() + + proc.on "close", (code) -> + if code == 0 + return callback(null,filepath) + else + return callback(new Error("mongodump failed: #{stderr}"),null) + + s3upStream: (project_id, doc_id, filepath, callback = (error) ->) -> + + AWS.config.update { + accessKeyId: settings.filestore.s3.key + secretAccessKey: settings.filestore.s3.secret + } + + upload = S3S.WriteStream new AWS.S3(), { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id + } + + fs.createReadStream(filepath) + .on 'open', (obj) -> + return 1 + .pipe(upload) + .on 'finish', () -> + return callback(null) + .on 'error', (err) -> + return callback(err) + + s3downStream: (project_id, doc_id, callback = (error, filepath) ->) -> + + filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonDown' + + AWS.config.update { + accessKeyId: settings.filestore.s3.key + secretAccessKey: settings.filestore.s3.secret + } + + download = S3S.ReadStream new AWS.S3(), { + "Bucket": settings.filestore.stores.user_files, + "Key": project_id+"/changes-"+doc_id + } + + download + .on 'open', (obj) -> + return 1 + .pipe(fs.createWriteStream(filepath)) + .on 'finish', () -> + return callback(null, filepath) + .on 'error', (err) -> + return callback(err, null) From 7de103af689e91e8f4baa9e547bd6d3059c7a695 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 2 Sep 2015 17:00:32 -0300 Subject: [PATCH 159/549] fix unit scope error --- .../test/unit/coffee/DocArchive/MongoAWS.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 180562dea7..4279ec0ab6 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -79,7 +79,7 @@ describe "MongoAWS", -> describe "handleBulk", -> beforeEach -> - @ops = [{ + @bulkOps = [{ _id: ObjectId() doc_id: ObjectId() project_id: ObjectId() @@ -99,12 +99,12 @@ describe "MongoAWS", -> execute: sinon.stub().callsArgWith(0, null, {}) @db.docHistory = {} @db.docHistory.initializeUnorderedBulkOp = sinon.stub().returns @bulk - @MongoAWS.handleBulk @ops, @callback + @MongoAWS.handleBulk @bulkOps, @callback it "should call updateOne for each operation", -> - @bulk.find.calledWith({_id:@ops[0]._id}).should.equal true - @bulk.find.calledWith({_id:@ops[1]._id}).should.equal true - @bulk.find.calledWith({_id:@ops[2]._id}).should.equal true + @bulk.find.calledWith({_id:@bulkOps[0]._id}).should.equal true + @bulk.find.calledWith({_id:@bulkOps[1]._id}).should.equal true + @bulk.find.calledWith({_id:@bulkOps[2]._id}).should.equal true it "should call the callback", -> @callback.calledWith(null).should.equal true From da9e7dc7e1db69ac9023f09081edf98f45ee1d02 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 2 Sep 2015 18:47:34 -0300 Subject: [PATCH 160/549] init archive acceptance tests --- .../app/coffee/DocArchiveManager.coffee | 4 +- .../coffee/ArchivingUpdatesTests.coffee | 88 +++++++++++++++++++ .../coffee/helpers/MockDocStoreApi.coffee | 24 +++++ .../coffee/helpers/TrackChangesClient.coffee | 16 +++- 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee create mode 100644 services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index e0d0d1bf1a..ab54a83039 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -36,7 +36,7 @@ module.exports = DocArchiveManager = else MongoManager.getLastCompressedUpdate doc_id, (error, update) -> MongoAWS.archiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "mongoexport" + logger.log doc_id:doc_id, error: error, "export to S3" MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> return callback(error) if error? callback() @@ -59,7 +59,7 @@ module.exports = DocArchiveManager = return callback() else MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "mongoimport" + logger.log doc_id:doc_id, error: error, "import from S3" MongoManager.markDocHistoryAsUnarchived doc_id, (error) -> return callback(error) if error? callback() diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee new file mode 100644 index 0000000000..4859d28761 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -0,0 +1,88 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +expect = chai.expect +mongojs = require "../../../app/js/mongojs" +db = mongojs.db +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" +request = require "request" +rclient = require("redis").createClient() # Only works locally for now + +TrackChangesClient = require "./helpers/TrackChangesClient" +MockDocStoreApi = require "./helpers/MockDocStoreApi" +MockWebApi = require "./helpers/MockWebApi" + +describe "Archiving updates", -> + before (done) -> + @now = Date.now() + @to = @now + @user_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + + @minutes = 60 * 1000 + @hours = 60 * @minutes + + MockWebApi.projects[@project_id] = + features: + versioning: true + + MockWebApi.users[@user_id] = @user = + email: "user@sharelatex.com" + first_name: "Leo" + last_name: "Lion" + id: @user_id + sinon.spy MockWebApi, "getUser" + + MockDocStoreApi.docs[@doc_id] = @doc = + _id: @doc_id + project_id: @project_id + sinon.spy MockDocStoreApi, "getAllDoc" + + @updates = [] + for i in [0..9] + @updates.push { + op: [{ i: "a", p: 0 }] + meta: { ts: @now - (9 - i) * @hours - 2 * @minutes, user_id: @user_id } + v: 2 * i + 1 + } + @updates.push { + op: [{ i: "b", p: 0 }] + meta: { ts: @now - (9 - i) * @hours, user_id: @user_id } + v: 2 * i + 2 + } + + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => + throw error if error? + TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> + throw error if error? + done() + + after: () -> + MockWebApi.getUser.restore() + + describe "archiving a doc's updates", -> + before (done) -> + + TrackChangesClient.archiveProject @project_id, (error) -> + throw error if error? + done() + + it "should remain one doc", (done) -> + db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> + throw error if error? + count.should.equal 1 + done() + + it "should remained doc marked as inS3", (done) -> + db.docHistory.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> + throw error if error? + doc.inS3.should.equal true + done() + + it "should remained doc have last version", (done) -> + db.docHistory.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> + throw error if error? + doc.v.should.equal 20 + done() \ No newline at end of file diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee new file mode 100644 index 0000000000..29864479a4 --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee @@ -0,0 +1,24 @@ +express = require("express") +app = express() + +module.exports = MockDocUpdaterApi = + docs: {} + + getAllDoc: (project_id, callback = (error) ->) -> + callback null, @docs + + run: () -> + app.get "/project/:project_id/doc", (req, res, next) => + @getAllDoc req.params.project_id, (error, docs) -> + if error? + res.send 500 + if !docs? + res.send 404 + else + res.send JSON.stringify docs + + app.listen 3016, (error) -> + throw error if error? + +MockDocUpdaterApi.run() + diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 661b0eafb8..552d455d31 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -71,4 +71,18 @@ module.exports = TrackChangesClient = "X-User-Id": user_id }, (error, response, body) => response.statusCode.should.equal 204 - callback null \ No newline at end of file + callback null + + archiveProject: (project_id, callback = (error) ->) -> + request.get { + url: "http://localhost:3015/project/#{project_id}/archive" + }, (error, response, body) => + response.statusCode.should.equal 204 + callback(error) + + unarchiveProject: (project_id, callback = (error) ->) -> + request.get { + url: "http://localhost:3015/project/#{project_id}/unarchive" + }, (error, response, body) => + response.statusCode.should.equal 204 + callback(error) \ No newline at end of file From c5a8a249c687fa74404556d3f7440de5c5ea7e65 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 3 Sep 2015 08:36:32 -0300 Subject: [PATCH 161/549] add unarchive acceptance tests --- .../track-changes/app/coffee/MongoAWS.coffee | 17 ++++++---- .../coffee/ArchivingUpdatesTests.coffee | 32 ++++++++++++++++--- .../coffee/helpers/TrackChangesClient.coffee | 22 ++++++++++++- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index aedd474c48..3fba799d22 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -72,10 +72,13 @@ module.exports = MongoAWS = op.doc_id = ObjectId(op.doc_id) op.project_id = ObjectId(op.project_id) bulk.find({_id:op._id}).upsert().updateOne(op) - - bulk.execute (err, result) -> - if err? - logger.error err:err, "error bulking ReadlineStream" - else - logger.log count:ops.length, result:result, "bulked ReadlineStream" - cb(err) + + if ops.length > 0 + bulk.execute (err, result) -> + if err? + logger.error err:err, "error bulking ReadlineStream" + else + logger.log count:ops.length, result:result, "bulked ReadlineStream" + cb(err) + else + cb() diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 4859d28761..973d4bde06 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -59,17 +59,18 @@ describe "Archiving updates", -> throw error if error? done() - after: () -> + after (done) -> MockWebApi.getUser.restore() + db.docHistory.remove {project_id: ObjectId(@project_id)} + TrackChangesClient.removeS3Doc @project_id, @doc_id, done describe "archiving a doc's updates", -> before (done) -> - TrackChangesClient.archiveProject @project_id, (error) -> throw error if error? done() - it "should remain one doc", (done) -> + it "should remain one doc change", (done) -> db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> throw error if error? count.should.equal 1 @@ -85,4 +86,27 @@ describe "Archiving updates", -> db.docHistory.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> throw error if error? doc.v.should.equal 20 - done() \ No newline at end of file + done() + + it "should store twenty doc changes in S3", (done) -> + TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => + doc.length.should.equal 20 + done() + + describe "unarchiving a doc's updates", -> + before (done) -> + TrackChangesClient.unarchiveProject @project_id, (error) -> + throw error if error? + done() + + it "should restore doc changes", (done) -> + db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> + throw error if error? + count.should.equal 20 + done() + + it "should remove doc marked as inS3", (done) -> + db.docHistory.count { doc_id: ObjectId(@doc_id), inS3 : true }, (error, count) -> + throw error if error? + count.should.equal 0 + done() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 552d455d31..1e409908d9 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -1,6 +1,7 @@ request = require "request" rclient = require("redis").createClient() # Only works locally for now {db, ObjectId} = require "../../../../app/js/mongojs" +Settings = require "settings-sharelatex" module.exports = TrackChangesClient = flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> @@ -85,4 +86,23 @@ module.exports = TrackChangesClient = url: "http://localhost:3015/project/#{project_id}/unarchive" }, (error, response, body) => response.statusCode.should.equal 204 - callback(error) \ No newline at end of file + callback(error) + + buildS3Options: (content, key)-> + return { + aws: + key: Settings.filestore.s3.key + secret: Settings.filestore.s3.secret + bucket: Settings.filestore.stores.user_files + timeout: 30 * 1000 + json: content + uri:"https://#{Settings.filestore.stores.user_files}.s3.amazonaws.com/#{key}" + } + + getS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> + options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id) + request.get options, callback + + removeS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> + options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id) + request.del options, callback \ No newline at end of file From 0b3ebcff065f2428909f241444e94745511465b7 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 8 Sep 2015 16:23:15 +0100 Subject: [PATCH 162/549] remove if statments checking if s3 is a backend if its not enable then it can crash. In prod it should always be there or not used at all --- services/track-changes/app.coffee | 5 ++--- services/track-changes/app/coffee/DocArchiveManager.coffee | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index f7af275d62..f5f12078ac 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -27,9 +27,8 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post "/doc/:doc_id/pack", HttpController.packDoc -if Settings.filestore?.backend == "s3" - app.get '/project/:project_id/archive', HttpController.archiveProject - app.get '/project/:project_id/unarchive', HttpController.unArchiveProject +app.get '/project/:project_id/archive', HttpController.archiveProject +app.get '/project/:project_id/unarchive', HttpController.unArchiveProject packWorker = null # use a single packing worker diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index ab54a83039..cf69f69b8a 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -10,8 +10,6 @@ settings = require("settings-sharelatex") module.exports = DocArchiveManager = archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> - if settings.filestore?.backend != "s3" - return callback(null) DocstoreHandler.getAllDocs project_id, (error, docs) -> if error? return callback(error) @@ -42,8 +40,6 @@ module.exports = DocArchiveManager = callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> - if settings.filestore?.backend != "s3" - return callback(null) DocstoreHandler.getAllDocs project_id, (error, docs) -> if error? return callback(error) From 17b0d99a65c6ac00ac14d308cbbf0500c45931fc Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 8 Sep 2015 16:26:01 +0100 Subject: [PATCH 163/549] rework the archiveDocChangesWithLock function make it a bit more readable for me, struggle to trust indentation based calls in coffeescript --- .../track-changes/app/coffee/DocArchiveManager.coffee | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index cf69f69b8a..b316863d0b 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -20,12 +20,9 @@ module.exports = DocArchiveManager = async.series jobs, callback archiveDocChangesWithLock: (project_id, doc_id, callback = (error) ->) -> - LockManager.runWithLock( - "HistoryArchiveLock:#{doc_id}", - (releaseLock) -> - DocArchiveManager.archiveDocChanges project_id, doc_id, releaseLock - callback - ) + job = (releaseLock) -> + DocArchiveManager.archiveDocChanges project_id, doc_id, releaseLock + LockManager.runWithLock("HistoryArchiveLock:#{doc_id}", job, callback) archiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getDocChangesCount doc_id, (error, count) -> From 18d817ee0a6da3f32d1aef3670dcbcaca01e474e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 8 Sep 2015 16:33:45 +0100 Subject: [PATCH 164/549] added some missing error handling --- .../track-changes/app/coffee/DocArchiveManager.coffee | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index b316863d0b..0e3eeba13e 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -26,12 +26,15 @@ module.exports = DocArchiveManager = archiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getDocChangesCount doc_id, (error, count) -> + return callback(error) if error? if count == 0 return callback() else MongoManager.getLastCompressedUpdate doc_id, (error, update) -> + return callback(error) if error? MongoAWS.archiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "export to S3" + return callback(error) if error? + logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> return callback(error) if error? callback() @@ -48,11 +51,13 @@ module.exports = DocArchiveManager = unArchiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getArchivedDocChanges doc_id, (error, count) -> + return callback(error) if error? if count == 0 return callback() else MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> - logger.log doc_id:doc_id, error: error, "import from S3" + return callback(error) if error? + logger.log doc_id:doc_id, project_id:project_id, "imported document from S3" MongoManager.markDocHistoryAsUnarchived doc_id, (error) -> return callback(error) if error? callback() From 522786d45e1c1783fff52218894fa8bb990c634a Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Wed, 9 Sep 2015 15:48:22 +0100 Subject: [PATCH 165/549] Produce a null value, rather than crashing when the user info service returns 404. --- services/track-changes/app/coffee/WebApiManager.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 555dc0179c..9264862fa3 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -13,6 +13,8 @@ module.exports = WebApiManager = }, (error, res, body)-> if error? return callback(error) + if res.statusCode == 404 + return callback null, null if res.statusCode >= 200 and res.statusCode < 300 return callback null, body else @@ -27,6 +29,9 @@ module.exports = WebApiManager = logger.error err: error, user_id: user_id, url: url, "error accessing web" return callback error + if body == null + logger.error user_id: user_id, url: url, "no user found" + return callback null, null try user = JSON.parse(body) catch error From 575bdc62ec9566868f69b5d919832b5397b9a401 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 10 Sep 2015 14:32:18 +0100 Subject: [PATCH 166/549] Add a test for when the user can't be found. --- .../coffee/WebApiManager/WebApiManagerTests.coffee | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee index aec51ab2ca..177a7b26f0 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee @@ -72,6 +72,16 @@ describe "WebApiManager", -> .calledWith(new Error("web returned failure status code: 500")) .should.equal true + describe "when the user cannot be found", -> + beforeEach -> + @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 404}, "nothing") + @WebApiManager.getUserInfo @user_id, @callback + + it "should return a null value", -> + @callback + .calledWith(null, null) + .should.equal true + describe "getProjectDetails", -> describe "successfully", -> From 810bddb2cbc6cdf1cdcbbccf80defe1a14f98c2c Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 10 Sep 2015 14:32:35 +0100 Subject: [PATCH 167/549] Log a message when the web api produces a 404 response. --- services/track-changes/app/coffee/WebApiManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 9264862fa3..d7441682e3 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -14,6 +14,7 @@ module.exports = WebApiManager = if error? return callback(error) if res.statusCode == 404 + logger.log url: url, "got 404 from web api" return callback null, null if res.statusCode >= 200 and res.statusCode < 300 return callback null, body From 8387383cb4a6a0773971f9f9292a87135550a35f Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 10 Sep 2015 14:32:47 +0100 Subject: [PATCH 168/549] In _summarizeUpdates, allow null users through. A null value represents a deleted or otherwise missing user record. --- .../app/coffee/UpdatesManager.coffee | 21 +++---- .../UpdatesManager/UpdatesManagerTests.coffee | 61 +++++++++++++++++++ 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 92357fc2dc..91a4877b35 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -188,14 +188,13 @@ module.exports = UpdatesManager = for update in updates earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES - if update.meta.user? - userExists = false - for user in earliestUpdate.meta.users - if user.id == update.meta.user.id - userExists = true - break - if !userExists - earliestUpdate.meta.users.push update.meta.user + userExists = false + for user in earliestUpdate.meta.users + if (!user and !update.meta.user) or (user.id == update.meta.user?.id) + userExists = true + break + if !userExists + earliestUpdate.meta.users.push update.meta.user doc_id = update.doc_id.toString() doc = earliestUpdate.docs[doc_id] @@ -220,11 +219,7 @@ module.exports = UpdatesManager = newUpdate.docs[update.doc_id.toString()] = fromV: update.v toV: update.v - - if update.meta.user? - newUpdate.meta.users.push update.meta.user - + newUpdate.meta.users.push update.meta.user summarizedUpdates.push newUpdate return summarizedUpdates - diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index fdc19360b7..545fa18ca0 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -627,3 +627,64 @@ describe "UpdatesManager", -> start_ts: @now end_ts: @now + 50 }] + + it "should include null user values", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user: @user_1 + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user: null + start_ts: @now + end_ts: @now + 10 + v: 4 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + users: [@user_1, null] + start_ts: @now + end_ts: @now + 30 + }] + + it "should roll several null user values into one", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user: @user_1 + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user: null + start_ts: @now + end_ts: @now + 10 + v: 4 + }, { + doc_id: "doc-id-1" + meta: + user: null + start_ts: @now + 2 + end_ts: @now + 4 + v: 4 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + users: [@user_1, null] + start_ts: @now + end_ts: @now + 30 + }] From 97326308fabf65f337d09c82f063e76a784d182e Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 10 Sep 2015 15:40:43 +0100 Subject: [PATCH 169/549] Update the Acceptance tests to include a case where a user doesn't exist. --- .../test/acceptance/coffee/GettingUpdatesTests.coffee | 6 +++--- .../test/acceptance/coffee/helpers/MockWebApi.coffee | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 6452649fc4..7c94bcdc34 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -15,6 +15,7 @@ describe "Getting updates", -> @now = Date.now() @to = @now @user_id = ObjectId().toString() + @deleted_user_id = 'deleted_user' @doc_id = ObjectId().toString() @project_id = ObjectId().toString() @@ -44,6 +45,7 @@ describe "Getting updates", -> meta: { ts: @now - (9 - i) * @hours, user_id: @user_id } v: 2 * i + 2 } + @updates[0].meta.user_id = @deleted_user_id TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? @@ -91,7 +93,6 @@ describe "Getting updates", -> users: [@user] }] - describe "getting updates beyond the end of the database", -> before (done) -> TrackChangesClient.getUpdates @project_id, { before: @to - 8 * @hours + 1, min_count: 30 }, (error, body) => @@ -115,6 +116,5 @@ describe "Getting updates", -> meta: start_ts: @to - 9 * @hours - 2 * @minutes end_ts: @to - 9 * @hours - users: [@user] + users: [@user, null] }] - diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee index f7cfc9e2e9..002d944dfd 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee @@ -7,7 +7,7 @@ module.exports = MockWebApi = projects: {} getUser: (user_id, callback = (error) ->) -> - callback null, @users[user_id] + callback null, @users[user_id] or null getProject: (project_id, callback = (error, project) ->) -> callback null, @projects[project_id] From 0ad374556dfabebe9bdb87e444cf5adf1e79de49 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 10 Sep 2015 16:43:40 +0100 Subject: [PATCH 170/549] Add a comment for clarity. --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 91a4877b35..49c2b6e1af 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -188,6 +188,8 @@ module.exports = UpdatesManager = for update in updates earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + # check if the user in this update is already present in the earliest update, + # if not, add them to the users list of the earliest update userExists = false for user in earliestUpdate.meta.users if (!user and !update.meta.user) or (user.id == update.meta.user?.id) From eab8b4b6c89f85a494be6f9017e0dbaec9fd502a Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 11 Sep 2015 14:07:06 +0100 Subject: [PATCH 171/549] Null safe access of `id` property, needed as user can be null. --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 49c2b6e1af..9bea5ba177 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -192,7 +192,7 @@ module.exports = UpdatesManager = # if not, add them to the users list of the earliest update userExists = false for user in earliestUpdate.meta.users - if (!user and !update.meta.user) or (user.id == update.meta.user?.id) + if (!user and !update.meta.user) or (user?.id == update.meta.user?.id) userExists = true break if !userExists From 39f528bcbcbc82cce5f137462a4d52668ce3330e Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 11 Sep 2015 14:12:01 +0100 Subject: [PATCH 172/549] Add a test to check that users are summarised properly even when a null user occurs earlier in the update list. --- .../UpdatesManager/UpdatesManagerTests.coffee | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 545fa18ca0..fc8853f2ab 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -655,6 +655,33 @@ describe "UpdatesManager", -> end_ts: @now + 30 }] + it "should include null user values, when the null is earlier in the updates list", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user: null + start_ts: @now + end_ts: @now + 10 + v: 4 + }, { + doc_id: "doc-id-1" + meta: + user: @user_1 + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + users: [null, @user_1] + start_ts: @now + end_ts: @now + 30 + }] + it "should roll several null user values into one", -> result = @UpdatesManager._summarizeUpdates [{ doc_id: "doc-id-1" From 092f98d3adc4c8ea54fa0db612d95773d6278c9f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Sat, 12 Sep 2015 11:07:54 +0100 Subject: [PATCH 173/549] suppress error in normal shutdown case --- services/track-changes/app/coffee/PackWorker.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index c9076c268d..5c23613e57 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -47,7 +47,7 @@ processUpdates = (pending) -> callback(err, result) , DOCUMENT_PACK_DELAY , (err, results) -> - if err? + if err? and err.message != "shutdown" logger.error {err}, 'error in pack worker processUpdates' finish() From 70200a9cf1987593373f1ea754afed3a6921a322 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 15:31:43 +0100 Subject: [PATCH 174/549] only log document ids, not document content avoid filling the log with large documents --- services/track-changes/app/coffee/DocstoreHandler.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/DocstoreHandler.coffee b/services/track-changes/app/coffee/DocstoreHandler.coffee index 7ca270fb0f..9e3b10d258 100644 --- a/services/track-changes/app/coffee/DocstoreHandler.coffee +++ b/services/track-changes/app/coffee/DocstoreHandler.coffee @@ -12,6 +12,7 @@ module.exports = DocstoreHandler = json: true }, (error, res, docs) -> return callback(error) if error? + logger.log {error, res, docs: if docs?.length then docs.map (d) -> d._id else []}, "docstore response" if 200 <= res.statusCode < 300 callback(null, docs) else From dfa0036507dcab5dd6dadd74a9f4c491b5edebd4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 15:32:36 +0100 Subject: [PATCH 175/549] pause stream while writing to mongo --- services/track-changes/app/coffee/MongoAWS.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 3fba799d22..583244bbae 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -57,7 +57,9 @@ module.exports = MongoAWS = if line.length > 2 ops.push(JSON.parse(line)) if ops.length == MongoAWS.bulkLimit + download.pause() MongoAWS.handleBulk ops.slice(0), () -> + download.resume() ops.splice(0,ops.length) .on 'end', () -> MongoAWS.handleBulk ops, callback From 82d0f4fce823128c652fe7a281b067dba74eada7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 15:33:59 +0100 Subject: [PATCH 176/549] make unarchive more responsive by downloading documents in parallel unarchive is triggered interactively so we should try to make it reasonably fast --- services/track-changes/app/coffee/DocArchiveManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 0e3eeba13e..210f6d9f9c 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -47,7 +47,7 @@ module.exports = DocArchiveManager = return callback new Error("No docs for project #{project_id}") jobs = _.map docs, (doc) -> (cb)-> DocArchiveManager.unArchiveDocChanges project_id, doc._id, cb - async.series jobs, callback + async.parallelLimit jobs, 4, callback unArchiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getArchivedDocChanges doc_id, (error, count) -> From 1c1b1d95956f899dabd853085e3d6647bb5c785d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 15:34:30 +0100 Subject: [PATCH 177/549] log the case where there are no entries in the document history --- services/track-changes/app/coffee/DocArchiveManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 210f6d9f9c..4da954b6b3 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -28,6 +28,7 @@ module.exports = DocArchiveManager = MongoManager.getDocChangesCount doc_id, (error, count) -> return callback(error) if error? if count == 0 + logger.log {project_id, doc_id}, "document history is empty, not archiving" return callback() else MongoManager.getLastCompressedUpdate doc_id, (error, update) -> From d9085a5e5e5ab0a5737da77ca42a1f07e03246f0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 16:00:25 +0100 Subject: [PATCH 178/549] add error handler to each stage of upload pipeline --- .../track-changes/app/coffee/MongoAWS.coffee | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 583244bbae..7fe90378c6 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -10,7 +10,12 @@ module.exports = MongoAWS = bulkLimit: 10 - archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + archiveDocHistory: (project_id, doc_id, _callback = (error) ->) -> + + callback = (args...) -> + _callback(args...) + _callback = () -> + query = { doc_id: ObjectId(doc_id) expiresAt: {$exists : false} @@ -27,10 +32,14 @@ module.exports = MongoAWS = } db.docHistory.find(query) + .on 'error', (err) -> + callback(err) .pipe JSONStream.stringify() - .pipe upload - .on 'finish', () -> - return callback(null) + .pipe upload + .on 'error', (err) -> + callback(err) + .on 'finish', () -> + return callback(null) unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> From 9d39012b49b0518ab6a0a17ac12f7707d7b8c7ae Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 16:00:37 +0100 Subject: [PATCH 179/549] add error handler to each stage of download pipeline --- services/track-changes/app/coffee/MongoAWS.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 7fe90378c6..ca4914c733 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -41,7 +41,11 @@ module.exports = MongoAWS = .on 'finish', () -> return callback(null) - unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> + unArchiveDocHistory: (project_id, doc_id, _callback = (error) ->) -> + + callback = (args...) -> + _callback(args...) + _callback = () -> AWS.config.update { accessKeyId: settings.filestore.s3.key @@ -61,6 +65,8 @@ module.exports = MongoAWS = download .on 'open', (obj) -> return 1 + .on 'error', (err) -> + callback(err) .pipe lineStream .on 'data', (line) -> if line.length > 2 From b4ffa7d57e4bc9746bdeddb536fc4cd615e2c502 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 16:03:55 +0100 Subject: [PATCH 180/549] share the document lock between archiving and packing --- services/track-changes/app/coffee/DocArchiveManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 4da954b6b3..7c2eefd152 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -22,7 +22,7 @@ module.exports = DocArchiveManager = archiveDocChangesWithLock: (project_id, doc_id, callback = (error) ->) -> job = (releaseLock) -> DocArchiveManager.archiveDocChanges project_id, doc_id, releaseLock - LockManager.runWithLock("HistoryArchiveLock:#{doc_id}", job, callback) + LockManager.runWithLock("HistoryLock:#{doc_id}", job, callback) archiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getDocChangesCount doc_id, (error, count) -> From 18f06a3daf3da84668b09eb73c0f3b241d30468b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 16:09:38 +0100 Subject: [PATCH 181/549] increase lock timeouts for archiving --- services/track-changes/app/coffee/DocArchiveManager.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 7c2eefd152..4f512bdc27 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -7,6 +7,11 @@ _ = require "underscore" async = require "async" settings = require("settings-sharelatex") +# increase lock timeouts because archiving can be slow +LockManager.LOCK_TEST_INTERVAL = 500 # 500ms between each test of the lock +LockManager.MAX_LOCK_WAIT_TIME = 30000 # 30s maximum time to spend trying to get the lock +LockManager.LOCK_TTL = 30 # seconds + module.exports = DocArchiveManager = archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> From 7af50503701bb6bc7487fe761f17c8dd7a56c7f4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 16:18:36 +0100 Subject: [PATCH 182/549] add lock to unarchive doc --- services/track-changes/app/coffee/DocArchiveManager.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 4f512bdc27..1ae3b22363 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -52,9 +52,14 @@ module.exports = DocArchiveManager = else if !docs? return callback new Error("No docs for project #{project_id}") jobs = _.map docs, (doc) -> - (cb)-> DocArchiveManager.unArchiveDocChanges project_id, doc._id, cb + (cb)-> DocArchiveManager.unArchiveDocChangesWithLock project_id, doc._id, cb async.parallelLimit jobs, 4, callback + unArchiveDocChangesWithLock: (project_id, doc_id, callback = (error) ->) -> + job = (releaseLock) -> + DocArchiveManager.unArchiveDocChanges project_id, doc_id, releaseLock + LockManager.runWithLock("HistoryLock:#{doc_id}", job, callback) + unArchiveDocChanges: (project_id, doc_id, callback)-> MongoManager.getArchivedDocChanges doc_id, (error, count) -> return callback(error) if error? From 12c5098b48b1ab15a44710a3b048cacf061d6d68 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 16 Sep 2015 16:50:36 +0100 Subject: [PATCH 183/549] fix lock in test --- .../test/unit/coffee/DocArchive/DocArchiveManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee index ac266132c3..bcc2659d24 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -50,7 +50,7 @@ describe "DocArchiveManager", -> it "should run archiveDocChangesWithLock with the lock", -> @LockManager.runWithLock .calledWith( - "HistoryArchiveLock:#{@doc_id}" + "HistoryLock:#{@doc_id}" ) .should.equal true From a4575f178dd0e654cc8c3c50b8dfe931874376b3 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 16 Sep 2015 18:39:07 -0300 Subject: [PATCH 184/549] fix unit test --- .../DocArchive/DocArchiveManager.coffee | 24 ++++++++++--- .../unit/coffee/DocArchive/MongoAWS.coffee | 35 ++++++++++--------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee index bcc2659d24..f09889492d 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -78,15 +78,31 @@ describe "DocArchiveManager", -> describe "unArchiveAllDocsChanges", -> it "should unarchive all project docs change", (done)-> @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) - @DocArchiveManager.unArchiveDocChanges = sinon.stub().callsArgWith(2, null) + @DocArchiveManager.unArchiveDocChangesWithLock = sinon.stub().callsArgWith(2, null) @DocArchiveManager.unArchiveAllDocsChanges @project_id, (err)=> - @DocArchiveManager.unArchiveDocChanges.calledWith(@project_id, @mongoDocs[0]._id).should.equal true - @DocArchiveManager.unArchiveDocChanges.calledWith(@project_id, @mongoDocs[1]._id).should.equal true - @DocArchiveManager.unArchiveDocChanges.calledWith(@project_id, @mongoDocs[2]._id).should.equal true + @DocArchiveManager.unArchiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[0]._id).should.equal true + @DocArchiveManager.unArchiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[1]._id).should.equal true + @DocArchiveManager.unArchiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[2]._id).should.equal true should.not.exist err done() + describe "unArchiveDocChangesWithLock", -> + beforeEach -> + @DocArchiveManager.unArchiveDocChanges = sinon.stub().callsArg(2) + @LockManager.runWithLock = sinon.stub().callsArg(2) + @DocArchiveManager.unArchiveDocChangesWithLock @project_id, @doc_id, @callback + + it "should run unArchiveDocChangesWithLock with the lock", -> + @LockManager.runWithLock + .calledWith( + "HistoryLock:#{@doc_id}" + ) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + describe "unArchiveDocChanges", -> beforeEach -> @MongoManager.getArchivedDocChanges = sinon.stub().callsArg(1) diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 4279ec0ab6..b7a6787f0f 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -37,19 +37,20 @@ describe "MongoAWS", -> @awssdk.S3 = sinon.stub() @s3streams.WriteStream = sinon.stub() @db.docHistory = {} - @db.docHistory.pipe = sinon.stub() + @db.docHistory.on = sinon.stub() @db.docHistory.find = sinon.stub().returns @db.docHistory - @db.docHistory.pipe.returns + @db.docHistory.on.returns pipe:-> - on: (type, cb)-> - if type == "finish" - cb() + pipe:-> + on: (type, cb)-> + on: (type, cb)-> + cb() @JSONStream.stringify = sinon.stub() @MongoAWS.archiveDocHistory @project_id, @doc_id, @callback it "should call the callback", -> - @callback.calledWith(null).should.equal true + @callback.called.should.equal true describe "unArchiveDocHistory", -> @@ -61,21 +62,23 @@ describe "MongoAWS", -> @s3streams.ReadStream.returns #describe on 'open' behavior on: (type, cb)-> - pipe:-> - #describe on 'data' behavior - on: (type, cb)-> - cb([]) - #describe on 'end' behavior - on: (type, cb)-> - cb() - #describe on 'error' behavior - on: sinon.stub() + #describe on 'error' behavior + on: (type, cb)-> + pipe:-> + #describe on 'data' behavior + on: (type, cb)-> + cb([]) + #describe on 'end' behavior + on: (type, cb)-> + cb() + #describe on 'error' behavior + on: sinon.stub() @MongoAWS.handleBulk = sinon.stub() @MongoAWS.unArchiveDocHistory @project_id, @doc_id, @callback it "should call handleBulk", -> - @MongoAWS.handleBulk.calledWith([],@callback).should.equal true + @MongoAWS.handleBulk.called.should.equal true describe "handleBulk", -> beforeEach -> From 01cbfd533857c396a8ba85e70c104ca54a3da1cd Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 16 Sep 2015 19:33:23 -0300 Subject: [PATCH 185/549] fix minor issues in acceptance test --- .../test/acceptance/coffee/ArchivingUpdatesTests.coffee | 5 +++-- .../test/acceptance/coffee/GettingADiffTests.coffee | 4 ++-- .../test/acceptance/coffee/GettingUpdatesTests.coffee | 6 +++--- .../test/acceptance/coffee/RestoringVersions.coffee | 3 ++- .../test/acceptance/coffee/helpers/MockWebApi.coffee | 8 ++++---- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 973d4bde06..416b2de9d4 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -27,13 +27,14 @@ describe "Archiving updates", -> MockWebApi.projects[@project_id] = features: versioning: true + sinon.spy MockWebApi, "getProjectDetails" MockWebApi.users[@user_id] = @user = email: "user@sharelatex.com" first_name: "Leo" last_name: "Lion" id: @user_id - sinon.spy MockWebApi, "getUser" + sinon.spy MockWebApi, "getUserInfo" MockDocStoreApi.docs[@doc_id] = @doc = _id: @doc_id @@ -60,7 +61,7 @@ describe "Archiving updates", -> done() after (done) -> - MockWebApi.getUser.restore() + MockWebApi.getUserInfo.restore() db.docHistory.remove {project_id: ObjectId(@project_id)} TrackChangesClient.removeS3Doc @project_id, @doc_id, done diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index 1285177bf9..f64f42ff87 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -28,7 +28,7 @@ describe "Getting a diff", -> first_name: "Leo" last_name: "Lion" id: @user_id - sinon.spy MockWebApi, "getUser" + sinon.spy MockWebApi, "getUserInfo" twoMinutes = 2 * 60 * 1000 @@ -68,7 +68,7 @@ describe "Getting a diff", -> after () -> MockDocUpdaterApi.getDoc.restore() - MockWebApi.getUser.restore() + MockWebApi.getUserInfo.restore() it "should return the diff", -> expect(@diff).to.deep.equal @expected_diff diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index 7c94bcdc34..d01797e25f 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -31,7 +31,7 @@ describe "Getting updates", -> first_name: "Leo" last_name: "Lion" id: @user_id - sinon.spy MockWebApi, "getUser" + sinon.spy MockWebApi, "getUserInfo" @updates = [] for i in [0..9] @@ -52,7 +52,7 @@ describe "Getting updates", -> done() after: () -> - MockWebApi.getUser.restore() + MockWebApi.getUserInfo.restore() describe "getting updates up to the limit", -> before (done) -> @@ -62,7 +62,7 @@ describe "Getting updates", -> done() it "should fetch the user details from the web api", -> - MockWebApi.getUser + MockWebApi.getUserInfo .calledWith(@user_id) .should.equal true diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index ebe9a7795e..5dbe094cd8 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -20,7 +20,7 @@ describe "Restoring a version", -> @doc_id = ObjectId().toString() @project_id = ObjectId().toString() MockWebApi.projects[@project_id] = features: versioning: true - + minutes = 60 * 1000 @updates = [{ @@ -49,6 +49,7 @@ describe "Restoring a version", -> first_name: "Leo" last_name: "Lion" id: @user_id + MockDocUpdaterApi.docs[@doc_id] = lines: @lines version: 7 diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee index 002d944dfd..7beb67075e 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee @@ -6,15 +6,15 @@ module.exports = MockWebApi = projects: {} - getUser: (user_id, callback = (error) ->) -> + getUserInfo: (user_id, callback = (error) ->) -> callback null, @users[user_id] or null - getProject: (project_id, callback = (error, project) ->) -> + getProjectDetails: (project_id, callback = (error, project) ->) -> callback null, @projects[project_id] run: () -> app.get "/user/:user_id/personal_info", (req, res, next) => - @getUser req.params.user_id, (error, user) -> + @getUserInfo req.params.user_id, (error, user) -> if error? res.send 500 if !user? @@ -23,7 +23,7 @@ module.exports = MockWebApi = res.send JSON.stringify user app.get "/project/:project_id/details", (req, res, next) => - @getProject req.params.project_id, (error, project) -> + @getProjectDetails req.params.project_id, (error, project) -> if error? res.send 500 if !project? From 3f712c452a38fe5dea8fb0723d1949f79e947b69 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 17 Sep 2015 09:23:13 -0300 Subject: [PATCH 186/549] add size bulk limit --- services/track-changes/app/coffee/MongoAWS.coffee | 11 ++++++++--- .../test/unit/coffee/DocArchive/MongoAWS.coffee | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index ca4914c733..798126724b 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -5,10 +5,12 @@ S3S = require 's3-streams' {db, ObjectId} = require "./mongojs" JSONStream = require "JSONStream" ReadlineStream = require "readline-stream" +BSON=db.bson.BSON module.exports = MongoAWS = - bulkLimit: 10 + MAX_SIZE: 1024*1024 # almost max size + MAX_COUNT: 1024 # almost max count archiveDocHistory: (project_id, doc_id, _callback = (error) ->) -> @@ -61,6 +63,7 @@ module.exports = MongoAWS = lineStream = new ReadlineStream(); ops = [] + sz = 0 download .on 'open', (obj) -> @@ -71,11 +74,13 @@ module.exports = MongoAWS = .on 'data', (line) -> if line.length > 2 ops.push(JSON.parse(line)) - if ops.length == MongoAWS.bulkLimit + sz += BSON.calculateObjectSize(ops[ops.length-1]) + if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE download.pause() MongoAWS.handleBulk ops.slice(0), () -> download.resume() ops.splice(0,ops.length) + sz = 0 .on 'end', () -> MongoAWS.handleBulk ops, callback .on 'error', (err) -> @@ -95,7 +100,7 @@ module.exports = MongoAWS = if err? logger.error err:err, "error bulking ReadlineStream" else - logger.log count:ops.length, result:result, "bulked ReadlineStream" + logger.log count:ops.length, result:result, size: BSON.calculateObjectSize(ops), "bulked ReadlineStream" cb(err) else cb() diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index b7a6787f0f..7bbb654cd1 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -21,11 +21,11 @@ describe "MongoAWS", -> "aws-sdk": @awssdk = {} "fs": @fs = {} "s3-streams": @s3streams = {} - "./mongojs" : { db: @db = {}, ObjectId: ObjectId } + "./mongojs" : { db: @db = { bson: { BSON:{} } }, ObjectId: ObjectId } "JSONStream": @JSONStream = {} "readline-stream": @readline = sinon.stub() - @bulkLimit = @MongoAWS.bulkLimit + @db.bson.BSON.calculateObjectSize = sinon.stub().returns true @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @callback = sinon.stub() From aa66c5ee8c74e15628263b1f23dcb192263e5d35 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 17 Sep 2015 10:41:53 -0300 Subject: [PATCH 187/549] improve size function --- services/track-changes/app/coffee/MongoAWS.coffee | 11 +++++------ .../test/unit/coffee/DocArchive/MongoAWS.coffee | 5 ++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 798126724b..f2ad22bdf4 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -5,7 +5,6 @@ S3S = require 's3-streams' {db, ObjectId} = require "./mongojs" JSONStream = require "JSONStream" ReadlineStream = require "readline-stream" -BSON=db.bson.BSON module.exports = MongoAWS = @@ -74,19 +73,19 @@ module.exports = MongoAWS = .on 'data', (line) -> if line.length > 2 ops.push(JSON.parse(line)) - sz += BSON.calculateObjectSize(ops[ops.length-1]) + sz += line.length if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE download.pause() - MongoAWS.handleBulk ops.slice(0), () -> + MongoAWS.handleBulk ops.slice(0), sz, () -> download.resume() ops.splice(0,ops.length) sz = 0 .on 'end', () -> - MongoAWS.handleBulk ops, callback + MongoAWS.handleBulk ops, sz, callback .on 'error', (err) -> return callback(err) - handleBulk: (ops, cb) -> + handleBulk: (ops, size, cb) -> bulk = db.docHistory.initializeUnorderedBulkOp(); for op in ops @@ -100,7 +99,7 @@ module.exports = MongoAWS = if err? logger.error err:err, "error bulking ReadlineStream" else - logger.log count:ops.length, result:result, size: BSON.calculateObjectSize(ops), "bulked ReadlineStream" + logger.log count:ops.length, result:result, size: size, "bulked ReadlineStream" cb(err) else cb() diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 7bbb654cd1..0bad498eb5 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -21,11 +21,10 @@ describe "MongoAWS", -> "aws-sdk": @awssdk = {} "fs": @fs = {} "s3-streams": @s3streams = {} - "./mongojs" : { db: @db = { bson: { BSON:{} } }, ObjectId: ObjectId } + "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "JSONStream": @JSONStream = {} "readline-stream": @readline = sinon.stub() - @db.bson.BSON.calculateObjectSize = sinon.stub().returns true @project_id = ObjectId().toString() @doc_id = ObjectId().toString() @callback = sinon.stub() @@ -102,7 +101,7 @@ describe "MongoAWS", -> execute: sinon.stub().callsArgWith(0, null, {}) @db.docHistory = {} @db.docHistory.initializeUnorderedBulkOp = sinon.stub().returns @bulk - @MongoAWS.handleBulk @bulkOps, @callback + @MongoAWS.handleBulk @bulkOps, @bulkOps.length, @callback it "should call updateOne for each operation", -> @bulk.find.calledWith({_id:@bulkOps[0]._id}).should.equal true From 0e627c92d8afdf0c18cd4e6edab273548b122769 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 18 Sep 2015 16:26:05 +0100 Subject: [PATCH 188/549] avoid clobbering global _ in loop --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 9bea5ba177..6d19db7fbd 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -158,7 +158,7 @@ module.exports = UpdatesManager = users[update.meta.user_id] = true jobs = [] - for user_id, _ of users + for user_id of users do (user_id) -> jobs.push (callback) -> WebApiManager.getUserInfo user_id, (error, userInfo) -> From dc35ef5cda233edbbc717746762b9d7c0aea553c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 21 Sep 2015 13:24:06 +0100 Subject: [PATCH 189/549] fix tests for archiving --- .../test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index fc8853f2ab..f29f15f195 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -14,6 +14,7 @@ describe "UpdatesManager", -> "./LockManager" : @LockManager = {} "./WebApiManager": @WebApiManager = {} "./UpdateTrimmer": @UpdateTrimmer = {} + "./DocArchiveManager": @DocArchiveManager = {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } @doc_id = "doc-id-123" @project_id = "project-id-123" From d6b827426ccd3cbfbe9667d0e482ca0772c79af3 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 13:22:38 +0100 Subject: [PATCH 190/549] support forcing new compressed update in popLastCompressedUpdate callback with a null update, passing the version as an additional argument --- .../app/coffee/MongoManager.coffee | 19 ++++++++--- .../app/coffee/UpdatesManager.coffee | 25 +++++++------- .../MongoManager/MongoManagerTests.coffee | 21 +++++++++++- .../UpdatesManager/UpdatesManagerTests.coffee | 33 +++++++++++++++++++ 4 files changed, 81 insertions(+), 17 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 3bcf50af2a..383cacc198 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -20,13 +20,24 @@ module.exports = MongoManager = deleteCompressedUpdate: (id, callback = (error) ->) -> db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) - popLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> + popLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) -> + # under normal use we pass back the last update as + # callback(null,update). + # + # when we have an existing last update but want to force a new one + # to start, we pass it back as callback(null,null,version), just + # giving the version so we can check consistency. MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? if update? - MongoManager.deleteCompressedUpdate update._id, (error) -> - return callback(error) if error? - callback null, update + if update.inS3? + # we want to force a new update, but ensure that it is + # consistent with the version of the existing one in S3 + return callback null, null, update.v + else + MongoManager.deleteCompressedUpdate update._id, (error) -> + return callback(error) if error? + callback null, update else callback null, null diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index ffec3d1efe..0eb69b2b23 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -7,7 +7,6 @@ UpdateTrimmer = require "./UpdateTrimmer" logger = require "logger-sharelatex" async = require "async" DocArchiveManager = require "./DocArchiveManager" -_ = require "underscore" module.exports = UpdatesManager = compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> @@ -15,27 +14,29 @@ module.exports = UpdatesManager = if length == 0 return callback() - MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate) -> + MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion = lastCompressedUpdate?.v) -> + # lastCompressedUpdate may be null if we are forcing the start + # of a new compressed update, in which case we have the + # lastVersion to check consistency (defaults to lastCompressedUpdate.v) return callback(error) if error? - # Ensure that raw updates start where lastCompressedUpdate left off - if lastCompressedUpdate? + # Ensure that raw updates start where lastVersion left off + if lastVersion? rawUpdates = rawUpdates.slice(0) - while rawUpdates[0]? and rawUpdates[0].v <= lastCompressedUpdate.v + while rawUpdates[0]? and rawUpdates[0].v <= lastVersion rawUpdates.shift() - if rawUpdates[0]? and rawUpdates[0].v != lastCompressedUpdate.v + 1 - error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastCompressedUpdate.v}") + if rawUpdates[0]? and rawUpdates[0].v != lastVersion + 1 + error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion}") logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" - # Push the update back into Mongo - catching errors at this + # Push the update back into Mongo - if it was present - catching errors at this # point is useless, we're already bailing - MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], temporary, () -> - return callback error + if lastCompressedUpdate? + MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], temporary, () -> + return callback error return compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - if lastCompressedUpdate?.inS3? and not _.some(compressedUpdates, (update) -> update.inS3) - compressedUpdates[compressedUpdates.length-1].inS3 = lastCompressedUpdate.inS3 MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, temporary,(error) -> return callback(error) if error? diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 8db054b40f..6c127cd343 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -104,6 +104,25 @@ describe "MongoManager", -> it "should call the callback with the update", -> @callback.calledWith(null, @update).should.equal true + describe "when there is a last update in S3", -> + beforeEach -> + @update = { _id: Object(), v: 12345, inS3: true } + @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) + @MongoManager.deleteCompressedUpdate = sinon.stub() + @MongoManager.popLastCompressedUpdate @doc_id, @callback + + it "should get the last update", -> + @MongoManager.getLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should not try to delete the last update", -> + @MongoManager.deleteCompressedUpdate.called.should.equal false + + it "should call the callback with a null update and the correct version", -> + @callback.calledWith(null, null, @update.v).should.equal true + + describe "insertCompressedUpdate", -> beforeEach -> @update = { op: "op", meta: "meta", v: "v"} @@ -483,4 +502,4 @@ describe "MongoManager", -> .should.equal true it "should call the callback", -> - @callback.called.should.equal true \ No newline at end of file + @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index f29f15f195..04a2111893 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -122,6 +122,39 @@ describe "UpdatesManager", -> .calledWith(@project_id, @doc_id, [@lastCompressedUpdate], @temporary) .should.equal true + describe "when the raw ops need appending to existing history which is in S3", -> + beforeEach -> + @lastCompressedUpdate = null + @lastVersion = 11 + @compressedUpdates = { v: 13, op: "compressed-op-12" } + + @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, @lastVersion) + @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) + @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) + + describe "when the raw ops start where the existing history ends", -> + beforeEach -> + @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + + it "should try to pop the last compressed op", -> + @MongoManager.popLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should compress the last compressed op and the raw ops", -> + @UpdateCompressor.compressRawUpdates + .calledWith(@lastCompressedUpdate, @rawUpdates) + .should.equal true + + it "should save the compressed ops", -> + @MongoManager.insertCompressedUpdates + .calledWith(@project_id, @doc_id, @compressedUpdates, @temporary) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + describe "processUncompressedUpdates", -> beforeEach -> @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(4) From 551e8334cf629c4b324e549d8566dc50f2c3f3d5 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 13:25:10 +0100 Subject: [PATCH 191/549] compressedUpdates are now never inserted with inS3 it is now always added later, and a new update is forced for any addition to an archived update --- services/track-changes/app/coffee/MongoManager.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 383cacc198..78627ebbd3 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -59,7 +59,6 @@ module.exports = MongoManager = insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> - inS3 = update.inS3? update = { doc_id: ObjectId(doc_id.toString()) project_id: ObjectId(project_id.toString()) @@ -67,8 +66,6 @@ module.exports = MongoManager = meta: update.meta v: update.v } - if inS3 - update.inS3 = true if temporary seconds = 1000 From e49f260507fb3c8028623dc0de0cdff1bafc3b7b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 13:28:07 +0100 Subject: [PATCH 192/549] allow rollback/locking by setting inS3:false when starting the archive process --- .../app/coffee/DocArchiveManager.coffee | 16 +++++++++++----- .../track-changes/app/coffee/MongoManager.coffee | 7 +++++++ .../app/coffee/UpdatesManager.coffee | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 1ae3b22363..05d8430984 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -38,12 +38,18 @@ module.exports = DocArchiveManager = else MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? - MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + MongoManager.markDocHistoryAsArchiveInProgress doc_id, update, (error) -> return callback(error) if error? - logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" - MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> - return callback(error) if error? - callback() + MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + if error? + MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> + return callback(err) if err? + callback(error) + else + logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" + MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> + return callback(error) if error? + callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> DocstoreHandler.getAllDocs project_id, (error, docs) -> diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 78627ebbd3..c31d466655 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -146,6 +146,12 @@ module.exports = MongoManager = getArchivedDocChanges: (doc_id, callback)-> db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback + markDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> + db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback + + clearDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> + db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback + markDocHistoryAsArchived: (doc_id, update, callback)-> db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> return callback(error) if error? @@ -154,5 +160,6 @@ module.exports = MongoManager = callback(error) markDocHistoryAsUnarchived: (doc_id, callback)-> + # note this removes any inS3 field, regardless of its value (true/false/null) db.docHistory.update { doc_id: ObjectId(doc_id.toString()) }, { $unset : { inS3 : true } }, { multi: true }, (error)-> callback(error) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 0eb69b2b23..68507d7237 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -103,7 +103,7 @@ module.exports = UpdatesManager = MongoManager.getProjectUpdates project_id, options, (error, updates) -> jobs = [] for update in updates - if update.inS3? + if update.inS3 do (update) -> jobs.push (callback) -> DocArchiveManager.unArchiveDocChanges update.project_id, update.doc_id, callback if jobs.length? From 847a553344d3de487e37195bdd0bb9f760c545c7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 13:29:32 +0100 Subject: [PATCH 193/549] prevent double archiving by checking if any inS3 field is already present --- .../app/coffee/DocArchiveManager.coffee | 29 +++++++++++-------- .../app/coffee/MongoManager.coffee | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 05d8430984..c69509ab10 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -36,20 +36,25 @@ module.exports = DocArchiveManager = logger.log {project_id, doc_id}, "document history is empty, not archiving" return callback() else - MongoManager.getLastCompressedUpdate doc_id, (error, update) -> + MongoManager.getArchivedDocChanges doc_id, (error, count) -> return callback(error) if error? - MongoManager.markDocHistoryAsArchiveInProgress doc_id, update, (error) -> + if count != 0 + logger.log {project_id, doc_id}, "document history contains archived entries, not archiving" + return callback() + MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? - MongoAWS.archiveDocHistory project_id, doc_id, (error) -> - if error? - MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> - return callback(err) if err? - callback(error) - else - logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" - MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> - return callback(error) if error? - callback() + MongoManager.markDocHistoryAsArchiveInProgress doc_id, update, (error) -> + return callback(error) if error? + MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + if error? + MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> + return callback(err) if err? + callback(error) + else + logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" + MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> + return callback(error) if error? + callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> DocstoreHandler.getAllDocs project_id, (error, docs) -> diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index c31d466655..7fe7d2ab4f 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -144,7 +144,7 @@ module.exports = MongoManager = db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, {}, callback getArchivedDocChanges: (doc_id, callback)-> - db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: true }, {}, callback + db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: { $exists: true }}, {}, callback markDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback From 696a866b67cc1d5cad2d0263d2f42e0777c130fc Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 13:38:57 +0100 Subject: [PATCH 194/549] pause the stream of ops, not the download the download is buffered in the lineStream so a lot comes out even after pausing the S3 download. --- services/track-changes/app/coffee/MongoAWS.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index f2ad22bdf4..e9fb3b76d2 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -64,20 +64,21 @@ module.exports = MongoAWS = ops = [] sz = 0 - download + inputStream = download .on 'open', (obj) -> return 1 .on 'error', (err) -> callback(err) .pipe lineStream - .on 'data', (line) -> + + inputStream.on 'data', (line) -> if line.length > 2 ops.push(JSON.parse(line)) sz += line.length if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE - download.pause() + inputStream.pause() MongoAWS.handleBulk ops.slice(0), sz, () -> - download.resume() + inputStream.resume() ops.splice(0,ops.length) sz = 0 .on 'end', () -> From dc0044020fb1712ad86bffe569c4bf0b9788cae4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 14:33:40 +0100 Subject: [PATCH 195/549] only archive entries older than the current update to avoid a stale version of the current update ever being pulled back from S3 --- services/track-changes/app/coffee/DocArchiveManager.coffee | 5 ++++- services/track-changes/app/coffee/MongoAWS.coffee | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index c69509ab10..456bca8d07 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -35,6 +35,9 @@ module.exports = DocArchiveManager = if count == 0 logger.log {project_id, doc_id}, "document history is empty, not archiving" return callback() + else if count == 1 + logger.log {project_id, doc_id}, "document history only has one entry, not archiving" + return callback() else MongoManager.getArchivedDocChanges doc_id, (error, count) -> return callback(error) if error? @@ -45,7 +48,7 @@ module.exports = DocArchiveManager = return callback(error) if error? MongoManager.markDocHistoryAsArchiveInProgress doc_id, update, (error) -> return callback(error) if error? - MongoAWS.archiveDocHistory project_id, doc_id, (error) -> + MongoAWS.archiveDocHistory project_id, doc_id, update, (error) -> if error? MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> return callback(err) if err? diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index e9fb3b76d2..36dd2715d7 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -11,7 +11,7 @@ module.exports = MongoAWS = MAX_SIZE: 1024*1024 # almost max size MAX_COUNT: 1024 # almost max count - archiveDocHistory: (project_id, doc_id, _callback = (error) ->) -> + archiveDocHistory: (project_id, doc_id, update, _callback = (error) ->) -> callback = (args...) -> _callback(args...) @@ -19,6 +19,7 @@ module.exports = MongoAWS = query = { doc_id: ObjectId(doc_id) + v: {$lt: update.v} expiresAt: {$exists : false} } From bdf1d267f0c44d0d74a817b5a4f83d01f0092b61 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 15:34:36 +0100 Subject: [PATCH 196/549] fix tests --- .../test/unit/coffee/DocArchive/DocArchiveManager.coffee | 4 +++- .../track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee | 3 ++- .../test/unit/coffee/MongoManager/MongoManagerTests.coffee | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee index f09889492d..fe648b6e67 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -61,8 +61,10 @@ describe "DocArchiveManager", -> beforeEach -> @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} @MongoManager.getDocChangesCount = sinon.stub().callsArg(1) + @MongoManager.getArchivedDocChanges = sinon.stub().callsArgWith(1, null, 0) @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) - @MongoAWS.archiveDocHistory = sinon.stub().callsArg(2) + @MongoAWS.archiveDocHistory = sinon.stub().callsArg(3) + @MongoManager.markDocHistoryAsArchiveInProgress = sinon.stub().callsArg(2) @MongoManager.markDocHistoryAsArchived = sinon.stub().callsArg(2) @DocArchiveManager.archiveDocChanges @project_id, @doc_id, @callback diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 0bad498eb5..5667f0fada 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -27,6 +27,7 @@ describe "MongoAWS", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() + @update = { v:123 } @callback = sinon.stub() describe "archiveDocHistory", -> @@ -46,7 +47,7 @@ describe "MongoAWS", -> cb() @JSONStream.stringify = sinon.stub() - @MongoAWS.archiveDocHistory @project_id, @doc_id, @callback + @MongoAWS.archiveDocHistory @project_id, @doc_id, @update, @callback it "should call the callback", -> @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 6c127cd343..5d5f210035 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -446,7 +446,7 @@ describe "MongoManager", -> @db.docHistory.count .calledWith({ doc_id: ObjectId(@doc_id) - inS3 : true + inS3: {$exists: true} }, { }) .should.equal true From 2ab1778dd9dc858ff3d9478f0cf631782e76512d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 23 Sep 2015 16:31:33 +0100 Subject: [PATCH 197/549] move default value of lastVersion into function body --- .../app/coffee/UpdatesManager.coffee | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 68507d7237..e1c9541748 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -14,10 +14,18 @@ module.exports = UpdatesManager = if length == 0 return callback() - MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion = lastCompressedUpdate?.v) -> - # lastCompressedUpdate may be null if we are forcing the start - # of a new compressed update, in which case we have the - # lastVersion to check consistency (defaults to lastCompressedUpdate.v) + MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> + # lastCompressedUpdate is the most recent update in Mongo. + # + # The popLastCompressedUpdate method may pass it back as 'null' + # to force the start of a new compressed update, even when there + # was a previous compressed update in Mongo. In this case it + # passes back the lastVersion from the update to check + # consistency. + + # when lastVersion is not provided, default to lastCompressedUpdate.v + lastVersion ?= lastCompressedUpdate?.v + return callback(error) if error? # Ensure that raw updates start where lastVersion left off From 692e8c657ce0cf857adae058971bba5e825bbb9a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Sep 2015 08:53:09 +0100 Subject: [PATCH 198/549] Revert to the default lock timeout now we have write barriers Revert "increase lock timeouts for archiving" This reverts commit 9eee1b383772adf058130d6e5eab409f57ce03cd. --- services/track-changes/app/coffee/DocArchiveManager.coffee | 5 ----- 1 file changed, 5 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index 456bca8d07..bb3c66eccf 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -7,11 +7,6 @@ _ = require "underscore" async = require "async" settings = require("settings-sharelatex") -# increase lock timeouts because archiving can be slow -LockManager.LOCK_TEST_INTERVAL = 500 # 500ms between each test of the lock -LockManager.MAX_LOCK_WAIT_TIME = 30000 # 30s maximum time to spend trying to get the lock -LockManager.LOCK_TTL = 30 # seconds - module.exports = DocArchiveManager = archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> From e683b0275ae8bcfcdbac74e47bfa751f1daf67ff Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Sep 2015 09:09:49 +0100 Subject: [PATCH 199/549] bug fix for clear archive in progress flag --- services/track-changes/app/coffee/MongoManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 7fe7d2ab4f..ce97bee016 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -150,7 +150,7 @@ module.exports = MongoManager = db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback clearDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> - db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback + db.docHistory.update { _id: update._id }, { $unset : { inS3 : true } }, callback markDocHistoryAsArchived: (doc_id, update, callback)-> db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> From 92e0b0f04c1867caaee0f74eaec176137c989bd8 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Sep 2015 09:10:06 +0100 Subject: [PATCH 200/549] add logging to each stage of archiving --- services/track-changes/app/coffee/DocArchiveManager.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index bb3c66eccf..b569f3e37b 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -41,17 +41,22 @@ module.exports = DocArchiveManager = return callback() MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? + logger.log {doc_id, project_id}, "archiving got last compressed update" MongoManager.markDocHistoryAsArchiveInProgress doc_id, update, (error) -> return callback(error) if error? + logger.log {doc_id, project_id}, "marked doc history as archive in progress" MongoAWS.archiveDocHistory project_id, doc_id, update, (error) -> if error? + logger.log {doc_id, project_id, error}, "error exporting document to S3" MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> return callback(err) if err? + logger.log {doc_id, project_id}, "cleared archive in progress flag" callback(error) else logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> return callback(error) if error? + logger.log {doc_id, project_id}, "marked doc history as archived" callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> From 23dfe68cb891bc37b6f1ad2d3a71c3001c39c31d Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 25 Sep 2015 13:44:44 +0100 Subject: [PATCH 201/549] Don't error when rewinding and insert op which is beyond the length of the document. ShareJS will accept an op where p > content.length when applied, and it applies as though p == content.length. However, the op is passed to us with the original p > content.length. Detect if that is the case with this op, and shift p back appropriately to match ShareJS if so. --- .../app/coffee/DiffGenerator.coffee | 17 +++++++++++++++-- .../DiffGenerator/DiffGeneratorTests.coffee | 9 ++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index 9593f42f36..e0fcf744cd 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -5,6 +5,8 @@ ConsistencyError = (message) -> return error ConsistencyError.prototype.__proto__ = Error.prototype +logger = require "logger-sharelatex" + module.exports = DiffGenerator = ConsistencyError: ConsistencyError @@ -15,13 +17,24 @@ module.exports = DiffGenerator = rewindOp: (content, op) -> if op.i? - textToBeRemoved = content.slice(op.p, op.p + op.i.length) + # ShareJS will accept an op where p > content.length when applied, + # and it applies as though p == content.length. However, the op is + # passed to us with the original p > content.length. Detect if that + # is the case with this op, and shift p back appropriately to match + # ShareJS if so. + p = op.p + max_p = content.length - op.i.length + if p > max_p + logger.warn {max_p, p}, "truncating position to content length" + p = max_p + + textToBeRemoved = content.slice(p, p + op.i.length) if op.i != textToBeRemoved throw new ConsistencyError( "Inserted content, '#{op.i}', does not match text to be removed, '#{textToBeRemoved}'" ) - return content.slice(0, op.p) + content.slice(op.p + op.i.length) + return content.slice(0, p) + content.slice(p + op.i.length) else if op.d? return content.slice(0, op.p) + op.d + content.slice(op.p) diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee index 3263d84dda..b956606a7d 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee @@ -7,7 +7,8 @@ SandboxedModule = require('sandboxed-module') describe "DiffGenerator", -> beforeEach -> - @DiffGenerator = SandboxedModule.require modulePath + @DiffGenerator = SandboxedModule.require modulePath, requires: + "logger-sharelatex": { warn: sinon.stub() } @ts = Date.now() @user_id = "mock-user-id" @user_id_2 = "mock-user-id-2" @@ -34,6 +35,12 @@ describe "DiffGenerator", -> expect( () => @DiffGenerator.rewindOp content, { p: 6, i: "foo" } ).to.throw(@DiffGenerator.ConsistencyError) + + describe "with an update which is beyond the length of the content", -> + it "should undo the insert as if it were at the end of the content", -> + content = "foobar" + rewoundContent = @DiffGenerator.rewindOp content, { p: 4, i: "bar" } + rewoundContent.should.equal "foo" describe "rewindUpdate", -> it "should rewind ops in reverse", -> From 2a0359103068c83a4916df44bb35c93379a3a730 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 25 Sep 2015 13:46:20 +0100 Subject: [PATCH 202/549] Stub out noisy/slow logger-sharelatex and mongojs modules in tests --- services/track-changes/app/coffee/MongoManager.coffee | 1 - .../test/unit/coffee/MongoManager/MongoManagerTests.coffee | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index ce97bee016..87f4024a51 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,7 +1,6 @@ {db, ObjectId} = require "./mongojs" PackManager = require "./PackManager" async = require "async" -logger = require "logger-sharelatex" module.exports = MongoManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 5d5f210035..0556e32413 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -15,6 +15,8 @@ describe "MongoManager", -> "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "./PackManager" : SandboxedModule.require packModulePath, requires: "./LockManager" : {} + "./mongojs": {db: bson: BSON = sinon.stub(), ObjectId} + "logger-sharelatex": {} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From 6052bf6a7eecf117dac52ca7d8b5973be6e1d933 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 1 Oct 2015 15:54:46 +0100 Subject: [PATCH 203/549] change test to 19 not 20 as a stub in mongo is left behind --- .../test/acceptance/coffee/ArchivingUpdatesTests.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 416b2de9d4..0b77f95b30 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -89,9 +89,9 @@ describe "Archiving updates", -> doc.v.should.equal 20 done() - it "should store twenty doc changes in S3", (done) -> + it "should store nineteen doc changes in S3", (done) -> TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => - doc.length.should.equal 20 + doc.length.should.equal 19 done() describe "unarchiving a doc's updates", -> From ef69729dfd972f20d09bdd000bfac5fb5613873f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 1 Oct 2015 15:55:07 +0100 Subject: [PATCH 204/549] change archive and unarchive to post's --- services/track-changes/app.coffee | 4 ++-- .../test/acceptance/coffee/helpers/TrackChangesClient.coffee | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index f5f12078ac..1600404963 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -27,8 +27,8 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post "/doc/:doc_id/pack", HttpController.packDoc -app.get '/project/:project_id/archive', HttpController.archiveProject -app.get '/project/:project_id/unarchive', HttpController.unArchiveProject +app.post '/project/:project_id/archive', HttpController.archiveProject +app.post '/project/:project_id/unarchive', HttpController.unArchiveProject packWorker = null # use a single packing worker diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 1e409908d9..d7872e288a 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -75,14 +75,14 @@ module.exports = TrackChangesClient = callback null archiveProject: (project_id, callback = (error) ->) -> - request.get { + request.post { url: "http://localhost:3015/project/#{project_id}/archive" }, (error, response, body) => response.statusCode.should.equal 204 callback(error) unarchiveProject: (project_id, callback = (error) ->) -> - request.get { + request.post { url: "http://localhost:3015/project/#{project_id}/unarchive" }, (error, response, body) => response.statusCode.should.equal 204 From 1a4b8f4269d9ccfaa8fcd592db77bee7f88265e7 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 7 Oct 2015 13:44:40 +0100 Subject: [PATCH 205/549] API/service layout deprecation warning --- services/track-changes/app/coffee/WebApiManager.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index d7441682e3..4e8e9602d8 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -2,6 +2,11 @@ request = require "request" logger = require "logger-sharelatex" Settings = require "settings-sharelatex" +# DEPRECATED! This method of getting user details via track-changes is deprecated +# in the way we lay out our services. +# Instead, web should be responsible for collecting the raw data (user_ids) and +# filling it out with calls to other services. All API calls should create a +# tree-like structure as much as possible, with web as the root. module.exports = WebApiManager = sendRequest: (url, callback = (error, body) ->) -> request.get { @@ -56,4 +61,4 @@ module.exports = WebApiManager = project = JSON.parse(body) catch error return callback(error) - callback null, project \ No newline at end of file + callback null, project From add6a68fe1f733b01ace22fda7b445e53ed4eeb0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 8 Oct 2015 10:53:25 +0100 Subject: [PATCH 206/549] add missing callback in compressAndSaveRawUpdates --- services/track-changes/app/coffee/UpdatesManager.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index e1c9541748..80b3a0d58b 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -42,7 +42,8 @@ module.exports = UpdatesManager = if lastCompressedUpdate? MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], temporary, () -> return callback error - return + else + return callback error compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates From 8226bf3be4ea61217d395be1c7248d810e44ef63 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 8 Oct 2015 14:43:34 +0100 Subject: [PATCH 207/549] increase lock time to 5 minutes --- services/track-changes/app/coffee/LockManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 1b81687c36..48fdc1665a 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -5,7 +5,7 @@ rclient = redis.createClient(Settings.redis.web) module.exports = LockManager = LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock MAX_LOCK_WAIT_TIME: 10000 # 10s maximum time to spend trying to get the lock - LOCK_TTL: 10 # seconds + LOCK_TTL: 300 # seconds (allow 5 minutes for any operation to complete) tryLock : (key, callback = (err, gotLock) ->) -> rclient.set key, "locked", "EX", @LOCK_TTL, "NX", (err, gotLock)-> From b6dae596550b7f3761cd64cd6df31c9311e67baf Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 8 Oct 2015 16:39:13 +0100 Subject: [PATCH 208/549] fix callback logic in compressAndSaveRawUpdates --- services/track-changes/app/coffee/UpdatesManager.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 80b3a0d58b..4703ff6a1d 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -43,7 +43,8 @@ module.exports = UpdatesManager = MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], temporary, () -> return callback error else - return callback error + callback error + return compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates From 8961e2395491a7be53f8aed07aac0c60b67193ef Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 8 Oct 2015 14:20:36 +0100 Subject: [PATCH 209/549] enhance LockManager to avoid accidental unlocking --- .../app/coffee/LockManager.coffee | 33 ++++++++++++++----- .../acceptance/coffee/LockManagerTests.coffee | 31 +++++++++++++++++ .../LockManager/LockManagerTests.coffee | 7 ++-- 3 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 services/track-changes/test/acceptance/coffee/LockManagerTests.coffee diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 48fdc1665a..349410752e 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -1,17 +1,34 @@ Settings = require "settings-sharelatex" redis = require("redis-sharelatex") rclient = redis.createClient(Settings.redis.web) +os = require "os" +crypto = require "crypto" + +HOST = os.hostname() +PID = process.pid +RND = crypto.randomBytes(4).toString('hex') +COUNT = 0 module.exports = LockManager = LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock MAX_LOCK_WAIT_TIME: 10000 # 10s maximum time to spend trying to get the lock LOCK_TTL: 300 # seconds (allow 5 minutes for any operation to complete) + # Use a signed lock value as described in + # http://redis.io/topics/distlock#correct-implementation-with-a-single-instance + # to prevent accidental unlocking by multiple processes + randomLock : () -> + time = Date.now() + return "locked:host=#{HOST}:pid=#{PID}:random=#{RND}:time=#{time}:count=#{COUNT++}" + + unlockScript: 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end'; + tryLock : (key, callback = (err, gotLock) ->) -> - rclient.set key, "locked", "EX", @LOCK_TTL, "NX", (err, gotLock)-> + lockValue = LockManager.randomLock() + rclient.set key, lockValue, "EX", @LOCK_TTL, "NX", (err, gotLock)-> return callback(err) if err? if gotLock == "OK" - callback err, true + callback err, true, lockValue else callback err, false @@ -21,10 +38,10 @@ module.exports = LockManager = if Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME return callback(new Error("Timeout")) - LockManager.tryLock key, (error, gotLock) -> + LockManager.tryLock key, (error, gotLock, lockValue) -> return callback(error) if error? if gotLock - callback(null) + callback(null, lockValue) else setTimeout attempt, LockManager.LOCK_TEST_INTERVAL @@ -37,14 +54,14 @@ module.exports = LockManager = else callback err, true - releaseLock: (key, callback) -> - rclient.del key, callback + releaseLock: (key, lockValue, callback) -> + rclient.eval LockManager.unlockScript, 1, key, lockValue, callback runWithLock: (key, runner = ( (releaseLock = (error) ->) -> ), callback = ( (error) -> )) -> - LockManager.getLock key, (error) -> + LockManager.getLock key, (error, lockValue) -> return callback(error) if error? runner (error1) -> - LockManager.releaseLock key, (error2) -> + LockManager.releaseLock key, lockValue, (error2) -> error = error1 or error2 return callback(error) if error? callback() diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee b/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee new file mode 100644 index 0000000000..6de4599a6b --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee @@ -0,0 +1,31 @@ +sinon = require "sinon" +chai = require("chai") +chai.should() +expect = chai.expect +mongojs = require "../../../app/js/mongojs" +ObjectId = mongojs.ObjectId +Settings = require "settings-sharelatex" +LockManager = require "../../../app/js/LockManager" +rclient = require("redis").createClient() # Only works locally for now + +describe "Locking document", -> + describe "when the lock has expired in redis", -> + before (done) -> + LockManager.LOCK_TTL = 1 # second + LockManager.runWithLock "doc123", (releaseA) => + # we create a lock A and allow it to expire in redis + setTimeout () -> + # now we create a new lock B and try to release A + LockManager.runWithLock "doc123", (releaseB) => + releaseA() # try to release lock A to see if it wipes out lock B + , (error) -> + # we never release lock B so nothing should happen here + , 1500 # enough time to wait until the lock has expired + , (error) -> + # we get here after trying to release lock A + done() + + it "the new lock should not be removed by the expired locker", (done) -> + LockManager.checkLock "doc123", (err, isFree) -> + expect(isFree).to.equal false + done() diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index 9ea9c81f15..298665a00b 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -46,11 +46,12 @@ describe "LockManager", -> describe "when the lock is taken", -> beforeEach -> @rclient.set = sinon.stub().callsArgWith(5, null, null) + @LockManager.randomLock = sinon.stub().returns "locked-random-value" @LockManager.tryLock @key, @callback it "should check the lock in redis", -> @rclient.set - .calledWith(@key, "locked", "EX", @LockManager.LOCK_TTL, "NX") + .calledWith(@key, "locked-random-value", "EX", @LockManager.LOCK_TTL, "NX") .should.equal true it "should return the callback with false", -> @@ -137,7 +138,7 @@ describe "LockManager", -> releaseLock() sinon.spy @, "runner" @LockManager.getLock = sinon.stub().callsArg(1) - @LockManager.releaseLock = sinon.stub().callsArg(1) + @LockManager.releaseLock = sinon.stub().callsArg(2) @LockManager.runWithLock @key, @runner, @callback it "should get the lock", -> @@ -163,7 +164,7 @@ describe "LockManager", -> releaseLock(@error) sinon.spy @, "runner" @LockManager.getLock = sinon.stub().callsArg(1) - @LockManager.releaseLock = sinon.stub().callsArg(1) + @LockManager.releaseLock = sinon.stub().callsArg(2) @LockManager.runWithLock @key, @runner, @callback it "should release the lock", -> From ad144371d09b7563d2e19fb7270568c12a43ff66 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 16 Oct 2015 11:24:50 +0100 Subject: [PATCH 210/549] gracefully handle updates marked as broken set update.broken == true to allow the user to view history without a crash --- services/track-changes/app/coffee/DiffManager.coffee | 10 +++++++++- services/track-changes/app/coffee/MongoManager.coffee | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 9183c8ecaa..29016fc42c 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -14,7 +14,11 @@ module.exports = DiffManager = getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) -> logger.log project_id: project_id, doc_id: doc_id, from: fromVersion, to: toVersion, "getting diff" DiffManager.getDocumentBeforeVersion project_id, doc_id, fromVersion, (error, startingContent, updates) -> - return callback(error) if error? + if error? + if error.message == "broken-history" + return callback(null, "history unavailable") + else + return callback(error) updatesToApply = [] for update in updates.slice().reverse() @@ -33,6 +37,10 @@ module.exports = DiffManager = DiffManager.getLatestDocAndUpdates project_id, doc_id, version, null, (error, content, version, updates) -> return callback(error) if error? + # bail out if we hit a broken update + for u in updates when u.broken + return callback new Error "broken-history" + lastUpdate = updates[0] if lastUpdate? and lastUpdate.v != version - 1 return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 87f4024a51..9b5c63c85f 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -33,6 +33,9 @@ module.exports = MongoManager = # we want to force a new update, but ensure that it is # consistent with the version of the existing one in S3 return callback null, null, update.v + else if update.broken + # the update is marked as broken so we will force a new op + return callback null, null else MongoManager.deleteCompressedUpdate update._id, (error) -> return callback(error) if error? From c44d5b1b3d390955f25a6bf5fb044a51a073a1f4 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 19 Oct 2015 10:59:39 +0100 Subject: [PATCH 211/549] added healthcheck --- services/track-changes/app.coffee | 2 ++ .../app/coffee/HealthChecker.coffee | 31 +++++++++++++++++++ .../app/coffee/HttpController.coffee | 11 ++++++- services/track-changes/package.json | 2 +- 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 services/track-changes/app/coffee/HealthChecker.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 1600404963..96423b3166 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -47,6 +47,8 @@ app.post "/pack", (req, res, next) -> app.get "/status", (req, res, next) -> res.send "track-changes is alive" +app.get "/health_check", HttpController.healthCheck + app.use (error, req, res, next) -> logger.error err: error, "an internal error occured" res.send 500 diff --git a/services/track-changes/app/coffee/HealthChecker.coffee b/services/track-changes/app/coffee/HealthChecker.coffee new file mode 100644 index 0000000000..efa3c2335d --- /dev/null +++ b/services/track-changes/app/coffee/HealthChecker.coffee @@ -0,0 +1,31 @@ +ObjectId = require("mongojs").ObjectId +request = require("request") +async = require("async") +settings = require("settings-sharelatex") +port = settings.internal.trackchanges.port +logger = require "logger-sharelatex" + +module.exports = + check : (callback)-> + project_id = ObjectId(settings.trackchanges.healthCheck.project_id) + url = "http://localhost:#{port}/project/#{project_id}" + logger.log project_id:project_id, "running health check" + jobs = [ + (cb)-> + request.post {url:"#{url}/flush", timeout:3000}, (err, res, body) -> + if err? + cb(err) + else if res?.statusCode != 204 + cb("status code not 204, it's #{res.statusCode}") + else + cb() + (cb)-> + request.get {url:"#{url}/updates", timeout:3000}, (err, res, body)-> + if err? + cb(err) + else if res?.statusCode != 200 + cb("status code not 200, it's #{res.statusCode}") + else + cb() + ] + async.series jobs, callback diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 298311c180..c91eb802bf 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -4,6 +4,7 @@ PackManager = require "./PackManager" RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" DocArchiveManager = require "./DocArchiveManager" +HealthChecker = require "./HealthChecker" module.exports = HttpController = flushDoc: (req, res, next = (error) ->) -> @@ -80,4 +81,12 @@ module.exports = HttpController = logger.log project_id: project_id, "unarchiving all track changes from s3" DocArchiveManager.unArchiveAllDocsChanges project_id, (error) -> return next(error) if error? - res.send 204 \ No newline at end of file + res.send 204 + + healthCheck: (req, res)-> + HealthChecker.check (err)-> + if err? + logger.err err:err, "error performing health check" + res.send 500 + else + res.send 200 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 4fe112262a..7c14dd8f28 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -16,7 +16,7 @@ "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", - "redis-sharelatex": "~0.0.4", + "redis-sharelatex": "~0.0.9", "redis": "~0.10.1", "underscore": "~1.7.0", "mongo-uri": "^0.1.2", From 992857d6a2568cb8fb9249675ba773947d694516 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 29 Oct 2015 10:52:23 +0000 Subject: [PATCH 212/549] added redis write check to healthcheck --- services/track-changes/app.coffee | 2 ++ services/track-changes/app/coffee/HealthChecker.coffee | 8 ++++++++ services/track-changes/app/coffee/HttpController.coffee | 9 +++++++++ services/track-changes/app/coffee/LockManager.coffee | 5 ++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 96423b3166..a85c9de290 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -47,6 +47,8 @@ app.post "/pack", (req, res, next) -> app.get "/status", (req, res, next) -> res.send "track-changes is alive" +app.get "/check_lock", HttpController.checkLock + app.get "/health_check", HttpController.healthCheck app.use (error, req, res, next) -> diff --git a/services/track-changes/app/coffee/HealthChecker.coffee b/services/track-changes/app/coffee/HealthChecker.coffee index efa3c2335d..87b31bb533 100644 --- a/services/track-changes/app/coffee/HealthChecker.coffee +++ b/services/track-changes/app/coffee/HealthChecker.coffee @@ -11,6 +11,14 @@ module.exports = url = "http://localhost:#{port}/project/#{project_id}" logger.log project_id:project_id, "running health check" jobs = [ + (cb)-> + request.get {url:"http://localhost:#{port}/check_lock", timeout:3000}, (err, res, body) -> + if err? + cb(err) + else if res?.statusCode != 200 + cb("status code not 200, it's #{res.statusCode}") + else + cb() (cb)-> request.post {url:"#{url}/flush", timeout:3000}, (err, res, body) -> if err? diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index c91eb802bf..ecc62e1e3d 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -5,6 +5,7 @@ RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" DocArchiveManager = require "./DocArchiveManager" HealthChecker = require "./HealthChecker" +LockManager = require "./LockManager" module.exports = HttpController = flushDoc: (req, res, next = (error) ->) -> @@ -90,3 +91,11 @@ module.exports = HttpController = res.send 500 else res.send 200 + + checkLock: (req, res)-> + LockManager.healthCheck (err) -> + if err? + logger.err err:err, "error performing lock check" + res.send 500 + else + res.send 200 diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 349410752e..e2123cbfdf 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -66,4 +66,7 @@ module.exports = LockManager = return callback(error) if error? callback() - + healthCheck: (callback) -> + action = (releaseLock) -> + releaseLock() + LockManager.runWithLock "HistoryLock:HealthCheck:host=#{HOST}:pid=#{PID}:random=#{RND}", action, callback From e65549099c79dcc37f6287c66b8f99fefde3a23a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 8 Oct 2015 14:40:42 +0100 Subject: [PATCH 213/549] only delete the applied ops from redis --- .../app/coffee/RedisManager.coffee | 24 ++++---- .../app/coffee/UpdatesManager.coffee | 30 +++++----- .../RedisManager/RedisManagerTests.coffee | 56 ++++++++++++------- .../UpdatesManager/UpdatesManagerTests.coffee | 22 +++++--- 4 files changed, 78 insertions(+), 54 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index eec6ce5086..6cc0f206be 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -6,20 +6,24 @@ rawUpdatesKey = (doc_id) -> "UncompressedHistoryOps:#{doc_id}" docsWithHistoryOpsKey = (project_id) -> "DocsWithHistoryOps:#{project_id}" module.exports = RedisManager = - getOldestRawUpdates: (doc_id, batchSize, callback = (error, rawUpdates) ->) -> - key = rawUpdatesKey(doc_id) - rclient.lrange key, 0, batchSize - 1, (error, jsonUpdates) -> - try - rawUpdates = ( JSON.parse(update) for update in jsonUpdates or [] ) - catch e - return callback(e) - callback null, rawUpdates - deleteOldestRawUpdates: (project_id, doc_id, batchSize, callback = (error) ->) -> + getOldestDocUpdates: (doc_id, batchSize, callback = (error, jsonUpdates) ->) -> + key = rawUpdatesKey(doc_id) + rclient.lrange key, 0, batchSize - 1, callback + + expandDocUpdates: (jsonUpdates, callback = (error, rawUpdates) ->) -> + try + rawUpdates = ( JSON.parse(update) for update in jsonUpdates or [] ) + catch e + return callback(e) + callback null, rawUpdates + + deleteAppliedDocUpdates: (project_id, doc_id, docUpdates, callback = (error) ->) -> # It's ok to delete the doc_id from the set here. Even though the list # of updates may not be empty, we will continue to process it until it is. multi = rclient.multi() - multi.ltrim rawUpdatesKey(doc_id), batchSize, -1 + for update in docUpdates or [] + multi.lrem rawUpdatesKey(doc_id), 0, update multi.srem docsWithHistoryOpsKey(project_id), doc_id multi.exec (error, results) -> return callback(error) if error? diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 4703ff6a1d..b46e4ccffa 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -59,23 +59,25 @@ module.exports = UpdatesManager = return callback(error) if error? MongoManager.backportProjectId project_id, doc_id, (error) -> return callback(error) if error? - RedisManager.getOldestRawUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, rawUpdates) -> + RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> return callback(error) if error? - length = rawUpdates.length - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> + length = docUpdates.length + RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - RedisManager.deleteOldestRawUpdates project_id, doc_id, length, (error) -> + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> + return callback(error) if error? + if length == UpdatesManager.REDIS_READ_BATCH_SIZE + # There might be more updates + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" + setTimeout () -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, callback + , 0 + else + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" + callback() processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> LockManager.runWithLock( diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee index 7293892be0..12f78fd4aa 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -20,40 +20,54 @@ describe "RedisManager", -> @batchSize = 100 @callback = sinon.stub() - describe "getOldestRawUpdates", -> + describe "getOldestDocUpdates", -> beforeEach -> @rawUpdates = [ {v: 42, op: "mock-op-42"}, { v: 45, op: "mock-op-45" }] @jsonUpdates = (JSON.stringify(update) for update in @rawUpdates) @rclient.lrange = sinon.stub().callsArgWith(3, null, @jsonUpdates) - @RedisManager.getOldestRawUpdates @doc_id, @batchSize, @callback + @RedisManager.getOldestDocUpdates @doc_id, @batchSize, @callback it "should read the updates from redis", -> @rclient.lrange .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @batchSize - 1) .should.equal true - it "should call the callback with the parsed ops", -> - @callback.calledWith(null, @rawUpdates).should.equal true + it "should call the callback with the unparsed ops", -> + @callback.calledWith(null, @jsonUpdates).should.equal true - describe "deleteOldestRawUpdates", -> - beforeEach -> - @rclient.ltrim = sinon.stub() - @rclient.srem = sinon.stub() - @rclient.exec = sinon.stub().callsArgWith(0) - @RedisManager.deleteOldestRawUpdates @project_id, @doc_id, @batchSize, @callback - it "should delete the updates from redis", -> - @rclient.ltrim - .calledWith("UncompressedHistoryOps:#{@doc_id}", @batchSize, -1) - .should.equal true + describe "expandDocUpdates", -> + beforeEach -> + @RedisManager.expandDocUpdates @jsonUpdates, @callback - it "should delete the doc from the set of docs with history ops", -> - @rclient.srem - .calledWith("DocsWithHistoryOps:#{@project_id}", @doc_id) - .should.equal true + it "should call the callback with the parsed ops", -> + @callback.calledWith(null, @rawUpdates).should.equal true - it "should call the callback ", -> - @callback.called.should.equal true + + describe "deleteAppliedDocUpdates", -> + beforeEach -> + @rclient.lrem = sinon.stub() + @rclient.srem = sinon.stub() + @rclient.exec = sinon.stub().callsArgWith(0) + @RedisManager.deleteAppliedDocUpdates @project_id, @doc_id, @jsonUpdates, @callback + + it "should delete the first update from redis", -> + @rclient.lrem + .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @jsonUpdates[0]) + .should.equal true + + it "should delete the second update from redis", -> + @rclient.lrem + .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @jsonUpdates[1]) + .should.equal true + + it "should delete the doc from the set of docs with history ops", -> + @rclient.srem + .calledWith("DocsWithHistoryOps:#{@project_id}", @doc_id) + .should.equal true + + it "should call the callback ", -> + @callback.called.should.equal true describe "getDocIdsWithHistoryOps", -> beforeEach -> @@ -67,4 +81,4 @@ describe "RedisManager", -> .should.equal true it "should call the callback with the doc_ids", -> - @callback.calledWith(null, @doc_ids).should.equal true \ No newline at end of file + @callback.calledWith(null, @doc_ids).should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 04a2111893..f1bfc13288 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -158,14 +158,15 @@ describe "UpdatesManager", -> describe "processUncompressedUpdates", -> beforeEach -> @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(4) - @RedisManager.deleteOldestRawUpdates = sinon.stub().callsArg(3) + @RedisManager.deleteAppliedDocUpdates = sinon.stub().callsArg(3) @MongoManager.backportProjectId = sinon.stub().callsArg(2) @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") describe "when there is fewer than one batch to send", -> beforeEach -> @updates = ["mock-update"] - @RedisManager.getOldestRawUpdates = sinon.stub().callsArgWith(2, null, @updates) + @RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, @updates) + @RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, @updates) @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback it "should check if the updates are temporary", -> @@ -179,7 +180,7 @@ describe "UpdatesManager", -> .should.equal true it "should get the oldest updates", -> - @RedisManager.getOldestRawUpdates + @RedisManager.getOldestDocUpdates .calledWith(@doc_id, @UpdatesManager.REDIS_READ_BATCH_SIZE) .should.equal true @@ -189,8 +190,8 @@ describe "UpdatesManager", -> .should.equal true it "should delete the batch of uncompressed updates that was just processed", -> - @RedisManager.deleteOldestRawUpdates - .calledWith(@project_id, @doc_id, @updates.length) + @RedisManager.deleteAppliedDocUpdates + .calledWith(@project_id, @doc_id, @updates) .should.equal true it "should call the callback", -> @@ -201,17 +202,20 @@ describe "UpdatesManager", -> @UpdatesManager.REDIS_READ_BATCH_SIZE = 2 @updates = ["mock-update-0", "mock-update-1", "mock-update-2", "mock-update-3", "mock-update-4"] @redisArray = @updates.slice() - @RedisManager.getOldestRawUpdates = (doc_id, batchSize, callback = (error, updates) ->) => + @RedisManager.getOldestDocUpdates = (doc_id, batchSize, callback = (error, updates) ->) => updates = @redisArray.slice(0, batchSize) @redisArray = @redisArray.slice(batchSize) callback null, updates - sinon.spy @RedisManager, "getOldestRawUpdates" + sinon.spy @RedisManager, "getOldestDocUpdates" + @RedisManager.expandDocUpdates = (jsonUpdates, callback) => + callback null, jsonUpdates + sinon.spy @RedisManager, "expandDocUpdates" @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => @callback(args...) done() it "should get the oldest updates in three batches ", -> - @RedisManager.getOldestRawUpdates.callCount.should.equal 3 + @RedisManager.getOldestDocUpdates.callCount.should.equal 3 it "should compress and save the updates in batches", -> @UpdatesManager.compressAndSaveRawUpdates @@ -225,7 +229,7 @@ describe "UpdatesManager", -> .should.equal true it "should delete the batches of uncompressed updates", -> - @RedisManager.deleteOldestRawUpdates.callCount.should.equal 3 + @RedisManager.deleteAppliedDocUpdates.callCount.should.equal 3 it "should call the callback", -> @callback.called.should.equal true From 3432d9e91ad7768161bc7d586eb326b513150b05 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 26 Nov 2015 15:16:54 +0000 Subject: [PATCH 214/549] added comments for redis delete --- services/track-changes/app/coffee/RedisManager.coffee | 5 +++-- services/track-changes/app/coffee/UpdatesManager.coffee | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 6cc0f206be..a634bbfed9 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -19,11 +19,12 @@ module.exports = RedisManager = callback null, rawUpdates deleteAppliedDocUpdates: (project_id, doc_id, docUpdates, callback = (error) ->) -> - # It's ok to delete the doc_id from the set here. Even though the list - # of updates may not be empty, we will continue to process it until it is. multi = rclient.multi() + # Delete all the updates which have been applied (exact match) for update in docUpdates or [] multi.lrem rawUpdatesKey(doc_id), 0, update + # It's ok to delete the doc_id from the set here. Even though the list + # of updates may not be empty, we will continue to process it until it is. multi.srem docsWithHistoryOpsKey(project_id), doc_id multi.exec (error, results) -> return callback(error) if error? diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index b46e4ccffa..e2b190c804 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -59,14 +59,17 @@ module.exports = UpdatesManager = return callback(error) if error? MongoManager.backportProjectId project_id, doc_id, (error) -> return callback(error) if error? + # get the updates as strings from redis (so we can delete them after they are applied) RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> return callback(error) if error? length = docUpdates.length + # parse the redis strings into ShareJs updates RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> return callback(error) if error? UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + # delete the applied updates from redis RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> return callback(error) if error? if length == UpdatesManager.REDIS_READ_BATCH_SIZE From 8ebc069ddbcea694724524085b0068637870d10e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 8 Oct 2015 16:10:48 +0100 Subject: [PATCH 215/549] modify last compressed op in place --- .../app/coffee/MongoManager.coffee | 24 ++++++--- .../app/coffee/UpdatesManager.coffee | 41 ++++++++++----- .../MongoManager/MongoManagerTests.coffee | 36 ++----------- .../UpdatesManager/UpdatesManagerTests.coffee | 50 +++++++++++-------- 4 files changed, 78 insertions(+), 73 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 9b5c63c85f..2605ca17e6 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -16,10 +16,7 @@ module.exports = MongoManager = return callback error, null return callback null, compressedUpdates[0] or null - deleteCompressedUpdate: (id, callback = (error) ->) -> - db.docHistory.remove({ _id: ObjectId(id.toString()) }, callback) - - popLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) -> + peekLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) -> # under normal use we pass back the last update as # callback(null,update). # @@ -37,9 +34,7 @@ module.exports = MongoManager = # the update is marked as broken so we will force a new op return callback null, null else - MongoManager.deleteCompressedUpdate update._id, (error) -> - return callback(error) if error? - callback null, update + return callback null, update else callback null, null @@ -60,6 +55,21 @@ module.exports = MongoManager = callback(err,results) + modifyCompressedUpdate: (lastUpdate, newUpdate, callback = (error) ->) -> + return callback() if not newUpdate? + db.docHistory.findAndModify + query: lastUpdate, + update: + $set : + op: newUpdate.op + meta: newUpdate.meta + v: newUpdate.v + new: true + , (err, result, lastErrorObject) -> + return callback(error) if error? + return new Error("could not modify existing op") if not result? + callback(err, result) + insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> update = { doc_id: ObjectId(doc_id.toString()) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 4703ff6a1d..61a0c7ce9d 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -14,10 +14,10 @@ module.exports = UpdatesManager = if length == 0 return callback() - MongoManager.popLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> + MongoManager.peekLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> # lastCompressedUpdate is the most recent update in Mongo. # - # The popLastCompressedUpdate method may pass it back as 'null' + # The peekLastCompressedUpdate method may pass it back as 'null' # to force the start of a new compressed update, even when there # was a previous compressed update in Mongo. In this case it # passes back the lastVersion from the update to check @@ -37,21 +37,36 @@ module.exports = UpdatesManager = if rawUpdates[0]? and rawUpdates[0].v != lastVersion + 1 error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion}") logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" - # Push the update back into Mongo - if it was present - catching errors at this - # point is useless, we're already bailing - if lastCompressedUpdate? - MongoManager.insertCompressedUpdates project_id, doc_id, [lastCompressedUpdate], temporary, () -> - return callback error - else - callback error - return + return callback error compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - MongoManager.insertCompressedUpdates project_id, doc_id, compressedUpdates, temporary,(error) -> + if not lastCompressedUpdate? + # no existing update, insert everything + updateToModify = null + updatesToInsert = compressedUpdates + else + # there are existing updates, see what happens when we + # compress them together with the new ones + [firstUpdate, additionalUpdates...] = compressedUpdates + + if firstUpdate.v == lastCompressedUpdate.v + # first update version hasn't changed, skip it and insert remaining updates + # this is an optimisation, we could update the existing op with itself + updateToModify = null + updatesToInsert = additionalUpdates + else + # first update version did changed, modify it and insert remaining updates + updateToModify = firstUpdate + updatesToInsert = additionalUpdates + + MongoManager.modifyCompressedUpdate lastCompressedUpdate, updateToModify, (error, result) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" - callback() + logger.log {project_id, doc_id, orig_v: lastCompressedUpdate.v, new_v: result.v}, "applied update in-place" if result? + MongoManager.insertCompressedUpdates project_id, doc_id, updatesToInsert, temporary,(error) -> + return callback(error) if error? + logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" + callback() REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 0556e32413..656447bfa5 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -53,36 +53,18 @@ describe "MongoManager", -> it "should call the call back with the update", -> @callback.calledWith(null, @update).should.equal true - describe "deleteCompressedUpdate", -> - beforeEach -> - @update_id = ObjectId().toString() - @db.docHistory = - remove: sinon.stub().callsArg(1) - @MongoManager.deleteCompressedUpdate(@update_id, @callback) - it "should remove the update", -> - @db.docHistory.remove - .calledWith(_id: ObjectId(@update_id)) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "popLastCompressedUpdate", -> + describe "peekLastCompressedUpdate", -> describe "when there is no last update", -> beforeEach -> @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @MongoManager.deleteCompressedUpdate = sinon.stub() - @MongoManager.popLastCompressedUpdate @doc_id, @callback + @MongoManager.peekLastCompressedUpdate @doc_id, @callback it "should get the last update", -> @MongoManager.getLastCompressedUpdate .calledWith(@doc_id) .should.equal true - it "should not try to delete the last update", -> - @MongoManager.deleteCompressedUpdate.called.should.equal false - it "should call the callback with no update", -> @callback.calledWith(null, null).should.equal true @@ -90,19 +72,13 @@ describe "MongoManager", -> beforeEach -> @update = { _id: Object() } @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) - @MongoManager.deleteCompressedUpdate = sinon.stub().callsArgWith(1, null) - @MongoManager.popLastCompressedUpdate @doc_id, @callback + @MongoManager.peekLastCompressedUpdate @doc_id, @callback it "should get the last update", -> @MongoManager.getLastCompressedUpdate .calledWith(@doc_id) .should.equal true - it "should delete the last update", -> - @MongoManager.deleteCompressedUpdate - .calledWith(@update._id) - .should.equal true - it "should call the callback with the update", -> @callback.calledWith(null, @update).should.equal true @@ -110,17 +86,13 @@ describe "MongoManager", -> beforeEach -> @update = { _id: Object(), v: 12345, inS3: true } @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) - @MongoManager.deleteCompressedUpdate = sinon.stub() - @MongoManager.popLastCompressedUpdate @doc_id, @callback + @MongoManager.peekLastCompressedUpdate @doc_id, @callback it "should get the last update", -> @MongoManager.getLastCompressedUpdate .calledWith(@doc_id) .should.equal true - it "should not try to delete the last update", -> - @MongoManager.deleteCompressedUpdate.called.should.equal false - it "should call the callback with a null update and the correct version", -> @callback.calledWith(null, null, @update.v).should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 04a2111893..b4c44f3b94 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -24,12 +24,12 @@ describe "UpdatesManager", -> describe "compressAndSaveRawUpdates", -> describe "when there are no raw ops", -> beforeEach -> - @MongoManager.popLastCompressedUpdate = sinon.stub() + @MongoManager.peekLastCompressedUpdate = sinon.stub() @MongoManager.insertCompressedUpdates = sinon.stub() @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, [], @temporary, @callback it "should not need to access the database", -> - @MongoManager.popLastCompressedUpdate.called.should.equal false + @MongoManager.peekLastCompressedUpdate.called.should.equal false @MongoManager.insertCompressedUpdates.called.should.equal false it "should call the callback", -> @@ -38,15 +38,16 @@ describe "UpdatesManager", -> describe "when there is no compressed history to begin with", -> beforeEach -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @compressedUpdates = { v: 13, op: "compressed-op-12" } + @compressedUpdates = [ { v: 13, op: "compressed-op-12" } ] - @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) + @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) + @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback - it "should try to pop the last compressed op", -> - @MongoManager.popLastCompressedUpdate + it "should look at the last compressed op", -> + @MongoManager.peekLastCompressedUpdate .calledWith(@doc_id) .should.equal true @@ -66,9 +67,10 @@ describe "UpdatesManager", -> describe "when the raw ops need appending to existing history", -> beforeEach -> @lastCompressedUpdate = { v: 11, op: "compressed-op-11" } - @compressedUpdates = { v: 13, op: "compressed-op-12" } + @compressedUpdates = [ { v: 12, op: "compressed-op-11+12" }, { v: 13, op: "compressed-op-12" } ] - @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) + @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) + @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @@ -77,8 +79,8 @@ describe "UpdatesManager", -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback - it "should try to pop the last compressed op", -> - @MongoManager.popLastCompressedUpdate + it "should look at the last compressed op", -> + @MongoManager.peekLastCompressedUpdate .calledWith(@doc_id) .should.equal true @@ -86,10 +88,15 @@ describe "UpdatesManager", -> @UpdateCompressor.compressRawUpdates .calledWith(@lastCompressedUpdate, @rawUpdates) .should.equal true + + it "should update the existing op", -> + @MongoManager.modifyCompressedUpdate + .calledWith(@lastCompressedUpdate, @compressedUpdates[0]) + .should.equal true - it "should save the compressed ops", -> + it "should save the new compressed ops", -> @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @compressedUpdates, @temporary) + .calledWith(@project_id, @doc_id, @compressedUpdates[1..], @temporary) .should.equal true it "should call the callback", -> @@ -116,19 +123,20 @@ describe "UpdatesManager", -> .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) .should.equal true - it "should put the popped update back into mongo", -> - @MongoManager.insertCompressedUpdates.calledOnce.should.equal true - @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, [@lastCompressedUpdate], @temporary) - .should.equal true + it "should not modify any update in mongo", -> + @MongoManager.modifyCompressedUpdate.called.should.equal false + + it "should not insert any update into mongo", -> + @MongoManager.insertCompressedUpdates.called.should.equal false describe "when the raw ops need appending to existing history which is in S3", -> beforeEach -> @lastCompressedUpdate = null @lastVersion = 11 - @compressedUpdates = { v: 13, op: "compressed-op-12" } + @compressedUpdates = [ { v: 13, op: "compressed-op-12" } ] - @MongoManager.popLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, @lastVersion) + @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, @lastVersion) + @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @@ -137,8 +145,8 @@ describe "UpdatesManager", -> @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback - it "should try to pop the last compressed op", -> - @MongoManager.popLastCompressedUpdate + it "should try to look at the last compressed op", -> + @MongoManager.peekLastCompressedUpdate .calledWith(@doc_id) .should.equal true From 11be8c3733a2192fc074a557c9430709f339ee25 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 27 Nov 2015 14:13:46 +0000 Subject: [PATCH 216/549] increase timeout in lock manager test on heavily loaded machines 20ms isn't long enough to consistently get multiple calls to the lock manager --- .../test/unit/coffee/LockManager/LockManagerTests.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index 298665a00b..266b12b120 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -103,7 +103,7 @@ describe "LockManager", -> startTime = Date.now() @LockManager.LOCK_TEST_INTERVAL = 5 @LockManager.tryLock = (doc_id, callback = (error, isFree) ->) -> - if Date.now() - startTime < 20 + if Date.now() - startTime < 100 callback null, false else callback null, true From be2136de7c1e5a5676a9e402028c0a7847f2582f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 3 Dec 2015 15:47:55 +0000 Subject: [PATCH 217/549] fix update-in-place bug for array ops --- .../app/coffee/UpdateCompressor.coffee | 4 ++++ .../app/coffee/UpdatesManager.coffee | 3 ++- .../UpdateCompressorTests.coffee | 22 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 6d7527057f..95e3f58e86 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -56,6 +56,10 @@ module.exports = UpdateCompressor = return concattedUpdates compressRawUpdates: (lastPreviousUpdate, rawUpdates) -> + if lastPreviousUpdate?.op?.length > 1 + # if the last previous update was an array op, don't compress onto it. + # The avoids cases where array length changes but version number doesn't + return [lastPreviousUpdate].concat UpdateCompressor.compressRawUpdates(null,rawUpdates) if lastPreviousUpdate? rawUpdates = [lastPreviousUpdate].concat(rawUpdates) updates = UpdateCompressor.convertToSingleOpUpdates(rawUpdates) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 4edfdb0ad7..ffc2cef19a 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -7,6 +7,7 @@ UpdateTrimmer = require "./UpdateTrimmer" logger = require "logger-sharelatex" async = require "async" DocArchiveManager = require "./DocArchiveManager" +_ = require "underscore" module.exports = UpdatesManager = compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> @@ -50,7 +51,7 @@ module.exports = UpdatesManager = # compress them together with the new ones [firstUpdate, additionalUpdates...] = compressedUpdates - if firstUpdate.v == lastCompressedUpdate.v + if firstUpdate.v == lastCompressedUpdate.v and _.isEqual(firstUpdate, lastCompressedUpdate) # first update version hasn't changed, skip it and insert remaining updates # this is an optimisation, we could update the existing op with itself updateToModify = null diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index 3d14a84eb9..be3bec5350 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -304,3 +304,25 @@ describe "UpdateCompressor", -> meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id v: 43 }] + + describe "compressRawUpdates", -> + describe "merging in-place with an array op", -> + it "should not change the existing last updates", -> + expect(@UpdateCompressor.compressRawUpdates { + op: [ {"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, [{ + op: [{ p: 1006, i: "WORLD" }] + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: [{"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ] + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + },{ + op: [{"p":1006,"i":"WORLD"}] + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 + }] From 23c43b80420b7adcd655c5792fddd9e4e6df0133 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 4 Dec 2015 13:57:15 +0000 Subject: [PATCH 218/549] skip any broken ops when viewing history diffs --- .../track-changes/app/coffee/DiffGenerator.coffee | 13 ++++++++++--- .../track-changes/app/coffee/DiffManager.coffee | 9 ++++++--- .../unit/coffee/DiffManager/DiffManagerTests.coffee | 10 ++++++---- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index e0fcf744cd..e57033ab40 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -11,8 +11,15 @@ module.exports = DiffGenerator = ConsistencyError: ConsistencyError rewindUpdate: (content, update) -> - for op in update.op.slice().reverse() - content = DiffGenerator.rewindOp content, op + for op in update.op by -1 + try + content = DiffGenerator.rewindOp content, op + catch e + if e instanceof ConsistencyError + logger.error {update, op: JSON.stringify(op)}, "marking op as broken" + op.broken = true + else + throw e # rethrow the execption return content rewindOp: (content, op) -> @@ -90,7 +97,7 @@ module.exports = DiffGenerator = return newDiff applyUpdateToDiff: (diff, update) -> - for op in update.op + for op in update.op when op.broken isnt true diff = DiffGenerator.applyOpToDiff diff, op, update.meta return diff diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 29016fc42c..91d41a909f 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -45,9 +45,12 @@ module.exports = DiffManager = if lastUpdate? and lastUpdate.v != version - 1 return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") + tryUpdates = updates.slice().reverse() + try - startingContent = DiffGenerator.rewindUpdates content, updates.slice().reverse() + startingContent = DiffGenerator.rewindUpdates content, tryUpdates + # tryUpdates is reversed, and any unapplied ops are marked as broken catch e return callback(e) - - callback(null, startingContent, updates) \ No newline at end of file + + callback(null, startingContent, tryUpdates) diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index e3214a2f36..388520293e 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -108,7 +108,11 @@ describe "DiffManager", -> describe "with matching versions", -> beforeEach -> @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) - @DiffGenerator.rewindUpdates = sinon.stub().returns(@rewound_content) + @DiffGenerator.rewindUpdates = sinon.spy (content, updates) => + # the rewindUpdates method reverses the 'updates' array + updates.reverse() + return @rewound_content + @rewindUpdatesWithArgs = @DiffGenerator.rewindUpdates.withArgs(@content, @updates.slice().reverse()) @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback it "should get the latest doc and version with all recent updates", -> @@ -117,9 +121,7 @@ describe "DiffManager", -> .should.equal true it "should rewind the diff", -> - @DiffGenerator.rewindUpdates - .calledWith(@content, @updates.slice().reverse()) - .should.equal true + sinon.assert.calledOnce(@rewindUpdatesWithArgs) it "should call the callback with the rewound document and updates", -> @callback.calledWith(null, @rewound_content, @updates).should.equal true From e7af602d329c0f7bc78bd35c14b224bb90b55070 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 7 Dec 2015 15:52:21 +0000 Subject: [PATCH 219/549] add sentry logging --- services/track-changes/app.coffee | 3 +++ services/track-changes/package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index a85c9de290..2e2928e85f 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -2,6 +2,9 @@ Settings = require "settings-sharelatex" logger = require "logger-sharelatex" logger.initialize("track-changes") +if Settings.sentry?.dsn? + logger.initializeErrorReporting(Settings.sentry.dsn, {release: process.env.BUILD_NUMBER}) + Path = require "path" Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 7c14dd8f28..4eb749b6d7 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -13,7 +13,7 @@ "line-reader": "^0.2.4", "mongojs": "^0.18.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.1.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", From 6483233f48fb00ba51d61a2f49dfd70daa055700 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 7 Dec 2015 16:17:17 +0000 Subject: [PATCH 220/549] added /oops endpoint for generating dummy exception --- services/track-changes/app.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 2e2928e85f..bda23d8a51 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -50,6 +50,9 @@ app.post "/pack", (req, res, next) -> app.get "/status", (req, res, next) -> res.send "track-changes is alive" +app.get "/oops", (req, res, next) -> + throw new Error("dummy test error") + app.get "/check_lock", HttpController.checkLock app.get "/health_check", HttpController.healthCheck From c51d249221d419046221eedcb9324753bf4db906 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 8 Dec 2015 10:44:26 +0000 Subject: [PATCH 221/549] remove explicit sentry release number use environment variable SENTRY_RELEASE instead --- services/track-changes/app.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index bda23d8a51..7d186ffe85 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -3,7 +3,7 @@ logger = require "logger-sharelatex" logger.initialize("track-changes") if Settings.sentry?.dsn? - logger.initializeErrorReporting(Settings.sentry.dsn, {release: process.env.BUILD_NUMBER}) + logger.initializeErrorReporting(Settings.sentry.dsn) Path = require "path" Metrics = require "metrics-sharelatex" From 2a7c33d7ca767ba0d0f746a2e823cf4e83b42643 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Dec 2015 14:57:04 +0000 Subject: [PATCH 222/549] added /check endpoint for documents --- services/track-changes/app.coffee | 2 ++ .../track-changes/app/coffee/DiffGenerator.coffee | 6 ++++-- .../app/coffee/HttpController.coffee | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 7d186ffe85..93d6b7fcd8 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -22,6 +22,8 @@ app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff +app.get "/project/:project_id/doc/:doc_id/check", HttpController.checkDoc + app.get "/project/:project_id/updates", HttpController.getUpdates app.post "/project/:project_id/flush", HttpController.flushProject diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index e57033ab40..6e3b36643b 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -11,11 +11,13 @@ module.exports = DiffGenerator = ConsistencyError: ConsistencyError rewindUpdate: (content, update) -> - for op in update.op by -1 + for op, i in update.op by -1 try content = DiffGenerator.rewindOp content, op catch e - if e instanceof ConsistencyError + if e instanceof ConsistencyError and i = update.op.length - 1 + # catch known case where the last op in an array has been + # merged into a later op logger.error {update, op: JSON.stringify(op)}, "marking op as broken" op.broken = true else diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index ecc62e1e3d..1a90c16fd4 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -30,6 +30,21 @@ module.exports = HttpController = return next(error) if error? res.send 204 + checkDoc: (req, res, next = (error) ->) -> + doc_id = req.params.doc_id + project_id = req.params.project_id + logger.log project_id: project_id, doc_id: doc_id, "checking doc history" + DiffManager.getDocumentBeforeVersion project_id, doc_id, 1, (error, document, rewoundUpdates) -> + return next(error) if error? + broken = [] + for update in rewoundUpdates + for op in update.op when op.broken is true + broken.push op + if broken.length > 0 + res.send broken + else + res.send 204 + getDiff: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id From 54d1036e371e513e1a527fa3fb73560daace0b69 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Dec 2015 15:13:37 +0000 Subject: [PATCH 223/549] skip ops marked as broken in database --- services/track-changes/app/coffee/DiffGenerator.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index 6e3b36643b..53f7c2d343 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -11,7 +11,7 @@ module.exports = DiffGenerator = ConsistencyError: ConsistencyError rewindUpdate: (content, update) -> - for op, i in update.op by -1 + for op, i in update.op by -1 when op.broken isnt true try content = DiffGenerator.rewindOp content, op catch e From b84a9e6e915c459bd45c124b0debc7d984eea818 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 17 Dec 2015 14:11:44 +0000 Subject: [PATCH 224/549] upgrade mongojs --- services/track-changes/app/coffee/PackManager.coffee | 3 +-- services/track-changes/app/coffee/PackWorker.coffee | 3 +-- services/track-changes/app/coffee/mongojs.coffee | 5 +++-- services/track-changes/package.json | 5 +++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 3311dbbb5a..14b1522d86 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -1,7 +1,6 @@ async = require "async" _ = require "underscore" -{db, ObjectId} = require "./mongojs" -BSON=db.bson.BSON +{db, ObjectId, BSON} = require "./mongojs" logger = require "logger-sharelatex" LockManager = require "./LockManager" diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 5c23613e57..040fd56d7b 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -1,7 +1,6 @@ async = require "async" _ = require "underscore" -{db, ObjectId} = require "./mongojs" -BSON=db.bson.BSON +{db, ObjectId, BSON} = require "./mongojs" logger = require "logger-sharelatex" logger.initialize("track-changes-packworker") LockManager = require "./LockManager" diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index 32efbc9a1d..33c21639c5 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,8 +1,9 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" -db = mongojs.connect(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats"]) +bson = require "bson" +db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats"]) module.exports = db: db ObjectId: mongojs.ObjectId - + BSON: bson diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 4eb749b6d7..fa4ea9501c 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -8,13 +8,14 @@ }, "dependencies": { "async": "~0.2.10", + "bson": "^0.4.20", "cli": "^0.6.6", "express": "3.3.5", "line-reader": "^0.2.4", - "mongojs": "^0.18.1", + "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.1.0", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.0.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", "redis": "~0.10.1", From 4a82dfe61864e11da4a4a6f6621adbe93bc106cd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 17 Dec 2015 16:28:02 +0000 Subject: [PATCH 225/549] add setting trackchanges.continueOnError to allow recovery from missing ops --- services/track-changes/app/coffee/UpdatesManager.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index ffc2cef19a..c38fb770c4 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -8,6 +8,7 @@ logger = require "logger-sharelatex" async = require "async" DocArchiveManager = require "./DocArchiveManager" _ = require "underscore" +Settings = require "settings-sharelatex" module.exports = UpdatesManager = compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> @@ -38,7 +39,10 @@ module.exports = UpdatesManager = if rawUpdates[0]? and rawUpdates[0].v != lastVersion + 1 error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion}") logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" - return callback error + if Settings.trackchanges?.continueOnError and rawUpdates[0].v > lastVersion + 1 + # we have lost some ops - continue to write into the database, we can't recover at this point + else + return callback error compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates From 911883a566fb98a05945393655f518e87a78bf8d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 18 Dec 2015 10:38:12 +0000 Subject: [PATCH 226/549] upgrade logger-sharelatex --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index fa4ea9501c..d57f598a3d 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -14,7 +14,7 @@ "line-reader": "^0.2.4", "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.1.0", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.2.1", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", From 4a6374efe8d56b5cd85ca32518f6a1433a14326b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 18 Dec 2015 12:38:42 +0000 Subject: [PATCH 227/549] fix read order when retrieving diffs --- services/track-changes/app/coffee/DiffManager.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 91d41a909f..bb110b1824 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -5,9 +5,11 @@ logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> - UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> + # retrieve the document before retreiving the updates, + # because updates are written to mongo after the document + DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? - DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> + UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? callback(null, content, version, updates) @@ -41,6 +43,10 @@ module.exports = DiffManager = for u in updates when u.broken return callback new Error "broken-history" + # discard any updates which are ahead of this document version + while updates[0]?.v >= version + updates.shift() + lastUpdate = updates[0] if lastUpdate? and lastUpdate.v != version - 1 return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") From b7de6f2f71650132168f3a7426e5273fb40212dc Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 21 Dec 2015 13:52:26 +0000 Subject: [PATCH 228/549] don't try to compress updates across point of broken history --- services/track-changes/app/coffee/UpdatesManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index c38fb770c4..d2b8683fdf 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -41,6 +41,7 @@ module.exports = UpdatesManager = logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" if Settings.trackchanges?.continueOnError and rawUpdates[0].v > lastVersion + 1 # we have lost some ops - continue to write into the database, we can't recover at this point + lastCompressedUpdate = null else return callback error From d49997d9f36a0fadcb180dd116b691e533f3ca93 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 21 Dec 2015 16:56:49 +0000 Subject: [PATCH 229/549] fix usage of BSON module --- services/track-changes/app/coffee/mongojs.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index 33c21639c5..be29816b82 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -5,5 +5,5 @@ db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHi module.exports = db: db ObjectId: mongojs.ObjectId - BSON: bson + BSON: new bson.BSONPure() From c7b40624121ed70a143e2c062d587df2e5a398e7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 22 Dec 2015 14:03:35 +0000 Subject: [PATCH 230/549] remove unsupported options argument in count() method of mongojs 1.x --- services/track-changes/app/coffee/MongoManager.coffee | 4 ++-- .../test/unit/coffee/MongoManager/MongoManagerTests.coffee | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 2605ca17e6..bee0a5ac67 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -153,10 +153,10 @@ module.exports = MongoManager = db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } getDocChangesCount: (doc_id, callback)-> - db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, {}, callback + db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, callback getArchivedDocChanges: (doc_id, callback)-> - db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: { $exists: true }}, {}, callback + db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: { $exists: true }}, callback markDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 656447bfa5..2cdc7c8680 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -395,7 +395,7 @@ describe "MongoManager", -> describe "getDocChangesCount", -> beforeEach -> @db.docHistory = - count: sinon.stub().callsArg(2) + count: sinon.stub().callsArg(1) @MongoManager.getDocChangesCount @doc_id, @callback it "should return if there is any doc changes", -> @@ -403,7 +403,6 @@ describe "MongoManager", -> .calledWith({ doc_id: ObjectId(@doc_id) inS3 : { $exists : false } - }, { }) .should.equal true @@ -413,7 +412,7 @@ describe "MongoManager", -> describe "getArchivedDocChanges", -> beforeEach -> @db.docHistory = - count: sinon.stub().callsArg(2) + count: sinon.stub().callsArg(1) @MongoManager.getArchivedDocChanges @doc_id, @callback it "should return if there is any archived doc changes", -> @@ -421,7 +420,6 @@ describe "MongoManager", -> .calledWith({ doc_id: ObjectId(@doc_id) inS3: {$exists: true} - }, { }) .should.equal true From d3583b4ef62de367f2463e144dfa7c630d14d600 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 22 Dec 2015 14:38:04 +0000 Subject: [PATCH 231/549] respect limit of 1000 ops in bulk operation with mongojs 1.x --- services/track-changes/app/coffee/PackManager.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 14b1522d86..082c1d8a9a 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -331,8 +331,8 @@ module.exports = PackManager = expect_nRemoved = packObj.pack.length logger.log {doc_id: doc_id}, "adding pack, removing #{expect_nRemoved} ops" bulk.insert packObj - packObj.pack.forEach (op) -> - bulk.find({_id:op._id}).removeOne() + ids = (op._id for op in packObj.pack) + bulk.find({_id:{$in:ids}}).remove() bulk.execute (err, result) -> if err? logger.error {doc_id: doc_id}, "error adding pack" From bb7153c6c139cb57c1a5a6829dcea14ac8329d45 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 22 Dec 2015 15:36:15 +0000 Subject: [PATCH 232/549] workaround for mongojs db.close issue https://github.com/mafintosh/mongojs/issues/224 --- services/track-changes/app/coffee/PackWorker.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 040fd56d7b..34af7014d9 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -27,6 +27,14 @@ setTimeout () -> logger.log "checking for updates, limit=#{LIMIT}, delay=#{DOCUMENT_PACK_DELAY}, timeout=#{TIMEOUT}" +# work around for https://github.com/mafintosh/mongojs/issues/224 +db.close = (callback) -> + this._getServer (err, server) -> + return callback(err) if err? + server = if server.destroy? then server else server.topology + server.destroy(true, true) + callback() + finish = () -> logger.log 'closing db' db.close () -> From e1aa4362862a6b866d33bf54e6d1115cfe82262b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 5 Jan 2016 11:13:13 +0000 Subject: [PATCH 233/549] respect mongo bulk operations limit of 1000 operations --- services/track-changes/app/coffee/MongoAWS.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 36dd2715d7..81261fc18d 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -9,7 +9,7 @@ ReadlineStream = require "readline-stream" module.exports = MongoAWS = MAX_SIZE: 1024*1024 # almost max size - MAX_COUNT: 1024 # almost max count + MAX_COUNT: 512 # almost max count archiveDocHistory: (project_id, doc_id, update, _callback = (error) ->) -> From 6754bdca1cb8a789d7229e563ffed531fb97f48f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 5 Jan 2016 11:30:24 +0000 Subject: [PATCH 234/549] log timestamp in human-readable form for inconsistent ops --- services/track-changes/app/coffee/UpdatesManager.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index d2b8683fdf..c52d920cb7 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -37,8 +37,10 @@ module.exports = UpdatesManager = rawUpdates.shift() if rawUpdates[0]? and rawUpdates[0].v != lastVersion + 1 - error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion}") - logger.error err: error, doc_id: doc_id, project_id: project_id, "inconsistent doc versions" + ts = lastCompressedUpdate?.meta?.end_ts + last_timestamp = if ts? then new Date(ts) else 'unknown time' + error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion} from #{last_timestamp}") + logger.error err: error, doc_id: doc_id, project_id: project_id, prev_end_ts: ts, "inconsistent doc versions" if Settings.trackchanges?.continueOnError and rawUpdates[0].v > lastVersion + 1 # we have lost some ops - continue to write into the database, we can't recover at this point lastCompressedUpdate = null From bad923e073eefef625be129c54bdaa700b94beda Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 5 Jan 2016 12:10:57 +0000 Subject: [PATCH 235/549] fix acceptance tests for mongojs 1.x driver --- .../test/acceptance/coffee/ArchivingUpdatesTests.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 0b77f95b30..ea8caa3226 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -62,8 +62,8 @@ describe "Archiving updates", -> after (done) -> MockWebApi.getUserInfo.restore() - db.docHistory.remove {project_id: ObjectId(@project_id)} - TrackChangesClient.removeS3Doc @project_id, @doc_id, done + db.docHistory.remove {project_id: ObjectId(@project_id)}, () -> + TrackChangesClient.removeS3Doc @project_id, @doc_id, done describe "archiving a doc's updates", -> before (done) -> From ee8931bc340b0b74348a8f6eb6e771388d1fb217 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 5 Jan 2016 15:41:06 +0000 Subject: [PATCH 236/549] log the request to sentry when an error occurs --- services/track-changes/app.coffee | 2 +- services/track-changes/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 93d6b7fcd8..f91f4f56a4 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -60,7 +60,7 @@ app.get "/check_lock", HttpController.checkLock app.get "/health_check", HttpController.healthCheck app.use (error, req, res, next) -> - logger.error err: error, "an internal error occured" + logger.error err: error, req: req, "an internal error occured" res.send 500 port = Settings.internal?.trackchanges?.port or 3015 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index d57f598a3d..37699bbb4a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -14,7 +14,7 @@ "line-reader": "^0.2.4", "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.2.1", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", From 05163837cbaa87ca07c4819301f127987309f4aa Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 5 Jan 2016 16:00:52 +0000 Subject: [PATCH 237/549] add sentry error reporting to PackWorker --- services/track-changes/app/coffee/PackWorker.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 34af7014d9..45a624faee 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -1,8 +1,12 @@ +Settings = require "settings-sharelatex" async = require "async" _ = require "underscore" {db, ObjectId, BSON} = require "./mongojs" logger = require "logger-sharelatex" logger.initialize("track-changes-packworker") +if Settings.sentry?.dsn? + logger.initializeErrorReporting(Settings.sentry.dsn) + LockManager = require "./LockManager" PackManager = require "./PackManager" From ffe30962c9ab33f77eabfc2694bfa54066f9852a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Jan 2016 09:34:39 +0000 Subject: [PATCH 238/549] add a close() method to LockManager to allow clean shutdown --- services/track-changes/app/coffee/LockManager.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index e2123cbfdf..9ed089ed47 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -70,3 +70,7 @@ module.exports = LockManager = action = (releaseLock) -> releaseLock() LockManager.runWithLock "HistoryLock:HealthCheck:host=#{HOST}:pid=#{PID}:random=#{RND}", action, callback + + close: (callback) -> + rclient.quit() + rclient.once 'end', callback From cb109a27a68acf0ddf5b17a792213f732d719abd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Jan 2016 09:43:10 +0000 Subject: [PATCH 239/549] allow PackWorker to shut down cleanly --- .../track-changes/app/coffee/PackWorker.coffee | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 45a624faee..7b37969229 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -18,7 +18,7 @@ DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 TIMEOUT = Number(process.argv[4]) || 30*60*1000 shutDownRequested = false -setTimeout () -> +shutDownTimer = setTimeout () -> logger.log "pack timed out, requesting shutdown" # start the shutdown on the next pack shutDownRequested = true @@ -40,10 +40,22 @@ db.close = (callback) -> callback() finish = () -> + if shutDownTimer? + logger.log 'cancelling timeout' + clearTimeout shutDownTimer logger.log 'closing db' db.close () -> - logger.log 'exiting from pack worker' - process.exit() + logger.log 'closing LockManager Redis Connection' + LockManager.close () -> + logger.log 'ready to exit from pack worker' + hardTimeout = setTimeout () -> + logger.error 'hard exit from pack worker' + process.exit(1) + , 5*1000 + hardTimeout.unref() + +process.on 'exit', (code) -> + logger.log {code}, 'pack worker exited' processUpdates = (pending) -> async.eachSeries pending, (doc_id, callback) -> From b8862ca5af1cc3ea9331636ac26146ce9d58a4ed Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 11 Jan 2016 16:50:22 +0000 Subject: [PATCH 240/549] switch to node-byline module to avoid buffering problem with readline-stream for lines > 64k the readline-stream module is affected by https://github.com/jahewson/node-byline/issues/30 which is fixed in node-byline (readline-stream was an earlier fork of the byline module) --- services/track-changes/app/coffee/MongoAWS.coffee | 2 +- services/track-changes/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 81261fc18d..15053f9766 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -4,7 +4,7 @@ AWS = require 'aws-sdk' S3S = require 's3-streams' {db, ObjectId} = require "./mongojs" JSONStream = require "JSONStream" -ReadlineStream = require "readline-stream" +ReadlineStream = require "byline" module.exports = MongoAWS = diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 37699bbb4a..581e97c79d 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -9,6 +9,7 @@ "dependencies": { "async": "~0.2.10", "bson": "^0.4.20", + "byline": "^4.2.1", "cli": "^0.6.6", "express": "3.3.5", "line-reader": "^0.2.4", @@ -22,8 +23,7 @@ "underscore": "~1.7.0", "mongo-uri": "^0.1.2", "s3-streams": "^0.3.0", - "JSONStream": "^1.0.4", - "readline-stream": "^1.0.1" + "JSONStream": "^1.0.4" }, "devDependencies": { "chai": "~1.9.0", From ca1f1dc94483ebe903a3aec22762ff9006bcd23f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 12 Jan 2016 09:26:29 +0000 Subject: [PATCH 241/549] handle exception in parsing retrieved json from aws --- services/track-changes/app/coffee/MongoAWS.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 15053f9766..e4cc3197ef 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -74,8 +74,11 @@ module.exports = MongoAWS = inputStream.on 'data', (line) -> if line.length > 2 - ops.push(JSON.parse(line)) - sz += line.length + try + ops.push(JSON.parse(line)) + catch err + return callback(err) + sz += line.length if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE inputStream.pause() MongoAWS.handleBulk ops.slice(0), sz, () -> From 6199532d08ced47e702b462eda26bc49089d5300 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 12 Jan 2016 10:36:00 +0000 Subject: [PATCH 242/549] increase logging on s3 operations --- .../track-changes/app/coffee/DocArchiveManager.coffee | 1 + services/track-changes/app/coffee/MongoAWS.coffee | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index b569f3e37b..c639a84c99 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -78,6 +78,7 @@ module.exports = DocArchiveManager = MongoManager.getArchivedDocChanges doc_id, (error, count) -> return callback(error) if error? if count == 0 + logger.log {project_id, doc_id}, "no changes marked as in s3, not unarchiving" return callback() else MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index e4cc3197ef..31ec778d71 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -27,7 +27,9 @@ module.exports = MongoAWS = accessKeyId: settings.filestore.s3.key secretAccessKey: settings.filestore.s3.secret } - + + logger.log {project_id, doc_id}, "uploading data to s3" + upload = S3S.WriteStream new AWS.S3(), { "Bucket": settings.filestore.stores.user_files, "Key": project_id+"/changes-"+doc_id @@ -53,7 +55,9 @@ module.exports = MongoAWS = accessKeyId: settings.filestore.s3.key secretAccessKey: settings.filestore.s3.secret } - + + logger.log {project_id, doc_id}, "downloading data from s3" + download = S3S.ReadStream new AWS.S3(), { "Bucket": settings.filestore.stores.user_files, "Key": project_id+"/changes-"+doc_id From 8e53d660798b39fe1709655227cb89c656c9314c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 12 Jan 2016 10:36:14 +0000 Subject: [PATCH 243/549] log the key for lock timeouts --- services/track-changes/app/coffee/LockManager.coffee | 4 +++- .../test/unit/coffee/LockManager/LockManagerTests.coffee | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 9ed089ed47..89a194e5de 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -36,7 +36,9 @@ module.exports = LockManager = startTime = Date.now() do attempt = () -> if Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME - return callback(new Error("Timeout")) + e = new Error("Timeout") + e.key = key + return callback(e) LockManager.tryLock key, (error, gotLock, lockValue) -> return callback(error) if error? diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index 266b12b120..d6744137ed 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -129,7 +129,7 @@ describe "LockManager", -> done() it "should return the callback with an error", -> - @callback.calledWith(new Error("timeout")).should.equal true + @callback.calledWith(sinon.match.instanceOf(Error)).should.equal true describe "runWithLock", -> describe "with successful run", -> From 933b6c50e7059e55d0c3a4ccced053c54cd4cddb Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 14 Jan 2016 16:24:13 +0000 Subject: [PATCH 244/549] add memory logger to track changes --- services/track-changes/app.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index f91f4f56a4..63c2949d25 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -9,6 +9,7 @@ Path = require "path" Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) +Metrics.memory.monitor(logger) child_process = require "child_process" From 5153ed82171ce62f85502518418207ff10d321e5 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 15 Jan 2016 15:02:09 +0000 Subject: [PATCH 245/549] make peekLastUpdate alway return lastVersion when available --- .../app/coffee/MongoManager.coffee | 20 +++++++++++-------- .../app/coffee/UpdatesManager.coffee | 16 ++++++--------- .../MongoManager/MongoManagerTests.coffee | 8 ++++++-- .../UpdatesManager/UpdatesManagerTests.coffee | 2 +- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index bee0a5ac67..83b89bce34 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -18,7 +18,7 @@ module.exports = MongoManager = peekLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) -> # under normal use we pass back the last update as - # callback(null,update). + # callback(null,update,version). # # when we have an existing last update but want to force a new one # to start, we pass it back as callback(null,null,version), just @@ -26,17 +26,18 @@ module.exports = MongoManager = MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? if update? - if update.inS3? - # we want to force a new update, but ensure that it is - # consistent with the version of the existing one in S3 - return callback null, null, update.v - else if update.broken + if update.broken # the update is marked as broken so we will force a new op return callback null, null + else if update.pack? + return callback null, update, update.pack[0]?.v else - return callback null, update + return callback null, update, update.v else - callback null, null + MongoManager.getArchivedDocStatus doc_id, (error, status) -> + return callback(error) if error? + return callback(null, null, status.lastVersion) if status?.inS3? and status?.lastVersion? + callback null, null insertCompressedUpdates: (project_id, doc_id, updates, temporary, callback = (error) ->) -> jobs = [] @@ -152,6 +153,9 @@ module.exports = MongoManager = db.docHistoryStats.ensureIndex { doc_id: 1 }, { background: true } db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } + getArchivedDocStatus: (doc_id, callback)-> + db.docHistoryStats.findOne {doc_id: ObjectId(doc_id.toString()), inS3: {$exists:true}}, {inS3: true, lastVersion: true}, callback + getDocChangesCount: (doc_id, callback)-> db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, callback diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index c52d920cb7..f40a7c8503 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -17,17 +17,13 @@ module.exports = UpdatesManager = return callback() MongoManager.peekLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> - # lastCompressedUpdate is the most recent update in Mongo. + # lastCompressedUpdate is the most recent update in Mongo, and + # lastVersion is its sharejs version number. # - # The peekLastCompressedUpdate method may pass it back as 'null' - # to force the start of a new compressed update, even when there - # was a previous compressed update in Mongo. In this case it - # passes back the lastVersion from the update to check - # consistency. - - # when lastVersion is not provided, default to lastCompressedUpdate.v - lastVersion ?= lastCompressedUpdate?.v - + # The peekLastCompressedUpdate method may pass the update back + # as 'null' (for example if the previous compressed update has + # been archived). In this case it can still pass back the + # lastVersion from the update to allow us to check consistency. return callback(error) if error? # Ensure that raw updates start where lastVersion left off diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 2cdc7c8680..300357e406 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -57,6 +57,8 @@ describe "MongoManager", -> describe "peekLastCompressedUpdate", -> describe "when there is no last update", -> beforeEach -> + @db.docHistoryStats = {} + @db.docHistoryStats.findOne = sinon.stub().callsArgWith(2, null, null) @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) @MongoManager.peekLastCompressedUpdate @doc_id, @callback @@ -84,8 +86,10 @@ describe "MongoManager", -> describe "when there is a last update in S3", -> beforeEach -> - @update = { _id: Object(), v: 12345, inS3: true } - @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) + @update = { _id: Object(), v: 12345} + @db.docHistoryStats = {} + @db.docHistoryStats.findOne = sinon.stub().callsArgWith(2, null, {inS3:true, lastVersion: @update.v}) + @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null) @MongoManager.peekLastCompressedUpdate @doc_id, @callback it "should get the last update", -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 576fa76ef4..4fc85a5e19 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -69,7 +69,7 @@ describe "UpdatesManager", -> @lastCompressedUpdate = { v: 11, op: "compressed-op-11" } @compressedUpdates = [ { v: 12, op: "compressed-op-11+12" }, { v: 13, op: "compressed-op-12" } ] - @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate) + @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) From dc564fd5d034b4f23d50986f9fcb6906348401b3 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 15 Jan 2016 15:49:07 +0000 Subject: [PATCH 246/549] archiving document history now sends all changes to s3 --- .../app/coffee/DocArchiveManager.coffee | 57 +++++++++---------- .../track-changes/app/coffee/MongoAWS.coffee | 2 +- .../app/coffee/MongoManager.coffee | 20 +++---- .../coffee/ArchivingUpdatesTests.coffee | 23 ++++---- .../DocArchive/DocArchiveManager.coffee | 8 +-- .../MongoManager/MongoManagerTests.coffee | 35 ++++++------ 6 files changed, 70 insertions(+), 75 deletions(-) diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee index c639a84c99..d557278b8e 100644 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ b/services/track-changes/app/coffee/DocArchiveManager.coffee @@ -25,39 +25,38 @@ module.exports = DocArchiveManager = LockManager.runWithLock("HistoryLock:#{doc_id}", job, callback) archiveDocChanges: (project_id, doc_id, callback)-> + MongoManager.getArchivedDocStatus doc_id, (error, result) -> + return callback(error) if error? + if result?.inS3 is true + logger.log {project_id, doc_id}, "document history is already archived" + return callback() + if result?.inS3? + logger.log {project_id, doc_id}, "document history archive is already in progress" + return callback() MongoManager.getDocChangesCount doc_id, (error, count) -> return callback(error) if error? if count == 0 logger.log {project_id, doc_id}, "document history is empty, not archiving" return callback() - else if count == 1 - logger.log {project_id, doc_id}, "document history only has one entry, not archiving" - return callback() - else - MongoManager.getArchivedDocChanges doc_id, (error, count) -> + MongoManager.peekLastCompressedUpdate doc_id, (error, update, lastVersion) -> + return callback(error) if error? + logger.log {doc_id, project_id}, "archiving got last compressed update" + MongoManager.markDocHistoryAsArchiveInProgress doc_id, lastVersion, (error) -> return callback(error) if error? - if count != 0 - logger.log {project_id, doc_id}, "document history contains archived entries, not archiving" - return callback() - MongoManager.getLastCompressedUpdate doc_id, (error, update) -> - return callback(error) if error? - logger.log {doc_id, project_id}, "archiving got last compressed update" - MongoManager.markDocHistoryAsArchiveInProgress doc_id, update, (error) -> - return callback(error) if error? - logger.log {doc_id, project_id}, "marked doc history as archive in progress" - MongoAWS.archiveDocHistory project_id, doc_id, update, (error) -> - if error? - logger.log {doc_id, project_id, error}, "error exporting document to S3" - MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> - return callback(err) if err? - logger.log {doc_id, project_id}, "cleared archive in progress flag" - callback(error) - else - logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" - MongoManager.markDocHistoryAsArchived doc_id, update, (error) -> - return callback(error) if error? - logger.log {doc_id, project_id}, "marked doc history as archived" - callback() + logger.log {doc_id, project_id}, "marked doc history as archive in progress" + MongoAWS.archiveDocHistory project_id, doc_id, update, (error) -> + if error? + logger.log {doc_id, project_id, error}, "error exporting document to S3" + MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> + return callback(err) if err? + logger.log {doc_id, project_id}, "cleared archive in progress flag" + callback(error) + else + logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" + MongoManager.markDocHistoryAsArchived doc_id, lastVersion, (error) -> + return callback(error) if error? + logger.log {doc_id, project_id}, "marked doc history as archived" + callback() unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> DocstoreHandler.getAllDocs project_id, (error, docs) -> @@ -75,9 +74,9 @@ module.exports = DocArchiveManager = LockManager.runWithLock("HistoryLock:#{doc_id}", job, callback) unArchiveDocChanges: (project_id, doc_id, callback)-> - MongoManager.getArchivedDocChanges doc_id, (error, count) -> + MongoManager.getArchivedDocStatus doc_id, (error, result) -> return callback(error) if error? - if count == 0 + if result?.inS3 isnt true logger.log {project_id, doc_id}, "no changes marked as in s3, not unarchiving" return callback() else diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 31ec778d71..692e82bca7 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -19,7 +19,7 @@ module.exports = MongoAWS = query = { doc_id: ObjectId(doc_id) - v: {$lt: update.v} + v: {$lte: update.v} expiresAt: {$exists : false} } diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 83b89bce34..98a9025d8d 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -157,25 +157,23 @@ module.exports = MongoManager = db.docHistoryStats.findOne {doc_id: ObjectId(doc_id.toString()), inS3: {$exists:true}}, {inS3: true, lastVersion: true}, callback getDocChangesCount: (doc_id, callback)-> - db.docHistory.count { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }}, callback + db.docHistory.count { doc_id : ObjectId(doc_id.toString())}, callback - getArchivedDocChanges: (doc_id, callback)-> - db.docHistory.count { doc_id: ObjectId(doc_id.toString()) , inS3: { $exists: true }}, callback - - markDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> - db.docHistory.update { _id: update._id }, { $set : { inS3 : false } }, callback + markDocHistoryAsArchiveInProgress: (doc_id, lastVersion, callback) -> + db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, {$set : {inS3: false, lastVersion: lastVersion}}, {upsert:true}, callback clearDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> - db.docHistory.update { _id: update._id }, { $unset : { inS3 : true } }, callback + db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, {$unset : {inS3: true, lastVersion: true}}, callback - markDocHistoryAsArchived: (doc_id, update, callback)-> - db.docHistory.update { _id: update._id }, { $set : { inS3 : true } }, (error)-> + markDocHistoryAsArchived: (doc_id, lastVersion, callback)-> + db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, {$set : {inS3: true}}, {upsert:true}, (error)-> return callback(error) if error? - db.docHistory.remove { doc_id : ObjectId(doc_id.toString()), inS3 : { $exists : false }, v: { $lt : update.v }, expiresAt: {$exists : false} }, (error)-> + # clear the archived entries from the docHistory now we have finally succeeded + db.docHistory.remove { doc_id : ObjectId(doc_id.toString()), v: {$lte : lastVersion}, expiresAt: {$exists : false} }, (error)-> return callback(error) if error? callback(error) markDocHistoryAsUnarchived: (doc_id, callback)-> # note this removes any inS3 field, regardless of its value (true/false/null) - db.docHistory.update { doc_id: ObjectId(doc_id.toString()) }, { $unset : { inS3 : true } }, { multi: true }, (error)-> + db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, { $unset : { inS3: true, lastVersion: true} }, (error)-> callback(error) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index ea8caa3226..eb04a976ab 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -71,27 +71,27 @@ describe "Archiving updates", -> throw error if error? done() - it "should remain one doc change", (done) -> + it "should remain zero doc change", (done) -> db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> throw error if error? - count.should.equal 1 + count.should.equal 0 done() - it "should remained doc marked as inS3", (done) -> - db.docHistory.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> + it "should have docHistoryStats marked as inS3", (done) -> + db.docHistoryStats.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> throw error if error? doc.inS3.should.equal true done() - it "should remained doc have last version", (done) -> - db.docHistory.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> + it "should have docHistoryStats with the last version", (done) -> + db.docHistoryStats.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> throw error if error? - doc.v.should.equal 20 + doc.lastVersion.should.equal 20 done() - it "should store nineteen doc changes in S3", (done) -> + it "should store twenty doc changes in S3", (done) -> TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => - doc.length.should.equal 19 + doc.length.should.equal 20 done() describe "unarchiving a doc's updates", -> @@ -107,7 +107,8 @@ describe "Archiving updates", -> done() it "should remove doc marked as inS3", (done) -> - db.docHistory.count { doc_id: ObjectId(@doc_id), inS3 : true }, (error, count) -> + db.docHistoryStats.findOne {doc_id: ObjectId(@doc_id)}, (error, doc) -> throw error if error? - count.should.equal 0 + doc.should.not.contain.key('inS3') + doc.should.not.contain.key('lastVersion') done() diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee index fe648b6e67..cc1b4ec3e7 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee @@ -61,8 +61,8 @@ describe "DocArchiveManager", -> beforeEach -> @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} @MongoManager.getDocChangesCount = sinon.stub().callsArg(1) - @MongoManager.getArchivedDocChanges = sinon.stub().callsArgWith(1, null, 0) - @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) + @MongoManager.getArchivedDocStatus = sinon.stub().callsArgWith(1, null, 0) + @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update, @update.v) @MongoAWS.archiveDocHistory = sinon.stub().callsArg(3) @MongoManager.markDocHistoryAsArchiveInProgress = sinon.stub().callsArg(2) @MongoManager.markDocHistoryAsArchived = sinon.stub().callsArg(2) @@ -71,7 +71,7 @@ describe "DocArchiveManager", -> it "should run markDocHistoryAsArchived with doc_id and update", -> @MongoManager.markDocHistoryAsArchived .calledWith( - @doc_id, @update + @doc_id, @update.v ) .should.equal true it "should call the callback", -> @@ -107,7 +107,7 @@ describe "DocArchiveManager", -> describe "unArchiveDocChanges", -> beforeEach -> - @MongoManager.getArchivedDocChanges = sinon.stub().callsArg(1) + @MongoManager.getArchivedDocStatus = sinon.stub().callsArgWith(1, null, {inS3: true}) @MongoAWS.unArchiveDocHistory = sinon.stub().callsArg(2) @MongoManager.markDocHistoryAsUnarchived = sinon.stub().callsArg(1) @DocArchiveManager.unArchiveDocChanges @project_id, @doc_id, @callback diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 300357e406..d1787c1649 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -406,21 +406,20 @@ describe "MongoManager", -> @db.docHistory.count .calledWith({ doc_id: ObjectId(@doc_id) - inS3 : { $exists : false } }) .should.equal true it "should call the callback", -> @callback.called.should.equal true - describe "getArchivedDocChanges", -> + describe "getArchivedDocStatus", -> beforeEach -> - @db.docHistory = - count: sinon.stub().callsArg(1) - @MongoManager.getArchivedDocChanges @doc_id, @callback + @db.docHistoryStats = + findOne: sinon.stub().callsArg(2) + @MongoManager.getArchivedDocStatus @doc_id, @callback it "should return if there is any archived doc changes", -> - @db.docHistory.count + @db.docHistoryStats.findOne .calledWith({ doc_id: ObjectId(@doc_id) inS3: {$exists: true} @@ -433,15 +432,16 @@ describe "MongoManager", -> describe "markDocHistoryAsArchived", -> beforeEach -> @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} + @db.docHistoryStats = + update: sinon.stub().callsArg(3) @db.docHistory = - update: sinon.stub().callsArg(2) remove: sinon.stub().callsArg(1) - @MongoManager.markDocHistoryAsArchived @doc_id, @update, @callback + @MongoManager.markDocHistoryAsArchived @doc_id, @update.v, @callback - it "should update last doc change with inS3 flag", -> - @db.docHistory.update + it "should update doc status with inS3 flag", -> + @db.docHistoryStats.update .calledWith({ - _id: ObjectId(@update._id) + doc_id: ObjectId(@doc_id) },{ $set : { inS3 : true } }) @@ -451,8 +451,7 @@ describe "MongoManager", -> @db.docHistory.remove .calledWith({ doc_id: ObjectId(@doc_id) - inS3 : { $exists : false } - v: { $lt : @update.v } + v: { $lte : @update.v } expiresAt: {$exists : false} }) .should.equal true @@ -462,18 +461,16 @@ describe "MongoManager", -> describe "markDocHistoryAsUnarchived", -> beforeEach -> - @db.docHistory = - update: sinon.stub().callsArg(3) + @db.docHistoryStats = + update: sinon.stub().callsArg(2) @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback it "should remove any doc changes inS3 flag", -> - @db.docHistory.update + @db.docHistoryStats.update .calledWith({ doc_id: ObjectId(@doc_id) },{ - $unset : { inS3 : true } - },{ - multi: true + $unset : { inS3 : true, lastVersion: true } }) .should.equal true From 5e830cbbdbaf0a9a41c85c7305efa0740e80fc4a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 11 Dec 2015 15:56:47 +0000 Subject: [PATCH 247/549] put all new ops into packs --- .../app/coffee/MongoManager.coffee | 10 +-- .../app/coffee/PackManager.coffee | 71 +++++++++++++++++++ .../app/coffee/UpdatesManager.coffee | 63 ++++++++++------ .../UpdatesManager/UpdatesManagerTests.coffee | 21 +++--- 4 files changed, 122 insertions(+), 43 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 98a9025d8d..ff3dea4cab 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -1,20 +1,17 @@ {db, ObjectId} = require "./mongojs" PackManager = require "./PackManager" async = require "async" +_ = require "underscore" module.exports = MongoManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> db.docHistory - .find(doc_id: ObjectId(doc_id.toString())) + .find(doc_id: ObjectId(doc_id.toString()), {pack: {$slice:-1}}) # only return the last entry in a pack .sort( v: -1 ) .limit(1) .toArray (error, compressedUpdates) -> return callback(error) if error? - if compressedUpdates[0]?.pack? - # cannot pop from a pack, throw error - error = new Error("last compressed update is a pack") - return callback error, null - return callback null, compressedUpdates[0] or null + callback null, compressedUpdates[0] or null peekLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) -> # under normal use we pass back the last update as @@ -55,7 +52,6 @@ module.exports = MongoManager = else callback(err,results) - modifyCompressedUpdate: (lastUpdate, newUpdate, callback = (error) ->) -> return callback() if not newUpdate? db.docHistory.findAndModify diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 082c1d8a9a..4e918814ff 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -451,3 +451,74 @@ module.exports = PackManager = bulk.execute callback else callback() + + insertCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> + return callback() if newUpdates.length == 0 + + updatesToFlush = [] + updatesRemaining = newUpdates.slice() + + n = lastUpdate?.n || 0 + sz = lastUpdate?.sz || 0 + + while updatesRemaining.length and n < PackManager.MAX_COUNT and sz < PackManager.MAX_SIZE + nextUpdate = updatesRemaining[0] + nextUpdateSize = BSON.calculateObjectSize(nextUpdate) + if nextUpdateSize + sz > PackManager.MAX_SIZE and n > 0 + break + n++ + sz += nextUpdateSize + updatesToFlush.push updatesRemaining.shift() + + PackManager.flushCompressedUpdates project_id, doc_id, lastUpdate, updatesToFlush, temporary, (error) -> + return callback(error) if error? + PackManager.insertCompressedUpdates project_id, doc_id, null, updatesRemaining, temporary, callback + + flushCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> + return callback() if newUpdates.length == 0 + if lastUpdate? + PackManager.appendUpdatesToExistingPack project_id, doc_id, lastUpdate, newUpdates, temporary, callback + else + PackManager.insertUpdatesIntoNewPack project_id, doc_id, newUpdates, temporary, callback + + insertUpdatesIntoNewPack: (project_id, doc_id, newUpdates, temporary, callback = (error) ->) -> + first = newUpdates[0] + last = newUpdates[newUpdates.length - 1] + n = newUpdates.length + sz = BSON.calculateObjectSize(newUpdates) + newPack = + project_id: ObjectId(project_id.toString()) + doc_id: ObjectId(doc_id.toString()) + pack: newUpdates + n: n + sz: sz + meta: + start_ts: first.meta.start_ts + end_ts: last.meta.end_ts + v: first.v + v_end: last.v + logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" + db.docHistory.insert newPack, callback + + appendUpdatesToExistingPack: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> + first = newUpdates[0] + last = newUpdates[newUpdates.length - 1] + n = newUpdates.length + sz = BSON.calculateObjectSize(newUpdates) + query = + _id: lastUpdate._id + project_id: ObjectId(project_id.toString()) + doc_id: ObjectId(doc_id.toString()) + pack: {$exists: true} + update = + $push: + "pack": {$each: newUpdates} + $inc: + "n": n + "sz": sz + $set: + "meta.end_ts": last.meta.end_ts + "v_end": last.v + logger.log {project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack" + db.docHistory.findAndModify {query, update}, callback + diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index f40a7c8503..97a630ec53 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -1,4 +1,5 @@ MongoManager = require "./MongoManager" +PackManager = require "./PackManager" RedisManager = require "./RedisManager" UpdateCompressor = require "./UpdateCompressor" LockManager = require "./LockManager" @@ -43,34 +44,50 @@ module.exports = UpdatesManager = else return callback error - compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates + if rawUpdates.length == 0 + return callback() - if not lastCompressedUpdate? - # no existing update, insert everything + if not lastCompressedUpdate? or lastCompressedUpdate.pack? # handle pack append as a special case + UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback + else #use the existing op code + UpdatesManager._updateOp project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback + + _updatePack: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) -> + compressedUpdates = UpdateCompressor.compressRawUpdates null, rawUpdates + PackManager.insertCompressedUpdates project_id, doc_id, lastCompressedUpdate, compressedUpdates, temporary, (error, result) -> + return callback(error) if error? + logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? + callback() + + _updateOp: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) -> + compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates + + if not lastCompressedUpdate? + # no existing update, insert everything + updateToModify = null + updatesToInsert = compressedUpdates + else + # there are existing updates, see what happens when we + # compress them together with the new ones + [firstUpdate, additionalUpdates...] = compressedUpdates + + if firstUpdate.v == lastCompressedUpdate.v and _.isEqual(firstUpdate, lastCompressedUpdate) + # first update version hasn't changed, skip it and insert remaining updates + # this is an optimisation, we could update the existing op with itself updateToModify = null - updatesToInsert = compressedUpdates + updatesToInsert = additionalUpdates else - # there are existing updates, see what happens when we - # compress them together with the new ones - [firstUpdate, additionalUpdates...] = compressedUpdates + # first update version did changed, modify it and insert remaining updates + updateToModify = firstUpdate + updatesToInsert = additionalUpdates - if firstUpdate.v == lastCompressedUpdate.v and _.isEqual(firstUpdate, lastCompressedUpdate) - # first update version hasn't changed, skip it and insert remaining updates - # this is an optimisation, we could update the existing op with itself - updateToModify = null - updatesToInsert = additionalUpdates - else - # first update version did changed, modify it and insert remaining updates - updateToModify = firstUpdate - updatesToInsert = additionalUpdates - - MongoManager.modifyCompressedUpdate lastCompressedUpdate, updateToModify, (error, result) -> + MongoManager.modifyCompressedUpdate lastCompressedUpdate, updateToModify, (error, result) -> + return callback(error) if error? + logger.log {project_id, doc_id, orig_v: lastCompressedUpdate.v, new_v: result.v}, "applied update in-place" if result? + MongoManager.insertCompressedUpdates project_id, doc_id, updatesToInsert, temporary,(error) -> return callback(error) if error? - logger.log {project_id, doc_id, orig_v: lastCompressedUpdate.v, new_v: result.v}, "applied update in-place" if result? - MongoManager.insertCompressedUpdates project_id, doc_id, updatesToInsert, temporary,(error) -> - return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" - callback() + logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: rawUpdates.length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" + callback() REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 4fc85a5e19..299c4ba068 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -10,6 +10,7 @@ describe "UpdatesManager", -> @UpdatesManager = SandboxedModule.require modulePath, requires: "./UpdateCompressor": @UpdateCompressor = {} "./MongoManager" : @MongoManager = {} + "./PackManager" : @PackManager = {} "./RedisManager" : @RedisManager = {} "./LockManager" : @LockManager = {} "./WebApiManager": @WebApiManager = {} @@ -41,8 +42,7 @@ describe "UpdatesManager", -> @compressedUpdates = [ { v: 13, op: "compressed-op-12" } ] @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) + @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback @@ -51,16 +51,12 @@ describe "UpdatesManager", -> .calledWith(@doc_id) .should.equal true - it "should compress the raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(null, @rawUpdates) - .should.equal true - it "should save the compressed ops", -> - @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @compressedUpdates, @temporary) + @PackManager.insertCompressedUpdates + .calledWith(@project_id, @doc_id, null, @compressedUpdates, @temporary) .should.equal true + it "should call the callback", -> @callback.called.should.equal true @@ -136,8 +132,7 @@ describe "UpdatesManager", -> @compressedUpdates = [ { v: 13, op: "compressed-op-12" } ] @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, @lastVersion) - @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) + @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) describe "when the raw ops start where the existing history ends", -> @@ -156,8 +151,8 @@ describe "UpdatesManager", -> .should.equal true it "should save the compressed ops", -> - @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @compressedUpdates, @temporary) + @PackManager.insertCompressedUpdates + .calledWith(@project_id, @doc_id, null, @compressedUpdates, @temporary) .should.equal true it "should call the callback", -> From 0ba00a9eb73b9aec584c5b13b6e17c9554bada9a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 17 Dec 2015 11:41:20 +0000 Subject: [PATCH 248/549] expire temporary packs and roll over to a new pack each day --- services/track-changes/app/coffee/PackManager.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 4e918814ff..f328cc0939 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -4,6 +4,8 @@ _ = require "underscore" logger = require "logger-sharelatex" LockManager = require "./LockManager" +DAYS = 24 * 3600 * 1000 # one day in milliseconds + module.exports = PackManager = # The following functions implement methods like a mongo find, but # expands any documents containing a 'pack' field into multiple @@ -476,7 +478,7 @@ module.exports = PackManager = flushCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> return callback() if newUpdates.length == 0 - if lastUpdate? + if lastUpdate? and not (temporary and ((Date.now() - lastUpdate.meta?.start_ts) > 1 * DAYS)) PackManager.appendUpdatesToExistingPack project_id, doc_id, lastUpdate, newUpdates, temporary, callback else PackManager.insertUpdatesIntoNewPack project_id, doc_id, newUpdates, temporary, callback @@ -497,6 +499,8 @@ module.exports = PackManager = end_ts: last.meta.end_ts v: first.v v_end: last.v + if temporary + newPack.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" db.docHistory.insert newPack, callback @@ -519,6 +523,8 @@ module.exports = PackManager = $set: "meta.end_ts": last.meta.end_ts "v_end": last.v + if lastUpdate.expiresAt and temporary + update.$set.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack" db.docHistory.findAndModify {query, update}, callback From 0532a4daaa98e4c63e8aeed7c8b0de5ac13a7299 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Dec 2015 11:29:46 +0000 Subject: [PATCH 249/549] use compound index to replace separate index for packs --- .../app/coffee/MongoManager.coffee | 4 +--- .../app/coffee/PackManager.coffee | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index ff3dea4cab..13df864c7b 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -136,9 +136,7 @@ module.exports = MongoManager = # For finding all updates that go into a diff for a doc db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } # For finding all updates that affect a project - db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } - # For finding all packs that affect a project (use a sparse index so only packs are included) - db.docHistory.ensureIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, { background: true, sparse: true } + db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1, "meta.start_ts": -1 }, { background: true } # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index f328cc0939..e838c8b001 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -178,20 +178,25 @@ module.exports = PackManager = .find(tailQuery, projection) .sort(sort) - # now find any packs that overlap with the time window + # now find any packs that overlap with the time window from outside + # cutoff before + # --|-----wanted-range--|------------------ time=> + # |-------------|pack(end_ts) + # + # these were not picked up by the original query because + # end_ts>before but the beginning of the pack may be in the time range overlapQuery = _.clone(query) if before? && cutoff? overlapQuery['meta.end_ts'] = {"$gte": before} - overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } + overlapQuery['meta.start_ts'] = {"$lte": before } else if before? && not cutoff? overlapQuery['meta.end_ts'] = {"$gte": before} - overlapQuery['pack.0.meta.end_ts'] = {"$lte": before } + overlapQuery['meta.start_ts'] = {"$lte": before } else if not before? && cutoff? - overlapQuery['meta.end_ts'] = {"$gte": cutoff} - overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } + overlapQuery['meta.end_ts'] = {"$gte": cutoff} # we already have these?? else if not before? && not cutoff? - overlapQuery['meta.end_ts'] = {"$gte": 0 } - overlapQuery['pack.0.meta.end_ts'] = {"$gte": 0 } + overlapQuery['meta.end_ts'] = {"$gte": 0 } # shouldn't happen?? + overlap = collection .find(overlapQuery, projection) .sort(sort) From f64969c7843a3378c006f397fc4af73661600211 Mon Sep 17 00:00:00 2001 From: Brian - Work Date: Mon, 11 Jan 2016 11:56:12 +0000 Subject: [PATCH 250/549] added comment about query memory usage for toArray --- services/track-changes/app/coffee/PackManager.coffee | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index e838c8b001..210139d468 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -88,6 +88,10 @@ module.exports = PackManager = needMore = false # keep track of whether we need to load more data updates = [] # used to accumulate the set of results + + # FIXME: packs are big so we should accumulate the results + # incrementally instead of using .toArray() to avoid reading all + # of the changes into memory cursor.toArray (err, result) -> unpackedSet = PackManager._unpackResults(result) updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) @@ -141,6 +145,9 @@ module.exports = PackManager = updates = [] # used to accumulate the set of results + # FIXME: packs are big so we should accumulate the results + # incrementally instead of using .toArray() to avoid reading all + # of the changes into memory cursor.toArray (err, result) -> if err? return callback err, result From c6be12f3d5432c200fa955455c52a36600beae39 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 11 Jan 2016 12:42:22 +0000 Subject: [PATCH 251/549] set v_end on pack creation --- services/track-changes/app/coffee/PackManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 210139d468..17dae0cba7 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -300,6 +300,7 @@ module.exports = PackManager = top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } top.sz = sz + top.v_end = d.v delete top.op delete top._id packs.push top From f592611cacd1ad6b6960f73eb2e442ac0e84ced7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 11 Jan 2016 12:42:53 +0000 Subject: [PATCH 252/549] always create a new pack, never keep as op --- .../track-changes/app/coffee/PackManager.coffee | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 17dae0cba7..5f67dd4baf 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -269,15 +269,10 @@ module.exports = PackManager = MAX_SIZE: 1024*1024 # make these configurable parameters MAX_COUNT: 1024 - MIN_COUNT: 100 - KEEP_OPS: 100 convertDocsToPacks: (docs, callback) -> packs = [] top = null - # keep the last KEEP_OPS as individual ops - docs = docs.slice(0,-PackManager.KEEP_OPS) - docs.forEach (d,i) -> # skip existing packs if d.pack? @@ -294,7 +289,7 @@ module.exports = PackManager = top.v_end = d.v top.meta.end_ts = d.meta.end_ts return - else if sz < PackManager.MAX_SIZE + else # create a new pack top = _.clone(d) top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] @@ -304,13 +299,7 @@ module.exports = PackManager = delete top.op delete top._id packs.push top - else - # keep the op - # util.log "keeping large op unchanged (#{sz} bytes)" - # only store packs with a sufficient number of ops, discard others - packs = packs.filter (packObj) -> - packObj.pack.length > PackManager.MIN_COUNT callback(null, packs) checkHistory: (docs, callback) -> From 679a81564ee73fb7e9ded5a640278ba1e20e3073 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 11 Jan 2016 12:46:26 +0000 Subject: [PATCH 253/549] respect mongo 3 limit of 1000 bulk operations --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 5f67dd4baf..5b277c3ec4 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -268,7 +268,7 @@ module.exports = PackManager = return newResults MAX_SIZE: 1024*1024 # make these configurable parameters - MAX_COUNT: 1024 + MAX_COUNT: 512 convertDocsToPacks: (docs, callback) -> packs = [] From 78b3412ca834e184d66a346b77e07541187873f6 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 11 Jan 2016 14:36:11 +0000 Subject: [PATCH 254/549] decrease delay when packing --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 5b277c3ec4..c4562d48d4 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -409,7 +409,7 @@ module.exports = PackManager = }, {upsert:true}, () -> callback null, null - DB_WRITE_DELAY: 2000 + DB_WRITE_DELAY: 100 savePacks: (packs, callback) -> async.eachSeries packs, PackManager.safeInsert, (err, result) -> From 399a8f0a297aa5e687dcc12e5804f3221730f451 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 15 Jan 2016 15:02:34 +0000 Subject: [PATCH 255/549] update tests to assume packs are created --- .../coffee/AppendingUpdatesTests.coffee | 36 +++++++++---------- .../coffee/ArchivingUpdatesTests.coffee | 7 ++-- .../coffee/FlushingUpdatesTests.coffee | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index a82767e7c5..3d4bf14465 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -37,7 +37,7 @@ describe "Appending doc ops to the history", -> done() it "should insert the compressed op into mongo", -> - expect(@updates[0].op).to.deep.equal [{ + expect(@updates[0].pack[0].op).to.deep.equal [{ p: 3, i: "foo" }] @@ -99,13 +99,13 @@ describe "Appending doc ops to the history", -> throw error if error? done() - it "should combine all the updates into one", -> - expect(@updates[0].op).to.deep.equal [{ - p: 3, i: "foobar" + it "should combine all the updates into one pack", -> + expect(@updates[0].pack[1].op).to.deep.equal [{ + p: 6, i: "bar" }] it "should insert the correct version number into mongo", -> - expect(@updates[0].v).to.equal 8 + expect(@updates[0].v_end).to.equal 8 describe "when the updates are far apart", -> @@ -129,11 +129,11 @@ describe "Appending doc ops to the history", -> throw error if error? done() - it "should keep the updates separate", -> - expect(@updates[0].op).to.deep.equal [{ + it "should combine the updates into one pack", -> + expect(@updates[0].pack[0].op).to.deep.equal [{ p: 3, i: "foo" }] - expect(@updates[1].op).to.deep.equal [{ + expect(@updates[0].pack[1].op).to.deep.equal [{ p: 6, i: "bar" }] @@ -160,10 +160,10 @@ describe "Appending doc ops to the history", -> done() it "should concat the compressed op into mongo", -> - expect(@updates[0].op).to.deep.equal @expectedOp + expect(@updates[0].pack.length).to.deep.equal 3 # batch size is 100 it "should insert the correct version number into mongo", -> - expect(@updates[0].v).to.equal 250 + expect(@updates[0].v_end).to.equal 250 describe "when there are multiple ops in each update", -> @@ -188,16 +188,16 @@ describe "Appending doc ops to the history", -> done() it "should insert the compressed ops into mongo", -> - expect(@updates[0].op).to.deep.equal [{ + expect(@updates[0].pack[0].op).to.deep.equal [{ p: 3, i: "foo" }] - expect(@updates[1].op).to.deep.equal [{ + expect(@updates[0].pack[1].op).to.deep.equal [{ p: 6, i: "bar" }] it "should insert the correct version numbers into mongo", -> - expect(@updates[0].v).to.equal 3 - expect(@updates[1].v).to.equal 4 + expect(@updates[0].pack[0].v).to.equal 3 + expect(@updates[0].pack[1].v).to.equal 4 describe "when there is a no-op update", -> before (done) -> @@ -221,17 +221,17 @@ describe "Appending doc ops to the history", -> done() it "should insert the compressed no-op into mongo", -> - expect(@updates[0].op).to.deep.equal [] + expect(@updates[0].pack[0].op).to.deep.equal [] it "should insert the compressed next update into mongo", -> - expect(@updates[1].op).to.deep.equal [{ + expect(@updates[0].pack[1].op).to.deep.equal [{ p: 3, i: "foo" }] it "should insert the correct version numbers into mongo", -> - expect(@updates[0].v).to.equal 3 - expect(@updates[1].v).to.equal 4 + expect(@updates[0].pack[0].v).to.equal 3 + expect(@updates[0].pack[1].v).to.equal 4 describe "when the project has versioning enabled", -> before (done) -> diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index eb04a976ab..ab06b991b8 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -89,9 +89,10 @@ describe "Archiving updates", -> doc.lastVersion.should.equal 20 done() - it "should store twenty doc changes in S3", (done) -> + it "should store twenty doc changes in S3 in one pack", (done) -> TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => - doc.length.should.equal 20 + doc.length.should.equal 1 + doc[0].pack.length.should.equal 20 done() describe "unarchiving a doc's updates", -> @@ -103,7 +104,7 @@ describe "Archiving updates", -> it "should restore doc changes", (done) -> db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> throw error if error? - count.should.equal 20 + count.should.equal 1 done() it "should remove doc marked as inS3", (done) -> diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee index 63ddca4da9..3f82304da1 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -31,7 +31,7 @@ describe "Flushing updates", -> it "should flush the op into mongo", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates[0].op).to.deep.equal [{ + expect(updates[0].pack[0].op).to.deep.equal [{ p: 3, i: "f" }] done() From 61d22f027a2a08209ef222d0ddf3253b247a95d0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 18 Jan 2016 15:17:48 +0000 Subject: [PATCH 256/549] added test for inserting as pack --- .../UpdatesManager/UpdatesManagerTests.coffee | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 299c4ba068..f742499d1b 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -51,12 +51,11 @@ describe "UpdatesManager", -> .calledWith(@doc_id) .should.equal true - it "should save the compressed ops", -> + it "should save the compressed ops as a pack", -> @PackManager.insertCompressedUpdates .calledWith(@project_id, @doc_id, null, @compressedUpdates, @temporary) .should.equal true - it "should call the callback", -> @callback.called.should.equal true @@ -98,6 +97,30 @@ describe "UpdatesManager", -> it "should call the callback", -> @callback.called.should.equal true + describe "when the raw ops start where the existing history ends and the history is in a pack", -> + beforeEach -> + @lastCompressedUpdate = {pack: [{ v: 11, op: "compressed-op-11" }], v:11} + @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + + it "should look at the last compressed op", -> + @MongoManager.peekLastCompressedUpdate + .calledWith(@doc_id) + .should.equal true + + it "should defer the compression of raw ops to PackManager", -> + @UpdateCompressor.compressRawUpdates + .should.not.be.called + + it "should save the new compressed ops into a pack", -> + @PackManager.insertCompressedUpdates + .calledWith(@project_id, @doc_id, @lastCompressedUpdate, @compressedUpdates, @temporary) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + describe "when some raw ops are passed that have already been compressed", -> beforeEach -> @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] From ae61d1261ee176c73fd03fdb44be50373a55cbda Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 19 Jan 2016 15:52:12 +0000 Subject: [PATCH 257/549] added tests for pack updates --- .../PackManager/PackManagerTests.coffee | 232 ++++++++++++++++++ .../UpdatesManager/UpdatesManagerTests.coffee | 3 +- 2 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee new file mode 100644 index 0000000000..c538096a0a --- /dev/null +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -0,0 +1,232 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +expect = chai.expect +modulePath = "../../../../app/js/PackManager.js" +SandboxedModule = require('sandboxed-module') +{ObjectId} = require("mongojs") +bson = require("bson") +BSON = new bson.BSONPure() + +tk = require "timekeeper" + +describe "PackManager", -> + beforeEach -> + tk.freeze(new Date()) + @PackManager = SandboxedModule.require modulePath, requires: + "./mongojs" : { db: @db = {}, ObjectId: ObjectId, BSON: BSON } + "./LockManager" : {} + "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } + @callback = sinon.stub() + @doc_id = ObjectId().toString() + @project_id = ObjectId().toString() + + afterEach -> + tk.reset() + + describe "insertCompressedUpdates", -> + beforeEach -> + @lastUpdate = { + _id: "12345" + pack: [ + { op: "op-1", meta: "meta-1", v: 1}, + { op: "op-2", meta: "meta-2", v: 2} + ] + n : 2 + sz : 100 + } + @newUpdates = [ + { op: "op-3", meta: "meta-3", v: 3}, + { op: "op-4", meta: "meta-4", v: 4} + ] + @db.docHistory = + insert: sinon.stub().callsArg(1) + findAndModify: sinon.stub().callsArg(1) + + describe "with no last update", -> + beforeEach -> + @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) + @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback + + describe "for a small update", -> + it "should insert the update into a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates, true).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "for many small updates", -> + beforeEach -> + @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048]) + @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, false, @callback + + it "should append the initial updates to the existing pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[0...512], false).should.equal true + + it "should insert the first set remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[512...1024], false).should.equal true + + it "should insert the second set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1024...1536], false).should.equal true + + it "should insert the third set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1536...2048], false).should.equal true + + it "should insert the final set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2048..2048], false).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + + + describe "with an existing pack as the last update", -> + beforeEach -> + @PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5) + @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) + @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + + describe "for a small update", -> + it "should append the update to the existing pack", -> + @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates, false).should.equal true + it "should not insert any new packs", -> + @PackManager.insertUpdatesIntoNewPack.called.should.equal false + it "should call the callback", -> + @callback.called.should.equal true + + describe "for many small updates", -> + beforeEach -> + @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048]) + @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + + it "should append the initial updates to the existing pack", -> + @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0...510], false).should.equal true + + it "should insert the first set remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[510...1022], false).should.equal true + + it "should insert the second set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1022...1534], false).should.equal true + + it "should insert the third set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1534...2046], false).should.equal true + + it "should insert the final set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2046..2048], false).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "for many big updates", -> + beforeEach -> + longString = ("a" for [0 .. (0.75*@PackManager.MAX_SIZE)]).join("") + @newUpdates = ({ op: "op-#{i}-#{longString}", meta: "meta-#{i}", v: i} for i in [0..4]) + @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + + it "should append the initial updates to the existing pack", -> + @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0..0], false).should.equal true + + it "should insert the first set remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1..1], false).should.equal true + + it "should insert the second set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2..2], false).should.equal true + + it "should insert the third set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[3..3], false).should.equal true + + it "should insert the final set of remaining updates as a new pack", -> + @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[4..4], false).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "flushCompressedUpdates", -> + describe "when there is no previous update", -> + beforeEach -> + @PackManager.flushCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback + + describe "for a small update that will expire", -> + it "should insert the update into mongo", -> + @db.docHistory.insert.calledWithMatch({ + pack: @newUpdates, + project_id: ObjectId(@project_id), + doc_id: ObjectId(@doc_id) + n: @newUpdates.length + v: @newUpdates[0].v + v_end: @newUpdates[@newUpdates.length-1].v + }).should.equal true + + it "should set an expiry time in the future", -> + @db.docHistory.insert.calledWithMatch({ + expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) + }).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when there is a recent previous update in mongo", -> + beforeEach -> + @lastUpdate = { + _id: "12345" + pack: [ + { op: "op-1", meta: "meta-1", v: 1}, + { op: "op-2", meta: "meta-2", v: 2} + ] + n : 2 + sz : 100 + expiresAt: new Date(Date.now()) + } + + @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback + + describe "for a small update that will expire", -> + it "should append the update in mongo", -> + @db.docHistory.findAndModify.calledWithMatch({ + query: {_id: @lastUpdate._id} + update: { $push: {"pack" : {$each: @newUpdates}}, $set: {v_end: @newUpdates[@newUpdates.length-1].v}} + }).should.equal true + + it "should set an expiry time in the future", -> + @db.docHistory.findAndModify.calledWithMatch({ + update: {$set: {expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)}} + }).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + + describe "when there is an old previous update in mongo", -> + beforeEach -> + @lastUpdate = { + _id: "12345" + pack: [ + { op: "op-1", meta: "meta-1", v: 1}, + { op: "op-2", meta: "meta-2", v: 2} + ] + n : 2 + sz : 100 + meta: {start_ts: Date.now() - 30 * 24 * 3600 * 1000} + expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000) + } + + @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback + + describe "for a small update that will expire", -> + it "should insert the update into mongo", -> + @db.docHistory.insert.calledWithMatch({ + pack: @newUpdates, + project_id: ObjectId(@project_id), + doc_id: ObjectId(@doc_id) + n: @newUpdates.length + v: @newUpdates[0].v + v_end: @newUpdates[@newUpdates.length-1].v + }).should.equal true + + it "should set an expiry time in the future", -> + @db.docHistory.insert.calledWithMatch({ + expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) + }).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index f742499d1b..64ed839bdc 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -67,6 +67,7 @@ describe "UpdatesManager", -> @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) + @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) describe "when the raw ops start where the existing history ends", -> @@ -109,7 +110,7 @@ describe "UpdatesManager", -> .calledWith(@doc_id) .should.equal true - it "should defer the compression of raw ops to PackManager", -> + it "should defer the compression of raw ops until they are written in a new pack", -> @UpdateCompressor.compressRawUpdates .should.not.be.called From 84ace7f4c7c25df9b422b6be85c78195af79fc0e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 20 Jan 2016 14:05:05 +0000 Subject: [PATCH 258/549] use packs only for temporary ops --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 97a630ec53..9f4ae4580f 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -47,7 +47,7 @@ module.exports = UpdatesManager = if rawUpdates.length == 0 return callback() - if not lastCompressedUpdate? or lastCompressedUpdate.pack? # handle pack append as a special case + if temporary and (not lastCompressedUpdate? or lastCompressedUpdate.pack?) # handle pack append as a special case UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback else #use the existing op code UpdatesManager._updateOp project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback From 9b2cd11cd468198b56634b902c964c2768f164e3 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 22 Jan 2016 10:45:24 +0000 Subject: [PATCH 259/549] don't try to append to packs when using the old op code --- services/track-changes/app/coffee/UpdatesManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 9f4ae4580f..29038fba8e 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -50,6 +50,7 @@ module.exports = UpdatesManager = if temporary and (not lastCompressedUpdate? or lastCompressedUpdate.pack?) # handle pack append as a special case UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback else #use the existing op code + lastCompressedUpdate = null if lastCompressedUpdate?.pack? # can't handle packs with op code UpdatesManager._updateOp project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback _updatePack: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) -> From d10123d3c48420eefc853b35d9ad4e2c179f3048 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 25 Jan 2016 09:45:25 +0000 Subject: [PATCH 260/549] include n parameter when packing --- services/track-changes/app/coffee/PackManager.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index c4562d48d4..c7cad2ad9e 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -286,6 +286,7 @@ module.exports = PackManager = if top? && top.pack.length < PackManager.MAX_COUNT && top.sz + sz < PackManager.MAX_SIZE top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} top.sz += sz + top.n += 1 top.v_end = d.v top.meta.end_ts = d.meta.end_ts return @@ -295,6 +296,7 @@ module.exports = PackManager = top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } top.sz = sz + top.n = 1 top.v_end = d.v delete top.op delete top._id From 29c7c5e2496c46e08f53a7b10d63a4694db95e34 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 25 Jan 2016 09:55:55 +0000 Subject: [PATCH 261/549] enable packs by default for new docs --- services/track-changes/app/coffee/UpdatesManager.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 29038fba8e..6332951734 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -47,10 +47,9 @@ module.exports = UpdatesManager = if rawUpdates.length == 0 return callback() - if temporary and (not lastCompressedUpdate? or lastCompressedUpdate.pack?) # handle pack append as a special case + if (not lastCompressedUpdate?) or lastCompressedUpdate.pack? # handle pack append as a special case UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback else #use the existing op code - lastCompressedUpdate = null if lastCompressedUpdate?.pack? # can't handle packs with op code UpdatesManager._updateOp project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback _updatePack: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) -> From b3ddd839e6070f0c77009061e278aa75529e949d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 Jan 2016 11:28:02 +0000 Subject: [PATCH 262/549] add logging of raw updates --- services/track-changes/app.coffee | 16 +++++++++++++++- .../app/coffee/UpdatesManager.coffee | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 63c2949d25..2faeec2286 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -1,10 +1,24 @@ Settings = require "settings-sharelatex" logger = require "logger-sharelatex" -logger.initialize("track-changes") +TrackChangesLogger = logger.initialize("track-changes").logger if Settings.sentry?.dsn? logger.initializeErrorReporting(Settings.sentry.dsn) +# log updates as truncated strings +truncateFn = (updates) -> + JSON.stringify updates, (key, value) -> + if typeof value == 'string' && (len = value.length) > 80 + return value.substr(0,32) + "...(message of length #{len} truncated)..." + value.substr(-32) + else + return value + +TrackChangesLogger.addSerializers { + rawUpdates: truncateFn + newUpdates: truncateFn + lastUpdate: truncateFn +} + Path = require "path" Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 6332951734..1047d98240 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -102,6 +102,7 @@ module.exports = UpdatesManager = # parse the redis strings into ShareJs updates RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> return callback(error) if error? + logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" From ed0aaa189d8d1f114770e4fcc88f864c9a6cec59 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 Jan 2016 12:07:33 +0000 Subject: [PATCH 263/549] add test for non-overlapping insert-delete case --- .../app/coffee/UpdateCompressor.coffee | 3 ++- .../UpdateCompressorTests.coffee | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 95e3f58e86..7f606b29d5 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -133,6 +133,7 @@ module.exports = UpdateCompressor = else if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) offset = secondOp.p - firstOp.p insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) + # Only trim the insert when the delete is fully contained within in it if insertedText == secondOp.d insert = strRemove(firstOp.i, offset, secondOp.d.length) return [ @@ -146,7 +147,7 @@ module.exports = UpdateCompressor = v: secondUpdate.v ] else - # This shouldn't be possible! + # This will only happen if the delete extends outside the insert return [firstUpdate, secondUpdate] else diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index be3bec5350..4486219b68 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -263,6 +263,26 @@ describe "UpdateCompressor", -> v: 43 }] + it "should not combine updates with overlap beyond the end", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foobar" } + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, d: "bardle" } + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: { p: 3, i: "foobar" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, d: "bardle" } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 + }] + describe "noop - insert", -> it "should leave them untouched", -> expect(@UpdateCompressor.compressUpdates [{ From b7a4c72f9cb4f8c93fad66dbc019e40f0da84692 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 Jan 2016 12:23:21 +0000 Subject: [PATCH 264/549] avoid compressing updates if the result would be too big --- .../app/coffee/UpdateCompressor.coffee | 9 ++- .../UpdateCompressorTests.coffee | 63 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 7f606b29d5..98b2e048bf 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -80,6 +80,7 @@ module.exports = UpdateCompressor = return compressedUpdates MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 + MAX_UPDATE_SIZE: twoMegabytes = 2* 1024 * 1024 _concatTwoUpdates: (firstUpdate, secondUpdate) -> firstUpdate = @@ -105,8 +106,12 @@ module.exports = UpdateCompressor = firstOp = firstUpdate.op secondOp = secondUpdate.op + + firstSize = firstOp.i?.length or firstOp.d?.length + secondSize = secondOp.i?.length or secondOp.d?.length + # Two inserts - if firstOp.i? and secondOp.i? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) + if firstOp.i? and secondOp.i? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) and firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE return [ meta: start_ts: firstUpdate.meta.start_ts @@ -118,7 +123,7 @@ module.exports = UpdateCompressor = v: secondUpdate.v ] # Two deletes - else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) + else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) and firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE return [ meta: start_ts: firstUpdate.meta.start_ts diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index 4486219b68..eab7422291 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -10,6 +10,8 @@ describe "UpdateCompressor", -> @UpdateCompressor = SandboxedModule.require modulePath @user_id = "user-id-1" @other_user_id = "user-id-2" + @bigstring = ("a" for [0 .. 2*1024*1024]).join("") + @mediumstring = ("a" for [0 .. 1024*1024]).join("") @ts1 = Date.now() @ts2 = Date.now() + 1000 @@ -141,6 +143,67 @@ describe "UpdateCompressor", -> v: 43 }] + it "should not append inserts that are too big (second op)", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: "foo" } + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, i: @bigstring } + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: { p: 3, i: "foo" } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 6, i: @bigstring } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 + }] + + it "should not append inserts that are too big (first op)", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: @bigstring } + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 3 + @bigstring.length, i: "bar" } + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: { p: 3, i: @bigstring } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 3 + @bigstring.length, i: "bar" } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 + }] + + it "should not append inserts that are too big (first and second op)", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, i: @mediumstring } + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 3 + @mediumstring.length, i: @mediumstring } + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: { p: 3, i: @mediumstring } + meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 3 + @mediumstring.length, i: @mediumstring } + meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + v: 43 + }] + + describe "delete - delete", -> it "should append one delete to the other", -> expect(@UpdateCompressor.compressUpdates [{ From b44a7b9aa6263c120aebf88b9aad45a5b114cc84 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 Jan 2016 14:52:40 +0000 Subject: [PATCH 265/549] reject very large ops --- services/track-changes/app.coffee | 1 + .../track-changes/app/coffee/UpdatesManager.coffee | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 2faeec2286..e8a00cf8f4 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -16,6 +16,7 @@ truncateFn = (updates) -> TrackChangesLogger.addSerializers { rawUpdates: truncateFn newUpdates: truncateFn + rawUpdate: truncateFn lastUpdate: truncateFn } diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 1047d98240..bffd7f540e 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -47,6 +47,17 @@ module.exports = UpdatesManager = if rawUpdates.length == 0 return callback() + # some old large ops in redis need to be rejected, they predate + # the size limit that now prevents them going through the system + REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024 + for rawUpdate in rawUpdates + opSizes = ((op.i?.length || op.d?.length) for op in rawUpdate?.op or []) + size = _.max opSizes + if size > REJECT_LARGE_OP_SIZE + error = new Error("dropped op exceeding maximum allowed size of #{REJECT_LARGE_OP_SIZE}") + logger.error err: error, doc_id: doc_id, project_id: project_id, size: size, rawUpdate: rawUpdate, "dropped op - too big" + rawUpdate.op = [] + if (not lastCompressedUpdate?) or lastCompressedUpdate.pack? # handle pack append as a special case UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback else #use the existing op code From ba0de9208166b800f8f0be04b96cdff972ab386f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 26 Jan 2016 14:54:06 +0000 Subject: [PATCH 266/549] improve logging by avoiding string escapes --- services/track-changes/app.coffee | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index e8a00cf8f4..7e208764b6 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -7,16 +7,18 @@ if Settings.sentry?.dsn? # log updates as truncated strings truncateFn = (updates) -> - JSON.stringify updates, (key, value) -> - if typeof value == 'string' && (len = value.length) > 80 - return value.substr(0,32) + "...(message of length #{len} truncated)..." + value.substr(-32) - else - return value + JSON.parse( + JSON.stringify updates, (key, value) -> + if typeof value == 'string' && (len = value.length) > 80 + return value.substr(0,32) + "...(message of length #{len} truncated)..." + value.substr(-32) + else + return value + ) TrackChangesLogger.addSerializers { + rawUpdate: truncateFn rawUpdates: truncateFn newUpdates: truncateFn - rawUpdate: truncateFn lastUpdate: truncateFn } From 199d2aaa926efd9ed38aba235e4bc6e48521f0d7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 27 Jan 2016 15:14:23 +0000 Subject: [PATCH 267/549] script to pack existing docs --- services/track-changes/app.coffee | 1 + .../track-changes/app/coffee/HttpController.coffee | 10 ++++++++++ services/track-changes/app/coffee/PackManager.coffee | 4 ++++ services/track-changes/pack.sh | 7 +++++++ 4 files changed, 22 insertions(+) create mode 100755 services/track-changes/pack.sh diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 7e208764b6..4ed177e91c 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -49,6 +49,7 @@ app.post "/project/:project_id/flush", HttpController.flushProject app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore app.post "/doc/:doc_id/pack", HttpController.packDoc +app.get "/doc/list", HttpController.listDocs app.post '/project/:project_id/archive', HttpController.archiveProject app.post '/project/:project_id/unarchive', HttpController.unArchiveProject diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 1a90c16fd4..71a3c2a770 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -6,6 +6,7 @@ logger = require "logger-sharelatex" DocArchiveManager = require "./DocArchiveManager" HealthChecker = require "./HealthChecker" LockManager = require "./LockManager" +_ = require "underscore" module.exports = HttpController = flushDoc: (req, res, next = (error) ->) -> @@ -23,6 +24,15 @@ module.exports = HttpController = return next(error) if error? res.send 204 + listDocs: (req, res, next = (error) ->) -> + logger.log "listing packing doc history" + limit = +req.query?.limit || 100 + PackManager.listDocs {limit}, (error, doc_ids) -> + return next(error) if error? + ids = (doc.doc_id.toString() for doc in doc_ids) + output = _.uniq(ids).join("\n") + "\n" + res.send output + packDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id logger.log doc_id: doc_id, "packing doc history" diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index c7cad2ad9e..2c877f4a0c 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -532,3 +532,7 @@ module.exports = PackManager = logger.log {project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack" db.docHistory.findAndModify {query, update}, callback + listDocs: (options, callback) -> + db.docHistory.find({"op.p":{$exists:true}}, {doc_id:true}).limit (options.limit||100), (err, docs) -> + return callback(err) if err? + callback(null, docs) diff --git a/services/track-changes/pack.sh b/services/track-changes/pack.sh new file mode 100755 index 0000000000..6a389a2f0b --- /dev/null +++ b/services/track-changes/pack.sh @@ -0,0 +1,7 @@ +while docs=$(curl "localhost:3015/doc/list?limit=1000"); do + if [ -z "$docs" ] ; then break ; fi + for d in $docs ; do + echo "packing $d" + curl -X POST "localhost:3015/doc/$d/pack" + done +done From 64545d65407eb804c8743928fcd9a7db06430161 Mon Sep 17 00:00:00 2001 From: Brian - Work Date: Wed, 27 Jan 2016 15:48:36 +0000 Subject: [PATCH 268/549] improve packing script --- services/track-changes/pack.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/services/track-changes/pack.sh b/services/track-changes/pack.sh index 6a389a2f0b..f94a58d077 100755 --- a/services/track-changes/pack.sh +++ b/services/track-changes/pack.sh @@ -1,7 +1,21 @@ -while docs=$(curl "localhost:3015/doc/list?limit=1000"); do +#!/bin/bash + +# find all the docHistories with unpacked ops and pack them + +HOST=${1:-"localhost:3015"} +T=${2:-10} + +echo packing all docHistory on $HOST with delay of $T +for n in $(seq 5 -1 1) ; do + echo starting in $n seconds + sleep 1 +done + +while docs=$(curl "$HOST/doc/list?limit=1000"); do if [ -z "$docs" ] ; then break ; fi for d in $docs ; do echo "packing $d" - curl -X POST "localhost:3015/doc/$d/pack" + curl -X POST "$HOST/doc/$d/pack" + sleep $T done done From 666a07e5ba41e594a049fe5cd476217a587b547f Mon Sep 17 00:00:00 2001 From: Brian - Work Date: Wed, 27 Jan 2016 16:04:55 +0000 Subject: [PATCH 269/549] move lock check into HealthChecker to avoid dependency of HttpController on LockManager in unit tests --- services/track-changes/app/coffee/HealthChecker.coffee | 4 ++++ services/track-changes/app/coffee/HttpController.coffee | 3 +-- .../unit/coffee/HttpController/HttpControllerTests.coffee | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/HealthChecker.coffee b/services/track-changes/app/coffee/HealthChecker.coffee index 87b31bb533..0c3aa47284 100644 --- a/services/track-changes/app/coffee/HealthChecker.coffee +++ b/services/track-changes/app/coffee/HealthChecker.coffee @@ -4,6 +4,7 @@ async = require("async") settings = require("settings-sharelatex") port = settings.internal.trackchanges.port logger = require "logger-sharelatex" +LockManager = require "./LockManager" module.exports = check : (callback)-> @@ -37,3 +38,6 @@ module.exports = cb() ] async.series jobs, callback + + checkLock: (callback) -> + LockManager.healthCheck callback diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 71a3c2a770..2d01d9b52c 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -5,7 +5,6 @@ RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" DocArchiveManager = require "./DocArchiveManager" HealthChecker = require "./HealthChecker" -LockManager = require "./LockManager" _ = require "underscore" module.exports = HttpController = @@ -118,7 +117,7 @@ module.exports = HttpController = res.send 200 checkLock: (req, res)-> - LockManager.healthCheck (err) -> + HealthChecker.checkLock (err) -> if err? logger.err err:err, "error performing lock check" res.send 500 diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index e9533664e3..fefe8286dc 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -14,6 +14,7 @@ describe "HttpController", -> "./RestoreManager": @RestoreManager = {} "./PackManager": @PackManager = {} "./DocArchiveManager": @DocArchiveManager = {} + "./HealthChecker": @HealthChecker = {} @doc_id = "doc-id-123" @project_id = "project-id-123" @next = sinon.stub() From 77cafa36af5b03f384d0d0269d84040836f57222 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 28 Jan 2016 16:40:20 +0000 Subject: [PATCH 270/549] support continuing from last packed doc --- services/track-changes/app/coffee/HttpController.coffee | 3 ++- services/track-changes/app/coffee/PackManager.coffee | 4 +++- services/track-changes/pack.sh | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 2d01d9b52c..0b4cd658e9 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -26,7 +26,8 @@ module.exports = HttpController = listDocs: (req, res, next = (error) ->) -> logger.log "listing packing doc history" limit = +req.query?.limit || 100 - PackManager.listDocs {limit}, (error, doc_ids) -> + doc_id = req.query?.doc_id if req.query?.doc_id?.match(/^[0-9a-f]{24}$/) + PackManager.listDocs {limit, doc_id}, (error, doc_ids) -> return next(error) if error? ids = (doc.doc_id.toString() for doc in doc_ids) output = _.uniq(ids).join("\n") + "\n" diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 2c877f4a0c..7d39c43c8d 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -533,6 +533,8 @@ module.exports = PackManager = db.docHistory.findAndModify {query, update}, callback listDocs: (options, callback) -> - db.docHistory.find({"op.p":{$exists:true}}, {doc_id:true}).limit (options.limit||100), (err, docs) -> + query = {"op.p":{$exists:true}} + query.doc_id = {$gt: ObjectId(options.doc_id)} if options.doc_id? + db.docHistory.find(query, {doc_id:true}).sort({doc_id:1}).limit (options.limit||100), (err, docs) -> return callback(err) if err? callback(null, docs) diff --git a/services/track-changes/pack.sh b/services/track-changes/pack.sh index f94a58d077..9a927af0ca 100755 --- a/services/track-changes/pack.sh +++ b/services/track-changes/pack.sh @@ -11,11 +11,12 @@ for n in $(seq 5 -1 1) ; do sleep 1 done -while docs=$(curl "$HOST/doc/list?limit=1000"); do +while docs=$(curl "$HOST/doc/list?limit=1000&doc_id=$last_doc"); do if [ -z "$docs" ] ; then break ; fi for d in $docs ; do echo "packing $d" curl -X POST "$HOST/doc/$d/pack" sleep $T + last_doc=$d done done From 612079ef8a28f75a1a753ac1acb0aa746ae50523 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 28 Jan 2016 16:56:32 +0000 Subject: [PATCH 271/549] started adding more pack tests and packing of temp ops --- services/track-changes/pack.sh | 4 +++- .../coffee/PackManager/PackManagerTests.coffee | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/services/track-changes/pack.sh b/services/track-changes/pack.sh index 9a927af0ca..c2aad785c7 100755 --- a/services/track-changes/pack.sh +++ b/services/track-changes/pack.sh @@ -1,7 +1,9 @@ -#!/bin/bash +#!/bin/bash -x # find all the docHistories with unpacked ops and pack them +# need to keep track of docs already done + HOST=${1:-"localhost:3015"} T=${2:-10} diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index c538096a0a..e92bd09313 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -230,3 +230,17 @@ describe "PackManager", -> it "should call the callback", -> @callback.called.should.equal true + + describe "convertDocsToPacks", -> + describe "with several small packs", -> + beforeEach -> + @ops = [ + { op: "op-3", meta: "meta-3", v: 3}, + { op: "op-4", meta: "meta-4", v: 4} + ] + @PackManager.convertDocsToPacks @ops, @callback +# it "should create a single pack", -> +# @callback. + it "should call the callback", -> + @callback.called.should.equal true + From a23ddf31c0c0e8937f1b37f63e2f8b8599b45d74 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 29 Jan 2016 12:36:03 +0000 Subject: [PATCH 272/549] allow packing of temporary ops --- .../app/coffee/PackManager.coffee | 13 +- .../PackManager/PackManagerTests.coffee | 180 +++++++++++++++++- 2 files changed, 183 insertions(+), 10 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 7d39c43c8d..29ec6fa584 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -278,17 +278,20 @@ module.exports = PackManager = if d.pack? top = null return - # skip temporary ops (we could pack these into temporary packs in future) - if d.expiresAt? - top = null - return sz = BSON.calculateObjectSize(d) - if top? && top.pack.length < PackManager.MAX_COUNT && top.sz + sz < PackManager.MAX_SIZE + # decide if this doc can be added to the current pack + validLength = top? && (top.pack.length < PackManager.MAX_COUNT) + validSize = top? && (top.sz + sz < PackManager.MAX_SIZE) + bothPermanent = top? && (top.expiresAt? is false) && (d.expiresAt? is false) + bothTemporary = top? && (top.expiresAt? is true) && (d.expiresAt? is true) + within1Day = bothTemporary && (d.meta.start_ts - top.meta.start_ts < 24 * 3600 * 1000) + if top? && validLength && validSize && (bothPermanent || (bothTemporary && within1Day)) top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} top.sz += sz top.n += 1 top.v_end = d.v top.meta.end_ts = d.meta.end_ts + top.expiresAt = d.expiresAt if top.expiresAt? return else # create a new pack diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index e92bd09313..9ac4e5b96b 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -1,5 +1,6 @@ sinon = require('sinon') chai = require('chai') +assert = require('chai').assert should = chai.should() expect = chai.expect modulePath = "../../../../app/js/PackManager.js" @@ -7,6 +8,7 @@ SandboxedModule = require('sandboxed-module') {ObjectId} = require("mongojs") bson = require("bson") BSON = new bson.BSONPure() +_ = require("underscore") tk = require "timekeeper" @@ -232,15 +234,183 @@ describe "PackManager", -> @callback.called.should.equal true describe "convertDocsToPacks", -> - describe "with several small packs", -> + describe "with several small ops", -> beforeEach -> @ops = [ - { op: "op-3", meta: "meta-3", v: 3}, - { op: "op-4", meta: "meta-4", v: 4} + { _id: "3", op: "op-3", meta: {start_ts: "ts3_s", end_ts: "ts3_e", user_id: "u3"}, v: 3}, + { _id: "4", op: "op-4", meta: {start_ts: "ts4_s", end_ts: "ts4_e", user_id: "u4"}, v: 4}, ] @PackManager.convertDocsToPacks @ops, @callback -# it "should create a single pack", -> -# @callback. + + it "should create a single pack", (done) -> + @PackManager.convertDocsToPacks @ops, (err, packs) => + assert.deepEqual packs, [ { + pack: [ + { _id: "3", op: "op-3", meta: {start_ts: "ts3_s", end_ts: "ts3_e", user_id: "u3"}, v: 3}, + { _id: "4", op: "op-4", meta: {start_ts: "ts4_s", end_ts: "ts4_e", user_id: "u4"}, v: 4}, + ] + v: 3 + v_end: 4 + meta: {start_ts: "ts3_s", end_ts: "ts4_e"} + n: 2 + sz: 202 + }] + done() + it "should call the callback", -> @callback.called.should.equal true + describe "with many small ops", -> + beforeEach -> + @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: "ts#{n}_s", end_ts: "ts#{n}_e", user_id: "u#{n}"}, v: n} for n in [0...1024]) + @PackManager.convertDocsToPacks @ops, @callback + + it "should create two packs", (done) -> + @PackManager.convertDocsToPacks @ops, (err, packs) => + assert.deepEqual packs, [ { + pack: @ops[0...512] + v: 0 + v_end: 511 + meta: {start_ts: "ts0_s", end_ts: "ts511_e"} + n: 512 + sz: 56282 + }, { + pack: @ops[512...1024] + v: 512 + v_end: 1023 + meta: {start_ts: "ts512_s", end_ts: "ts1023_e"} + n: 512 + sz: 56952 + }] + done() + + it "should call the callback", -> + @callback.called.should.equal true + + describe "with many temporary ops", -> + beforeEach -> + @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n, end_ts: n+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...1024]) + @PackManager.convertDocsToPacks @ops, @callback + + it "should create two packs", (done) -> + @PackManager.convertDocsToPacks @ops, (err, packs) => + assert.deepEqual packs, [ { + pack: (_.omit(op, "expiresAt") for op in @ops[0...512]) + v: 0 + v_end: 511 + meta: {start_ts: 0 , end_ts: 512} + n: 512 + sz: 55990 + expiresAt: @ops[511].expiresAt + }, { + pack: (_.omit(op, "expiresAt") for op in @ops[512...1024]) + v: 512 + v_end: 1023 + meta: {start_ts: 512, end_ts: 1024} + n: 512 + sz: 56392 + expiresAt: @ops[1023].expiresAt + }] + done() + + it "should call the callback", -> + @callback.called.should.equal true + + describe "with temporary ops spanning more than 1 day", -> + beforeEach -> + @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n*3600*1000, end_ts: n*3600*1000+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...48]) + @PackManager.convertDocsToPacks @ops, @callback + + it "should create two packs", (done) -> + @PackManager.convertDocsToPacks @ops, (err, packs) => + assert.deepEqual packs, [ { + pack: (_.omit(op, "expiresAt") for op in @ops[0...24]) + v: 0 + v_end: 23 + meta: {start_ts: 0 , end_ts: 23*3600*1000+1} + n: 24 + sz: 2538 + expiresAt: @ops[23].expiresAt + }, { + pack: (_.omit(op, "expiresAt") for op in @ops[24...48]) + v: 24 + v_end: 47 + meta: {start_ts: 24*3600*1000, end_ts: 47*3600*1000+1} + n: 24 + sz: 2568 + expiresAt: @ops[47].expiresAt + }] + done() + + it "should call the callback", -> + @callback.called.should.equal true + + describe "with mixture of temporary and permanent ops", -> + beforeEach -> + @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n*3600*1000, end_ts: n*3600*1000+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...48]) + for n in [10...48] + delete @ops[n].expiresAt + @PackManager.convertDocsToPacks @ops, @callback + + it "should create two packs", (done) -> + @PackManager.convertDocsToPacks @ops, (err, packs) => + assert.deepEqual packs, [ { + pack: (_.omit(op, "expiresAt") for op in @ops[0...10]) + v: 0 + v_end: 9 + meta: {start_ts: 0 , end_ts: 9*3600*1000+1} + n: 10 + sz: 1040 + expiresAt: @ops[9].expiresAt + }, { + pack: (_.omit(op, "expiresAt") for op in @ops[10...48]) + v: 10 + v_end: 47 + meta: {start_ts: 10*3600*1000, end_ts: 47*3600*1000+1} + n: 38 + sz: 3496 + }] + done() + + it "should call the callback", -> + @callback.called.should.equal true + + describe "with mixture of temporary and permanent ops and an existing pack", -> + beforeEach -> + @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n*3600*1000, end_ts: n*3600*1000+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...48]) + for n in [10...48] + delete @ops[n].expiresAt + # convert op 16 into a pack + @ops[16].pack = [ @ops[16].op ] + delete @ops[16].op + @PackManager.convertDocsToPacks @ops, @callback + + it "should create three packs", (done) -> + @PackManager.convertDocsToPacks @ops, (err, packs) => + assert.deepEqual packs, [ { + pack: (_.omit(op, "expiresAt") for op in @ops[0...10]) + v: 0 + v_end: 9 + meta: {start_ts: 0 , end_ts: 9*3600*1000+1} + n: 10 + sz: 1040 + expiresAt: @ops[9].expiresAt + }, { + pack: @ops[10...16] + v: 10 + v_end: 15 + meta: {start_ts: 10*3600*1000, end_ts: 15*3600*1000+1} + n: 6 + sz: 552 + }, { + pack: @ops[17...48] + v: 17 + v_end: 47 + meta: {start_ts: 17*3600*1000, end_ts: 47*3600*1000+1} + n: 31 + sz: 2852 + }] + done() + + it "should call the callback", -> + @callback.called.should.equal true From 3d9dfeccc33d79d236a83096ffc64c40a5602bed Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 8 Feb 2016 16:22:42 +0000 Subject: [PATCH 273/549] remove pack worker remove the op-specific code remove tests for ops, now only packing remove unused packing code work in progress store index for completed packs only support archiving and unarchiving of individual packs remove support for archiving whole document history split out ArchiveManager, IndexManager remove old DocArchive code remove docHistoryStats collection comment about archiving added method to look at index when last pack has been archived added start of iterator for project results use a proper iterator added heap module getting it working increase pack size since bulk operations no longer needed remove unused MongoAWSexternal cleanup added doc iterator remove old query code added missing files cleanup clean upclean up started adding pack worker for archiving work in progress work in progress getting pack worker working updating worker getting packworker working added lock use correct key name for track changes aws access use correct key name for track changes aws access always send back users array fix up comparison of retrieved objects handle op ids inside packs log when s3 download completes comments cleanup, remove finalisation ideacleanup, remove finalisation idea remove logging --- services/track-changes/app.coffee | 6 - .../app/coffee/DiffManager.coffee | 1 - .../app/coffee/DocArchiveManager.coffee | 88 -- .../app/coffee/DocstoreHandler.coffee | 21 - .../app/coffee/HttpController.coffee | 34 +- .../track-changes/app/coffee/MongoAWS.coffee | 148 ++-- .../app/coffee/MongoAWSexternal.coffee | 123 --- .../app/coffee/MongoManager.coffee | 102 +-- .../app/coffee/PackManager.coffee | 826 ++++++++---------- .../app/coffee/PackWorker.coffee | 61 +- .../app/coffee/ProjectIterator.coffee | 66 ++ .../app/coffee/UpdatesManager.coffee | 196 ++--- .../track-changes/app/coffee/mongojs.coffee | 2 +- services/track-changes/package.json | 3 +- .../unit/coffee/DocArchive/MongoAWS.coffee | 4 +- .../MongoManager/MongoManagerTests.coffee | 60 -- .../PackManager/PackManagerTests.coffee | 181 ---- .../UpdatesManager/UpdatesManagerTests.coffee | 26 +- 18 files changed, 627 insertions(+), 1321 deletions(-) delete mode 100644 services/track-changes/app/coffee/DocArchiveManager.coffee delete mode 100644 services/track-changes/app/coffee/DocstoreHandler.coffee delete mode 100644 services/track-changes/app/coffee/MongoAWSexternal.coffee create mode 100644 services/track-changes/app/coffee/ProjectIterator.coffee diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 4ed177e91c..f12691027b 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -48,12 +48,6 @@ app.post "/project/:project_id/flush", HttpController.flushProject app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore -app.post "/doc/:doc_id/pack", HttpController.packDoc -app.get "/doc/list", HttpController.listDocs - -app.post '/project/:project_id/archive', HttpController.archiveProject -app.post '/project/:project_id/unarchive', HttpController.unArchiveProject - packWorker = null # use a single packing worker app.post "/pack", (req, res, next) -> diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index bb110b1824..dfdb1a80a2 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -14,7 +14,6 @@ module.exports = DiffManager = callback(null, content, version, updates) getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) -> - logger.log project_id: project_id, doc_id: doc_id, from: fromVersion, to: toVersion, "getting diff" DiffManager.getDocumentBeforeVersion project_id, doc_id, fromVersion, (error, startingContent, updates) -> if error? if error.message == "broken-history" diff --git a/services/track-changes/app/coffee/DocArchiveManager.coffee b/services/track-changes/app/coffee/DocArchiveManager.coffee deleted file mode 100644 index d557278b8e..0000000000 --- a/services/track-changes/app/coffee/DocArchiveManager.coffee +++ /dev/null @@ -1,88 +0,0 @@ -MongoManager = require "./MongoManager" -MongoAWS = require "./MongoAWS" -LockManager = require "./LockManager" -DocstoreHandler = require "./DocstoreHandler" -logger = require "logger-sharelatex" -_ = require "underscore" -async = require "async" -settings = require("settings-sharelatex") - -module.exports = DocArchiveManager = - - archiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> - DocstoreHandler.getAllDocs project_id, (error, docs) -> - if error? - return callback(error) - else if !docs? - return callback new Error("No docs for project #{project_id}") - jobs = _.map docs, (doc) -> - (cb)-> DocArchiveManager.archiveDocChangesWithLock project_id, doc._id, cb - async.series jobs, callback - - archiveDocChangesWithLock: (project_id, doc_id, callback = (error) ->) -> - job = (releaseLock) -> - DocArchiveManager.archiveDocChanges project_id, doc_id, releaseLock - LockManager.runWithLock("HistoryLock:#{doc_id}", job, callback) - - archiveDocChanges: (project_id, doc_id, callback)-> - MongoManager.getArchivedDocStatus doc_id, (error, result) -> - return callback(error) if error? - if result?.inS3 is true - logger.log {project_id, doc_id}, "document history is already archived" - return callback() - if result?.inS3? - logger.log {project_id, doc_id}, "document history archive is already in progress" - return callback() - MongoManager.getDocChangesCount doc_id, (error, count) -> - return callback(error) if error? - if count == 0 - logger.log {project_id, doc_id}, "document history is empty, not archiving" - return callback() - MongoManager.peekLastCompressedUpdate doc_id, (error, update, lastVersion) -> - return callback(error) if error? - logger.log {doc_id, project_id}, "archiving got last compressed update" - MongoManager.markDocHistoryAsArchiveInProgress doc_id, lastVersion, (error) -> - return callback(error) if error? - logger.log {doc_id, project_id}, "marked doc history as archive in progress" - MongoAWS.archiveDocHistory project_id, doc_id, update, (error) -> - if error? - logger.log {doc_id, project_id, error}, "error exporting document to S3" - MongoManager.clearDocHistoryAsArchiveInProgress doc_id, update, (err) -> - return callback(err) if err? - logger.log {doc_id, project_id}, "cleared archive in progress flag" - callback(error) - else - logger.log doc_id:doc_id, project_id:project_id, "exported document to S3" - MongoManager.markDocHistoryAsArchived doc_id, lastVersion, (error) -> - return callback(error) if error? - logger.log {doc_id, project_id}, "marked doc history as archived" - callback() - - unArchiveAllDocsChanges: (project_id, callback = (error, docs) ->) -> - DocstoreHandler.getAllDocs project_id, (error, docs) -> - if error? - return callback(error) - else if !docs? - return callback new Error("No docs for project #{project_id}") - jobs = _.map docs, (doc) -> - (cb)-> DocArchiveManager.unArchiveDocChangesWithLock project_id, doc._id, cb - async.parallelLimit jobs, 4, callback - - unArchiveDocChangesWithLock: (project_id, doc_id, callback = (error) ->) -> - job = (releaseLock) -> - DocArchiveManager.unArchiveDocChanges project_id, doc_id, releaseLock - LockManager.runWithLock("HistoryLock:#{doc_id}", job, callback) - - unArchiveDocChanges: (project_id, doc_id, callback)-> - MongoManager.getArchivedDocStatus doc_id, (error, result) -> - return callback(error) if error? - if result?.inS3 isnt true - logger.log {project_id, doc_id}, "no changes marked as in s3, not unarchiving" - return callback() - else - MongoAWS.unArchiveDocHistory project_id, doc_id, (error) -> - return callback(error) if error? - logger.log doc_id:doc_id, project_id:project_id, "imported document from S3" - MongoManager.markDocHistoryAsUnarchived doc_id, (error) -> - return callback(error) if error? - callback() diff --git a/services/track-changes/app/coffee/DocstoreHandler.coffee b/services/track-changes/app/coffee/DocstoreHandler.coffee deleted file mode 100644 index 9e3b10d258..0000000000 --- a/services/track-changes/app/coffee/DocstoreHandler.coffee +++ /dev/null @@ -1,21 +0,0 @@ -request = require("request").defaults(jar: false) -logger = require "logger-sharelatex" -settings = require "settings-sharelatex" - -module.exports = DocstoreHandler = - - getAllDocs: (project_id, callback = (error) ->) -> - logger.log project_id: project_id, "getting all docs for project in docstore api" - url = "#{settings.apis.docstore.url}/project/#{project_id}/doc" - request.get { - url: url - json: true - }, (error, res, docs) -> - return callback(error) if error? - logger.log {error, res, docs: if docs?.length then docs.map (d) -> d._id else []}, "docstore response" - if 200 <= res.statusCode < 300 - callback(null, docs) - else - error = new Error("docstore api responded with non-success code: #{res.statusCode}") - logger.error err: error, project_id: project_id, "error getting all docs from docstore" - callback(error) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 0b4cd658e9..6c357c505f 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -3,7 +3,6 @@ DiffManager = require "./DiffManager" PackManager = require "./PackManager" RestoreManager = require "./RestoreManager" logger = require "logger-sharelatex" -DocArchiveManager = require "./DocArchiveManager" HealthChecker = require "./HealthChecker" _ = require "underscore" @@ -23,23 +22,6 @@ module.exports = HttpController = return next(error) if error? res.send 204 - listDocs: (req, res, next = (error) ->) -> - logger.log "listing packing doc history" - limit = +req.query?.limit || 100 - doc_id = req.query?.doc_id if req.query?.doc_id?.match(/^[0-9a-f]{24}$/) - PackManager.listDocs {limit, doc_id}, (error, doc_ids) -> - return next(error) if error? - ids = (doc.doc_id.toString() for doc in doc_ids) - output = _.uniq(ids).join("\n") + "\n" - res.send output - - packDoc: (req, res, next = (error) ->) -> - doc_id = req.params.doc_id - logger.log doc_id: doc_id, "packing doc history" - PackManager.packDocHistory doc_id, (error) -> - return next(error) if error? - res.send 204 - checkDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id @@ -68,7 +50,7 @@ module.exports = HttpController = else to = null - logger.log project_id, doc_id: doc_id, from: from, to: to, "getting diff" + logger.log {project_id, doc_id, from, to}, "getting diff" DiffManager.getDiff project_id, doc_id, from, to, (error, diff) -> return next(error) if error? res.send JSON.stringify(diff: diff) @@ -95,20 +77,6 @@ module.exports = HttpController = return next(error) if error? res.send 204 - archiveProject: (req, res, next = (error) ->) -> - project_id = req.params.project_id - logger.log project_id: project_id, "archiving all track changes to s3" - DocArchiveManager.archiveAllDocsChanges project_id, (error) -> - return next(error) if error? - res.send 204 - - unArchiveProject: (req, res, next = (error) ->) -> - project_id = req.params.project_id - logger.log project_id: project_id, "unarchiving all track changes from s3" - DocArchiveManager.unArchiveAllDocsChanges project_id, (error) -> - return next(error) if error? - res.send 204 - healthCheck: (req, res)-> HealthChecker.check (err)-> if err? diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 692e82bca7..cfa256e0f6 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -5,110 +5,106 @@ S3S = require 's3-streams' {db, ObjectId} = require "./mongojs" JSONStream = require "JSONStream" ReadlineStream = require "byline" +zlib = require "zlib" + +DAYS = 24 * 3600 * 1000 # one day in milliseconds + +AWS_CONFIG = { + accessKeyId: settings.trackchanges.s3.key + secretAccessKey: settings.trackchanges.s3.secret +} + +createStream = (streamConstructor, project_id, doc_id, pack_id) -> + return streamConstructor new AWS.S3(AWS_CONFIG), { + "Bucket": settings.trackchanges.stores.doc_history, + "Key": project_id+"/changes-"+doc_id+"/pack-"+pack_id + } module.exports = MongoAWS = - MAX_SIZE: 1024*1024 # almost max size - MAX_COUNT: 512 # almost max count - - archiveDocHistory: (project_id, doc_id, update, _callback = (error) ->) -> + archivePack: (project_id, doc_id, pack_id, _callback = (error) ->) -> callback = (args...) -> _callback(args...) _callback = () -> query = { + _id: ObjectId(pack_id) doc_id: ObjectId(doc_id) - v: {$lte: update.v} - expiresAt: {$exists : false} } - AWS.config.update { - accessKeyId: settings.filestore.s3.key - secretAccessKey: settings.filestore.s3.secret - } + return callback new Error("invalid project id") if not project_id? + return callback new Error("invalid doc id") if not doc_id? + return callback new Error("invalid pack id") if not pack_id? - logger.log {project_id, doc_id}, "uploading data to s3" + logger.log {project_id, doc_id, pack_id}, "uploading data to s3" - upload = S3S.WriteStream new AWS.S3(), { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id - } + upload = createStream S3S.WriteStream, project_id, doc_id, pack_id - db.docHistory.find(query) - .on 'error', (err) -> - callback(err) - .pipe JSONStream.stringify() - .pipe upload - .on 'error', (err) -> - callback(err) - .on 'finish', () -> - return callback(null) - - unArchiveDocHistory: (project_id, doc_id, _callback = (error) ->) -> + db.docHistory.findOne query, (err, result) -> + return callback(err) if err? + return callback new Error("cannot find pack to send to s3") if not result? + return callback new Error("refusing to send pack with TTL to s3") if result.expiresAt? + uncompressedData = JSON.stringify(result) + zlib.gzip uncompressedData, (err, buf) -> + logger.log {project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack" + return callback(err) if err? + upload.on 'error', (err) -> + callback(err) + upload.on 'finish', () -> + logger.log {project_id, doc_id, pack_id}, "upload to s3 completed" + callback(null) + upload.write buf + upload.end() + readArchivedPack: (project_id, doc_id, pack_id, _callback = (error, result) ->) -> callback = (args...) -> _callback(args...) _callback = () -> - AWS.config.update { - accessKeyId: settings.filestore.s3.key - secretAccessKey: settings.filestore.s3.secret - } + return callback new Error("invalid project id") if not project_id? + return callback new Error("invalid doc id") if not doc_id? + return callback new Error("invalid pack id") if not pack_id? - logger.log {project_id, doc_id}, "downloading data from s3" + logger.log {project_id, doc_id, pack_id}, "downloading data from s3" - download = S3S.ReadStream new AWS.S3(), { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id - }, { - encoding: "utf8" - } - - lineStream = new ReadlineStream(); - ops = [] - sz = 0 + download = createStream S3S.ReadStream, project_id, doc_id, pack_id inputStream = download .on 'open', (obj) -> return 1 .on 'error', (err) -> callback(err) - .pipe lineStream - inputStream.on 'data', (line) -> - if line.length > 2 - try - ops.push(JSON.parse(line)) - catch err - return callback(err) - sz += line.length - if ops.length >= MongoAWS.MAX_COUNT || sz >= MongoAWS.MAX_SIZE - inputStream.pause() - MongoAWS.handleBulk ops.slice(0), sz, () -> - inputStream.resume() - ops.splice(0,ops.length) - sz = 0 - .on 'end', () -> - MongoAWS.handleBulk ops, sz, callback - .on 'error', (err) -> + gunzip = zlib.createGunzip() + gunzip.setEncoding('utf8') + gunzip.on 'error', (err) -> + logger.log {project_id, doc_id, pack_id, err}, "error uncompressing gzip stream" + callback(err) + + outputStream = inputStream.pipe gunzip + parts = [] + outputStream.on 'error', (err) -> return callback(err) + outputStream.on 'end', () -> + logger.log {project_id, doc_id, pack_id}, "download from s3 completed" + try + object = JSON.parse parts.join('') + catch e + return callback(e) + object._id = ObjectId(object._id) + object.doc_id = ObjectId(object.doc_id) + object.project_id = ObjectId(object.project_id) + for op in object.pack + op._id = ObjectId(op._id) if op._id? + callback null, object + outputStream.on 'data', (data) -> + parts.push data - handleBulk: (ops, size, cb) -> - bulk = db.docHistory.initializeUnorderedBulkOp(); - - for op in ops - op._id = ObjectId(op._id) - op.doc_id = ObjectId(op.doc_id) - op.project_id = ObjectId(op.project_id) - bulk.find({_id:op._id}).upsert().updateOne(op) - - if ops.length > 0 - bulk.execute (err, result) -> - if err? - logger.error err:err, "error bulking ReadlineStream" - else - logger.log count:ops.length, result:result, size: size, "bulked ReadlineStream" - cb(err) - else - cb() + unArchivePack: (project_id, doc_id, pack_id, callback = (error) ->) -> + MongoAWS.readArchivedPack project_id, doc_id, pack_id, (err, object) -> + return callback(err) if err? + # allow the object to expire, we can always retrieve it again + object.expiresAt = new Date(Date.now() + 7 * DAYS) + logger.log {project_id, doc_id, pack_id}, "inserting object from s3" + db.docHistory.insert object, callback diff --git a/services/track-changes/app/coffee/MongoAWSexternal.coffee b/services/track-changes/app/coffee/MongoAWSexternal.coffee deleted file mode 100644 index f422a583b5..0000000000 --- a/services/track-changes/app/coffee/MongoAWSexternal.coffee +++ /dev/null @@ -1,123 +0,0 @@ -settings = require "settings-sharelatex" -child_process = require "child_process" -mongoUri = require "mongo-uri"; -logger = require "logger-sharelatex" -AWS = require 'aws-sdk' -fs = require 'fs' -S3S = require 's3-streams' - -module.exports = MongoAWSexternal = - - archiveDocHistory: (project_id, doc_id, callback = (error) ->) -> - MongoAWS.mongoExportDocHistory doc_id, (error, filepath) -> - MongoAWS.s3upStream project_id, doc_id, filepath, callback - #delete temp file? - - - unArchiveDocHistory: (project_id, doc_id, callback = (error) ->) -> - MongoAWS.s3downStream project_id, doc_id, (error, filepath) -> - if error == null - MongoAWS.mongoImportDocHistory filepath, callback - #delete temp file? - else - callback - - mongoExportDocHistory: (doc_id, callback = (error, filepath) ->) -> - uriData = mongoUri.parse(settings.mongo.url); - filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonUp' - - args = [] - args.push '-h' - args.push uriData.hosts[0] - args.push '-d' - args.push uriData.database - args.push '-c' - args.push 'docHistory' - args.push '-q' - args.push "{doc_id: ObjectId('#{doc_id}') , expiresAt: {$exists : false} }" - args.push '-o' - args.push filepath - - proc = child_process.spawn "mongoexport", args - - proc.on "error", callback - - stderr = "" - proc.stderr.on "data", (chunk) -> stderr += chunk.toString() - - proc.on "close", (code) -> - if code == 0 - return callback(null,filepath) - else - return callback(new Error("mongodump failed: #{stderr}"),null) - - mongoImportDocHistory: (filepath, callback = (error) ->) -> - - uriData = mongoUri.parse(settings.mongo.url); - - args = [] - args.push '-h' - args.push uriData.hosts[0] - args.push '-d' - args.push uriData.database - args.push '-c' - args.push 'docHistory' - args.push '--file' - args.push filepath - - proc = child_process.spawn "mongoimport", args - - proc.on "error", callback - - stderr = "" - proc.stderr.on "data", (chunk) -> stderr += chunk.toString() - - proc.on "close", (code) -> - if code == 0 - return callback(null,filepath) - else - return callback(new Error("mongodump failed: #{stderr}"),null) - - s3upStream: (project_id, doc_id, filepath, callback = (error) ->) -> - - AWS.config.update { - accessKeyId: settings.filestore.s3.key - secretAccessKey: settings.filestore.s3.secret - } - - upload = S3S.WriteStream new AWS.S3(), { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id - } - - fs.createReadStream(filepath) - .on 'open', (obj) -> - return 1 - .pipe(upload) - .on 'finish', () -> - return callback(null) - .on 'error', (err) -> - return callback(err) - - s3downStream: (project_id, doc_id, callback = (error, filepath) ->) -> - - filepath = settings.path.dumpFolder + '/' + doc_id + '.jsonDown' - - AWS.config.update { - accessKeyId: settings.filestore.s3.key - secretAccessKey: settings.filestore.s3.secret - } - - download = S3S.ReadStream new AWS.S3(), { - "Bucket": settings.filestore.stores.user_files, - "Key": project_id+"/changes-"+doc_id - } - - download - .on 'open', (obj) -> - return 1 - .pipe(fs.createWriteStream(filepath)) - .on 'finish', () -> - return callback(null, filepath) - .on 'error', (err) -> - return callback(err, null) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 13df864c7b..dfb8c2a2e1 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -31,81 +31,11 @@ module.exports = MongoManager = else return callback null, update, update.v else - MongoManager.getArchivedDocStatus doc_id, (error, status) -> + PackManager.getLastPackFromIndex doc_id, (error, pack) -> return callback(error) if error? - return callback(null, null, status.lastVersion) if status?.inS3? and status?.lastVersion? + return callback(null, null, pack.v_end) if pack?.inS3? and pack?.v_end? callback null, null - insertCompressedUpdates: (project_id, doc_id, updates, temporary, callback = (error) ->) -> - jobs = [] - for update in updates - do (update) -> - jobs.push (callback) -> MongoManager.insertCompressedUpdate project_id, doc_id, update, temporary, callback - async.series jobs, (err, results) -> - if not temporary - # keep track of updates to be packed - db.docHistoryStats.update {doc_id:ObjectId(doc_id)}, { - $inc:{update_count:updates.length}, - $currentDate:{last_update:true} - }, {upsert:true}, () -> - callback(err,results) - else - callback(err,results) - - modifyCompressedUpdate: (lastUpdate, newUpdate, callback = (error) ->) -> - return callback() if not newUpdate? - db.docHistory.findAndModify - query: lastUpdate, - update: - $set : - op: newUpdate.op - meta: newUpdate.meta - v: newUpdate.v - new: true - , (err, result, lastErrorObject) -> - return callback(error) if error? - return new Error("could not modify existing op") if not result? - callback(err, result) - - insertCompressedUpdate: (project_id, doc_id, update, temporary, callback = (error) ->) -> - update = { - doc_id: ObjectId(doc_id.toString()) - project_id: ObjectId(project_id.toString()) - op: update.op - meta: update.meta - v: update.v - } - - if temporary - seconds = 1000 - minutes = 60 * seconds - hours = 60 * minutes - days = 24 * hours - update.expiresAt = new Date(Date.now() + 7 * days) - # may need to roll over a pack here if we are inserting packs - db.docHistory.insert update, callback - - getDocUpdates:(doc_id, options = {}, callback = (error, updates) ->) -> - query = - doc_id: ObjectId(doc_id.toString()) - if options.from? - query["v"] ||= {} - query["v"]["$gte"] = options.from - if options.to? - query["v"] ||= {} - query["v"]["$lte"] = options.to - - PackManager.findDocResults(db.docHistory, query, options.limit, callback) - - getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> - query = - project_id: ObjectId(project_id.toString()) - - if options.before? - query["meta.end_ts"] = { $lt: options.before } - - PackManager.findProjectResults(db.docHistory, query, options.limit, callback) - backportProjectId: (project_id, doc_id, callback = (error) ->) -> db.docHistory.update { doc_id: ObjectId(doc_id.toString()) @@ -143,31 +73,3 @@ module.exports = MongoManager = db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } # TTL index for auto deleting week old temporary ops db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true } - # For finding documents which need packing - db.docHistoryStats.ensureIndex { doc_id: 1 }, { background: true } - db.docHistoryStats.ensureIndex { updates: -1, doc_id: 1 }, { background: true } - - getArchivedDocStatus: (doc_id, callback)-> - db.docHistoryStats.findOne {doc_id: ObjectId(doc_id.toString()), inS3: {$exists:true}}, {inS3: true, lastVersion: true}, callback - - getDocChangesCount: (doc_id, callback)-> - db.docHistory.count { doc_id : ObjectId(doc_id.toString())}, callback - - markDocHistoryAsArchiveInProgress: (doc_id, lastVersion, callback) -> - db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, {$set : {inS3: false, lastVersion: lastVersion}}, {upsert:true}, callback - - clearDocHistoryAsArchiveInProgress: (doc_id, update, callback) -> - db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, {$unset : {inS3: true, lastVersion: true}}, callback - - markDocHistoryAsArchived: (doc_id, lastVersion, callback)-> - db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, {$set : {inS3: true}}, {upsert:true}, (error)-> - return callback(error) if error? - # clear the archived entries from the docHistory now we have finally succeeded - db.docHistory.remove { doc_id : ObjectId(doc_id.toString()), v: {$lte : lastVersion}, expiresAt: {$exists : false} }, (error)-> - return callback(error) if error? - callback(error) - - markDocHistoryAsUnarchived: (doc_id, callback)-> - # note this removes any inS3 field, regardless of its value (true/false/null) - db.docHistoryStats.update {doc_id: ObjectId(doc_id.toString())}, { $unset : { inS3: true, lastVersion: true} }, (error)-> - callback(error) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 29ec6fa584..4eba1bff2d 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -3,463 +3,49 @@ _ = require "underscore" {db, ObjectId, BSON} = require "./mongojs" logger = require "logger-sharelatex" LockManager = require "./LockManager" +MongoAWS = require "./MongoAWS" +ProjectIterator = require "./ProjectIterator" + +# Sharejs operations are stored in a 'pack' object +# +# e.g. a single sharejs update looks like +# +# { +# "doc_id" : 549dae9e0a2a615c0c7f0c98, +# "project_id" : 549dae9c0a2a615c0c7f0c8c, +# "op" : [ {"p" : 6981, "d" : "?" } ], +# "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, +# "v" : 17082 +# } +# +# and a pack looks like this +# +# { +# "doc_id" : 549dae9e0a2a615c0c7f0c98, +# "project_id" : 549dae9c0a2a615c0c7f0c8c, +# "pack" : [ U1, U2, U3, ...., UN], +# "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, +# "v" : 17082 +# "v_end" : ... +# } +# +# where U1, U2, U3, .... are single updates stripped of their +# doc_id and project_id fields (which are the same for all the +# updates in the pack). +# +# The pack itself has v and meta fields, this makes it possible to +# treat packs and single updates in a similar way. +# +# The v field of the pack itself is from the first entry U1, the +# v_end field from UN. The meta.end_ts field of the pack itself is +# from the last entry UN, the meta.start_ts field from U1. DAYS = 24 * 3600 * 1000 # one day in milliseconds module.exports = PackManager = - # The following functions implement methods like a mongo find, but - # expands any documents containing a 'pack' field into multiple - # values - # - # e.g. a single update looks like - # - # { - # "doc_id" : 549dae9e0a2a615c0c7f0c98, - # "project_id" : 549dae9c0a2a615c0c7f0c8c, - # "op" : [ {"p" : 6981, "d" : "?" } ], - # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, - # "v" : 17082 - # } - # - # and a pack looks like this - # - # { - # "doc_id" : 549dae9e0a2a615c0c7f0c98, - # "project_id" : 549dae9c0a2a615c0c7f0c8c, - # "pack" : [ U1, U2, U3, ...., UN], - # "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, - # "v" : 17082 - # } - # - # where U1, U2, U3, .... are single updates stripped of their - # doc_id and project_id fields (which are the same for all the - # updates in the pack). - # - # The pack itself has v and meta fields, this makes it possible to - # treat packs and single updates in the same way. - # - # The v field of the pack itself is from the first entry U1 - # The meta.end_ts field of the pack itself is from the last entry UN. - - findDocResults: (collection, query, limit, callback) -> - # query - the mongo query selector, includes both the doc_id/project_id and - # the range on v - # limit - the mongo limit, we need to apply it after unpacking any - # packs - - sort = {} - sort['v'] = -1; - cursor = collection - .find( query ) - .sort( sort ) - # if we have packs, we will trim the results more later after expanding them - if limit? - cursor.limit(limit) - - # take the part of the query which selects the range over the parameter - rangeQuery = query['v'] - - # helper function to check if an item from a pack is inside the - # desired range - filterFn = (item) -> - return false if rangeQuery?['$gte']? && item['v'] < rangeQuery['$gte'] - return false if rangeQuery?['$lte']? && item['v'] > rangeQuery['$lte'] - return false if rangeQuery?['$lt']? && item['v'] >= rangeQuery['$lt'] - return false if rangeQuery?['$gt']? && item['v'] <= rangeQuery['$gt'] - return true - - versionOrder = (a, b) -> - b.v - a.v - - # create a query which can be used to select the entries BEFORE - # the range because we sometimes need to find extra ones (when the - # boundary falls in the middle of a pack) - extraQuery = _.clone(query) - # The pack uses its first entry for its metadata and v, so the - # only queries where we might not get all the packs are those for - # $gt and $gte (i.e. we need to find packs which start before our - # range but end in it) - if rangeQuery?['$gte']? - extraQuery['v'] = {'$lt' : rangeQuery['$gte']} - else if rangeQuery?['$gt'] - extraQuery['v'] = {'$lte' : rangeQuery['$gt']} - else - delete extraQuery['v'] - - needMore = false # keep track of whether we need to load more data - updates = [] # used to accumulate the set of results - - # FIXME: packs are big so we should accumulate the results - # incrementally instead of using .toArray() to avoid reading all - # of the changes into memory - cursor.toArray (err, result) -> - unpackedSet = PackManager._unpackResults(result) - updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) - # check if we need to retrieve more data, because there is a - # pack that crosses into our range - last = if unpackedSet.length then unpackedSet[unpackedSet.length-1] else null - if limit? && updates.length == limit - needMore = false - else if extraQuery['v']? && last? && filterFn(last) - needMore = true - else if extraQuery['v']? && updates.length == 0 - needMore = true - if needMore - # we do need an extra result set - extra = collection - .find(extraQuery) - .sort(sort) - .limit(1) - extra.toArray (err, result2) -> - if err? - return callback err, updates.sort versionOrder - else - extraSet = PackManager._unpackResults(result2) - updates = PackManager._filterAndLimit(updates, extraSet, filterFn, limit) - callback err, updates.sort versionOrder - return - if err? - callback err, result - else - callback err, updates.sort versionOrder - - findProjectResults: (collection, query, limit, callback) -> - # query - the mongo query selector, includes both the doc_id/project_id and - # the range on meta.end_ts - # limit - the mongo limit, we need to apply it after unpacking any - # packs - - sort = {} - sort['meta.end_ts'] = -1; - - projection = {"op":false, "pack.op": false} - cursor = collection - .find( query, projection ) # no need to return the op only need version info - .sort( sort ) - # if we have packs, we will trim the results more later after expanding them - if limit? - cursor.limit(limit) - - # take the part of the query which selects the range over the parameter - before = query['meta.end_ts']?['$lt'] # may be null - - updates = [] # used to accumulate the set of results - - # FIXME: packs are big so we should accumulate the results - # incrementally instead of using .toArray() to avoid reading all - # of the changes into memory - cursor.toArray (err, result) -> - if err? - return callback err, result - if result.length == 0 && not before? # no results and no time range specified - return callback err, result - - unpackedSet = PackManager._unpackResults(result) - if limit? - unpackedSet = unpackedSet.slice(0, limit) - # find the end time of the last result, we will take all the - # results up to this, and then all the changes at that time - # (without imposing a limit) and any overlapping packs - cutoff = if unpackedSet.length then unpackedSet[unpackedSet.length-1].meta.end_ts else null - - filterFn = (item) -> - ts = item?.meta?.end_ts - return false if before? && ts >= before - return false if cutoff? && ts < cutoff - return true - - timeOrder = (a, b) -> - (b.meta.end_ts - a.meta.end_ts) || documentOrder(a, b) - - documentOrder = (a, b) -> - x = a.doc_id.valueOf() - y = b.doc_id.valueOf() - if x > y then 1 else if x < y then -1 else 0 - - updates = PackManager._filterAndLimit(updates, unpackedSet, filterFn, limit) - - # get all elements on the lower bound (cutoff) - tailQuery = _.clone(query) - tailQuery['meta.end_ts'] = cutoff - tail = collection - .find(tailQuery, projection) - .sort(sort) - - # now find any packs that overlap with the time window from outside - # cutoff before - # --|-----wanted-range--|------------------ time=> - # |-------------|pack(end_ts) - # - # these were not picked up by the original query because - # end_ts>before but the beginning of the pack may be in the time range - overlapQuery = _.clone(query) - if before? && cutoff? - overlapQuery['meta.end_ts'] = {"$gte": before} - overlapQuery['meta.start_ts'] = {"$lte": before } - else if before? && not cutoff? - overlapQuery['meta.end_ts'] = {"$gte": before} - overlapQuery['meta.start_ts'] = {"$lte": before } - else if not before? && cutoff? - overlapQuery['meta.end_ts'] = {"$gte": cutoff} # we already have these?? - else if not before? && not cutoff? - overlapQuery['meta.end_ts'] = {"$gte": 0 } # shouldn't happen?? - - overlap = collection - .find(overlapQuery, projection) - .sort(sort) - - # we don't specify a limit here, as there could be any number of overlaps - # NB. need to catch items in original query and followup query for duplicates - - applyAndUpdate = (result) -> - extraSet = PackManager._unpackResults(result) - # note: final argument is null, no limit applied because we - # need all the updates at the final time to avoid breaking - # the changeset into parts - updates = PackManager._filterAndLimit(updates, extraSet, filterFn, null) - tail.toArray (err, result2) -> - if err? - return callback err, updates.sort timeOrder - else - applyAndUpdate result2 - overlap.toArray (err, result3) -> - if err? - return callback err, updates.sort timeOrder - else - applyAndUpdate result3 - callback err, updates.sort timeOrder - - _unpackResults: (updates) -> - # iterate over the updates, if there's a pack, expand it into ops and - # insert it into the array at that point - result = [] - updates.forEach (item) -> - if item.pack? - all = PackManager._explodePackToOps item - result = result.concat all - else - result.push item - return result - - _explodePackToOps: (packObj) -> - # convert a pack into an array of ops - doc_id = packObj.doc_id - project_id = packObj.project_id - result = packObj.pack.map (item) -> - item.doc_id = doc_id - item.project_id = project_id - item - return result.reverse() - - _filterAndLimit: (results, extra, filterFn, limit) -> - # update results with extra docs, after filtering and limiting - filtered = extra.filter(filterFn) - newResults = results.concat filtered - # remove duplicates - seen = {} - newResults = newResults.filter (item) -> - key = item.doc_id + ' ' + item.v - if seen[key] - return false - else - seen[key] = true - return true - newResults.slice(0, limit) if limit? - return newResults MAX_SIZE: 1024*1024 # make these configurable parameters - MAX_COUNT: 512 - - convertDocsToPacks: (docs, callback) -> - packs = [] - top = null - docs.forEach (d,i) -> - # skip existing packs - if d.pack? - top = null - return - sz = BSON.calculateObjectSize(d) - # decide if this doc can be added to the current pack - validLength = top? && (top.pack.length < PackManager.MAX_COUNT) - validSize = top? && (top.sz + sz < PackManager.MAX_SIZE) - bothPermanent = top? && (top.expiresAt? is false) && (d.expiresAt? is false) - bothTemporary = top? && (top.expiresAt? is true) && (d.expiresAt? is true) - within1Day = bothTemporary && (d.meta.start_ts - top.meta.start_ts < 24 * 3600 * 1000) - if top? && validLength && validSize && (bothPermanent || (bothTemporary && within1Day)) - top.pack = top.pack.concat {v: d.v, meta: d.meta, op: d.op, _id: d._id} - top.sz += sz - top.n += 1 - top.v_end = d.v - top.meta.end_ts = d.meta.end_ts - top.expiresAt = d.expiresAt if top.expiresAt? - return - else - # create a new pack - top = _.clone(d) - top.pack = [ {v: d.v, meta: d.meta, op: d.op, _id: d._id} ] - top.meta = { start_ts: d.meta.start_ts, end_ts: d.meta.end_ts } - top.sz = sz - top.n = 1 - top.v_end = d.v - delete top.op - delete top._id - packs.push top - - callback(null, packs) - - checkHistory: (docs, callback) -> - errors = [] - prev = null - error = (args...) -> - errors.push args - docs.forEach (d,i) -> - if d.pack? - n = d.pack.length - last = d.pack[n-1] - error('bad pack v_end', d) if d.v_end != last.v - error('bad pack start_ts', d) if d.meta.start_ts != d.pack[0].meta.start_ts - error('bad pack end_ts', d) if d.meta.end_ts != last.meta.end_ts - d.pack.forEach (p, i) -> - prev = v - v = p.v - error('bad version', v, 'in', p) if v <= prev - #error('expired op', p, 'in pack') if p.expiresAt? - else - prev = v - v = d.v - error('bad version', v, 'in', d) if v <= prev - if errors.length - callback(errors) - else - callback() - - insertPack: (packObj, callback) -> - bulk = db.docHistory.initializeOrderedBulkOp() - doc_id = packObj.doc_id - expect_nInserted = 1 - expect_nRemoved = packObj.pack.length - logger.log {doc_id: doc_id}, "adding pack, removing #{expect_nRemoved} ops" - bulk.insert packObj - ids = (op._id for op in packObj.pack) - bulk.find({_id:{$in:ids}}).remove() - bulk.execute (err, result) -> - if err? - logger.error {doc_id: doc_id}, "error adding pack" - callback(err, result) - else if result.nInserted != expect_nInserted or result.nRemoved != expect_nRemoved - logger.error {doc_id: doc_id, result}, "unexpected result adding pack" - callback(new Error( - msg: 'unexpected result' - expected: {expect_nInserted, expect_nRemoved} - ), result) - else - db.docHistoryStats.update {doc_id:doc_id}, { - $inc:{update_count:-expect_nRemoved}, - $currentDate:{last_packed:true} - }, {upsert:true}, () -> - callback(err, result) - - # retrieve document ops/packs and check them - getDocHistory: (doc_id, callback) -> - db.docHistory.find({doc_id:ObjectId(doc_id)}).sort {v:1}, (err, docs) -> - return callback(err) if err? - # for safety, do a consistency check of the history - logger.log {doc_id}, "checking history for document" - PackManager.checkHistory docs, (err) -> - return callback(err) if err? - callback(err, docs) - #PackManager.deleteExpiredPackOps docs, (err) -> - # return callback(err) if err? - # callback err, docs - - packDocHistory: (doc_id, options, callback) -> - if typeof callback == "undefined" and typeof options == 'function' - callback = options - options = {} - LockManager.runWithLock( - "HistoryLock:#{doc_id}", - (releaseLock) -> - PackManager._packDocHistory(doc_id, options, releaseLock) - , callback - ) - - _packDocHistory: (doc_id, options, callback) -> - logger.log {doc_id},"starting pack operation for document history" - - PackManager.getDocHistory doc_id, (err, docs) -> - return callback(err) if err? - origDocs = 0 - origPacks = 0 - for d in docs - if d.pack? then origPacks++ else origDocs++ - PackManager.convertDocsToPacks docs, (err, packs) -> - return callback(err) if err? - total = 0 - for p in packs - total = total + p.pack.length - logger.log {doc_id, origDocs, origPacks, newPacks: packs.length, totalOps: total}, "document stats" - if packs.length - if options['dry-run'] - logger.log {doc_id}, 'dry-run, skipping write packs' - return callback() - PackManager.savePacks packs, (err) -> - return callback(err) if err? - # check the history again - PackManager.getDocHistory doc_id, callback - else - logger.log {doc_id}, "no packs to write" - # keep a record that we checked this one to avoid rechecking it - db.docHistoryStats.update {doc_id:doc_id}, { - $currentDate:{last_checked:true} - }, {upsert:true}, () -> - callback null, null - - DB_WRITE_DELAY: 100 - - savePacks: (packs, callback) -> - async.eachSeries packs, PackManager.safeInsert, (err, result) -> - if err? - logger.log {err, result}, "error writing packs" - callback err, result - else - callback() - - safeInsert: (packObj, callback) -> - PackManager.insertPack packObj, (err, result) -> - setTimeout () -> - callback(err,result) - , PackManager.DB_WRITE_DELAY - - deleteExpiredPackOps: (docs, callback) -> - now = Date.now() - toRemove = [] - toUpdate = [] - docs.forEach (d,i) -> - if d.pack? - newPack = d.pack.filter (op) -> - if op.expiresAt? then op.expiresAt > now else true - if newPack.length == 0 - toRemove.push d - else if newPack.length < d.pack.length - # adjust the pack properties - d.pack = newPack - first = d.pack[0] - last = d.pack[d.pack.length - 1] - d.v_end = last.v - d.meta.start_ts = first.meta.start_ts - d.meta.end_ts = last.meta.end_ts - toUpdate.push d - if toRemove.length or toUpdate.length - bulk = db.docHistory.initializeOrderedBulkOp() - toRemove.forEach (pack) -> - console.log "would remove", pack - #bulk.find({_id:pack._id}).removeOne() - toUpdate.forEach (pack) -> - console.log "would update", pack - #bulk.find({_id:pack._id}).updateOne(pack); - bulk.execute callback - else - callback() + MAX_COUNT: 1024 insertCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> return callback() if newUpdates.length == 0 @@ -509,7 +95,12 @@ module.exports = PackManager = if temporary newPack.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" - db.docHistory.insert newPack, callback + db.docHistory.save newPack, (err, result) -> + return callback(err) if err? + if temporary + return callback() + else + PackManager.updateIndex project_id, doc_id, callback appendUpdatesToExistingPack: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> first = newUpdates[0] @@ -533,11 +124,330 @@ module.exports = PackManager = if lastUpdate.expiresAt and temporary update.$set.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack" - db.docHistory.findAndModify {query, update}, callback + db.docHistory.findAndModify {query, update, new:true, fields:{meta:1,v_end:1}}, callback - listDocs: (options, callback) -> - query = {"op.p":{$exists:true}} - query.doc_id = {$gt: ObjectId(options.doc_id)} if options.doc_id? - db.docHistory.find(query, {doc_id:true}).sort({doc_id:1}).limit (options.limit||100), (err, docs) -> + # Retrieve all changes for a document + + getOpsByVersionRange: (project_id, doc_id, fromVersion, toVersion, callback = (error, updates) ->) -> + PackManager.loadPacksByVersionRange project_id, doc_id, fromVersion, toVersion, (error) -> + query = {doc_id:ObjectId(doc_id.toString())} + query.v = {$lte:toVersion} if toVersion? + query.v_end = {$gte:fromVersion} if fromVersion? + #console.log "query:", query + db.docHistory.find(query).sort {v:-1}, (err, result) -> + return callback(err) if err? + #console.log "getOpsByVersionRange:", err, result + updates = [] + opInRange = (op, from, to) -> + return false if fromVersion? and op.v < fromVersion + return false if toVersion? and op.v > toVersion + return true + for docHistory in result + #console.log 'adding', docHistory.pack + for op in docHistory.pack.reverse() when opInRange(op, fromVersion, toVersion) + op.project_id = docHistory.project_id + op.doc_id = docHistory.doc_id + #console.log "added op", op.v, fromVersion, toVersion + updates.push op + callback(null, updates) + + loadPacksByVersionRange: (project_id, doc_id, fromVersion, toVersion, callback) -> + PackManager.getIndex doc_id, (err, indexResult) -> return callback(err) if err? - callback(null, docs) + indexPacks = indexResult?.packs or [] + packInRange = (pack, from, to) -> + return false if fromVersion? and pack.v_end < fromVersion + return false if toVersion? and pack.v > toVersion + return true + neededIds = (pack._id for pack in indexPacks when packInRange(pack, fromVersion, toVersion)) + PackManager.fetchPacksIfNeeded project_id, doc_id, neededIds, callback + + fetchPacksIfNeeded: (project_id, doc_id, pack_ids, callback) -> + db.docHistory.find {_id: {$in: (ObjectId(id) for id in pack_ids)}}, {_id:1}, (err, loadedPacks) -> + return callback(err) if err? + allPackIds = (id.toString() for id in pack_ids) + loadedPackIds = (pack._id.toString() for pack in loadedPacks) + packIdsToFetch = _.difference allPackIds, loadedPackIds + logger.log {loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs" + async.eachLimit packIdsToFetch, 4, (pack_id, cb) -> + MongoAWS.unArchivePack project_id, doc_id, pack_id, cb + , (err) -> + return callback(err) if err? + logger.log "done unarchiving" + callback() + + # Retrieve all changes across a project + + makeProjectIterator: (project_id, before, callback) -> + # get all the docHistory Entries + db.docHistory.find({project_id: ObjectId(project_id)},{pack:false}).sort {"meta.end_ts":-1}, (err, packs) -> + return callback(err) if err? + allPacks = [] + seenIds = {} + for pack in packs + allPacks.push pack + seenIds[pack._id] = true + db.docHistoryIndex.find {project_id: ObjectId(project_id)}, (err, indexes) -> + return callback(err) if err? + for index in indexes + for pack in index.packs when not seenIds[pack._id] + pack.project_id = index.project_id + pack.doc_id = index._id + pack.fromIndex = true + allPacks.push pack + seenIds[pack._id] = true + callback(null, new ProjectIterator(allPacks, before, PackManager.getPackById)) + + getPackById: (project_id, doc_id, pack_id, callback) -> + db.docHistory.findOne {_id: pack_id}, (err, pack) -> + return callback(err) if err? + if not pack? + MongoAWS.unArchivePack project_id, doc_id, pack_id, callback + else if pack.expiresAt? + # we only need to touch the TTL on the listing of changes in the project + # because diffs on individual documents are always done after that + PackManager.increaseTTL pack, callback + else + callback(null, pack) + + increaseTTL: (pack, callback) -> + if pack.expiresAt < new Date(Date.now() + 6 * DAYS) + # update cache expiry since we are using this pack + db.docHistory.findAndModify { + query: {_id: pack._id} + update: {$set: {expiresAt: new Date(Date.now() + 7 * DAYS)}} + }, (err) -> + return callback(err, pack) + else + callback(null, pack) + + # Manage docHistoryIndex collection + + getIndex: (doc_id, callback) -> + db.docHistoryIndex.findOne {_id:ObjectId(doc_id.toString())}, callback + + getPackFromIndex: (doc_id, pack_id, callback) -> + db.docHistoryIndex.findOne {_id:ObjectId(doc_id.toString()), "packs._id": pack_id}, {"packs.$":1}, callback + + getLastPackFromIndex: (doc_id, callback) -> + db.docHistoryIndex.findOne {_id: ObjectId(doc_id.toString())}, {packs:{$slice:-1}}, (err, indexPack) -> + return callback(err) if err? + return callback() if not indexPack? + callback(null,indexPack[0]) + + getIndexWithKeys: (doc_id, callback) -> + PackManager.getIndex doc_id, (err, index) -> + return callback(err) if err? + return callback() if not index? + for pack in index?.packs or [] + index[pack._id] = pack + callback(null, index) + + initialiseIndex: (project_id, doc_id, callback) -> + PackManager.findCompletedPacks project_id, doc_id, (err, packs) -> + #console.log 'err', err, 'packs', packs, packs?.length + return callback(err) if err? + return callback() if not packs? + PackManager.insertPacksIntoIndexWithLock project_id, doc_id, packs, callback + + updateIndex: (project_id, doc_id, callback) -> + # find all packs prior to current pack + PackManager.findUnindexedPacks project_id, doc_id, (err, newPacks) -> + return callback(err) if err? + return callback() if not newPacks? + PackManager.insertPacksIntoIndexWithLock project_id, doc_id, newPacks, (err) -> + return callback(err) if err? + logger.log {project_id, doc_id, newPacks}, "added new packs to index" + callback() + + findCompletedPacks: (project_id, doc_id, callback) -> + query = { doc_id: ObjectId(doc_id.toString()) } + db.docHistory.find(query, {pack:false}).sort {v:1}, (err, packs) -> + return callback(err) if err? + return callback() if not packs? + return callback() if packs?.length <= 1 + packs.pop() # discard the last pack, it's still in progress + callback(null, packs) + + # findPacks: (project_id, doc_id, queryFilter, callback) -> + # query = { doc_id: ObjectId(doc_id.toString()) } + # query = _.defaults query, queryFilter if queryFilter? + # db.docHistory.find(query, {pack:false}).sort {v:1}, callback + + findUnindexedPacks: (project_id, doc_id, callback) -> + PackManager.getIndexWithKeys doc_id, (err, indexResult) -> + return callback(err) if err? + PackManager.findCompletedPacks project_id, doc_id, (err, historyPacks) -> + return callback(err) if err? + return callback() if not historyPacks? + # select only the new packs not already in the index + newPacks = (pack for pack in historyPacks when not indexResult[pack._id]?) + newPacks = (_.omit(pack, 'doc_id', 'project_id', 'n', 'sz') for pack in newPacks) + logger.log {project_id, doc_id, n: newPacks.length}, "found new packs" + callback(null, newPacks) + + insertPacksIntoIndexWithLock: (project_id, doc_id, newPacks, callback) -> + LockManager.runWithLock( + "HistoryIndexLock:#{doc_id}", + (releaseLock) -> + PackManager._insertPacksIntoIndex project_id, doc_id, newPacks, releaseLock + callback + ) + + _insertPacksIntoIndex: (project_id, doc_id, newPacks, callback) -> + db.docHistoryIndex.findAndModify { + query: {_id:ObjectId(doc_id.toString())} + update: + $setOnInsert: project_id: ObjectId(project_id.toString()) + $push: + packs: {$each: newPacks, $sort: {v: 1}} + upsert: true + }, callback + + # Archiving packs to S3 + + archivePack: (project_id, doc_id, pack_id, callback) -> + clearFlagOnError = (err, cb) -> + if err? # clear the inS3 flag on error + PackManager.clearPackAsArchiveInProgress project_id, doc_id, pack_id, (err2) -> + return cb(err2) if err2? + return cb(err) + else + cb() + async.series [ + (cb) -> + PackManager.checkArchiveNotInProgress project_id, doc_id, pack_id, cb + (cb) -> + PackManager.markPackAsArchiveInProgress project_id, doc_id, pack_id, cb + (cb) -> + MongoAWS.archivePack project_id, doc_id, pack_id, (err) -> + clearFlagOnError(err, cb) + (cb) -> + PackManager.checkArchivedPack project_id, doc_id, pack_id, (err) -> + clearFlagOnError(err, cb) + (cb) -> + PackManager.markPackAsArchived project_id, doc_id, pack_id, cb + (cb) -> + PackManager.setTTLOnArchivedPack project_id, doc_id, pack_id, callback + ], callback + + + checkArchivedPack: (project_id, doc_id, pack_id, callback) -> + db.docHistory.findOne {_id: pack_id}, (err, pack) -> + return callback(err) if err? + return callback new Error("pack not found") if not pack? + MongoAWS.readArchivedPack project_id, doc_id, pack_id, (err, result) -> + delete result.last_checked + delete pack.last_checked + # need to compare ids as ObjectIds with .equals() + for key in ['_id', 'project_id', 'doc_id'] + result[key] = pack[key] if result[key].equals(pack[key]) + for op, i in result.pack + op._id = pack.pack[i]._id if op._id? and op._id.equals(pack.pack[i]._id) + if _.isEqual pack, result + callback() + else + logger.err {pack, result, jsondiff: JSON.stringify(pack) is JSON.stringify(result)}, "difference when comparing packs" + callback new Error("pack retrieved from s3 does not match pack in mongo") + + # Processing old packs via worker + + processOldPack: (project_id, doc_id, pack_id, callback) -> + markAsChecked = (err) -> + PackManager.markPackAsChecked project_id, doc_id, pack_id, (err2) -> + return callback(err2) if err2? + callback(err) + logger.log {project_id, doc_id}, "processing old packs" + db.docHistory.findOne {_id:pack_id}, (err, pack) -> + return markAsChecked(err) if err? + return markAsChecked() if not pack? + return callback() if pack.expiresAt? # return directly + PackManager.updateIndexIfNeeded project_id, doc_id, (err) -> + return markAsChecked(err) if err? + PackManager.findUnarchivedPacks project_id, doc_id, (err, unarchivedPacks) -> + return markAsChecked(err) if err? + if not unarchivedPacks?.length + logger.log "no packs need archiving" + return markAsChecked() + async.eachSeries unarchivedPacks, (pack, cb) -> + PackManager.archivePack project_id, doc_id, pack._id, cb + , (err) -> + return markAsChecked(err) if err? + logger.log "done processing" + markAsChecked() + + updateIndexIfNeeded: (project_id, doc_id, callback) -> + logger.log {project_id, doc_id}, "archiving old packs" + PackManager.getIndexWithKeys doc_id, (err, index) -> + return callback(err) if err? + if not index? + PackManager.initialiseIndex project_id, doc_id, callback + else + PackManager.updateIndex project_id, doc_id, callback + + markPackAsChecked: (project_id, doc_id, pack_id, callback) -> + logger.log {project_id, doc_id, pack_id}, "marking pack as checked" + db.docHistory.findAndModify { + query: {_id: pack_id} + update: {$currentDate: {"last_checked":true}} + }, callback + + findUnarchivedPacks: (project_id, doc_id, callback) -> + PackManager.getIndex doc_id, (err, indexResult) -> + return callback(err) if err? + indexPacks = indexResult?.packs or [] + unArchivedPacks = (pack for pack in indexPacks when not pack.inS3?) + logger.log {project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs" + callback(null, unArchivedPacks) + + # Archive locking flags + + checkArchiveNotInProgress: (project_id, doc_id, pack_id, callback) -> + logger.log {project_id, doc_id, pack_id}, "checking if archive in progress" + PackManager.getPackFromIndex doc_id, pack_id, (err, result) -> + return callback(err) if err? + return callback new Error("pack not found in index") if not result? + if result.inS3? + callback new Error("pack archiving already in progress") + else + callback() + + markPackAsArchiveInProgress: (project_id, doc_id, pack_id, callback) -> + logger.log {project_id, doc_id}, "marking pack as archive in progress status" + db.docHistoryIndex.findAndModify { + query: {_id:ObjectId(doc_id.toString()), packs: {$elemMatch: {"_id": pack_id, inS3: {$exists:false}}}} + fields: { "packs.$": 1 } + update: {$set: {"packs.$.inS3":false}} + }, (err, result) -> + return callback(err) if err? + return callback new Error("archive is already in progress") if not result? + logger.log {project_id, doc_id, pack_id}, "marked as archive in progress" + callback() + + clearPackAsArchiveInProgress: (project_id, doc_id, pack_id, callback) -> + logger.log {project_id, doc_id, pack_id}, "clearing as archive in progress" + db.docHistoryIndex.findAndModify { + query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}} + fields: { "packs.$": 1 } + update: {$unset: {"packs.$.inS3":true}} + }, callback + + markPackAsArchived: (project_id, doc_id, pack_id, callback) -> + logger.log {project_id, doc_id, pack_id}, "marking pack as archived" + db.docHistoryIndex.findAndModify { + query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}} + fields: { "packs.$": 1 } + update: {$set: {"packs.$.inS3":true}} + }, (err, result) -> + return callback(err) if err? + return callback new Error("archive is not marked as progress") if not result? + logger.log {project_id, doc_id, pack_id}, "marked as archived" + callback() + + setTTLOnArchivedPack: (project_id, doc_id, pack_id, callback) -> + db.docHistory.findAndModify { + query: {_id: pack_id} + update: {$set: {expiresAt: new Date(Date.now() + 1*DAYS)}} + }, (err) -> + logger.log {project_id, doc_id, pack_id}, "set expiry on pack" + callback() diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 7b37969229..a6d31b8436 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -7,11 +7,13 @@ logger.initialize("track-changes-packworker") if Settings.sentry?.dsn? logger.initializeErrorReporting(Settings.sentry.dsn) +DAYS = 24 * 3600 * 1000 + LockManager = require "./LockManager" PackManager = require "./PackManager" # this worker script is forked by the main process to look for -# document histories which can be packed +# document histories which can be archived LIMIT = Number(process.argv[2]) || 1000 DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 @@ -24,7 +26,7 @@ shutDownTimer = setTimeout () -> shutDownRequested = true # do a hard shutdown after a further 5 minutes setTimeout () -> - logger.error "HARD TIMEOUT in pack worker" + logger.error "HARD TIMEOUT in pack archive worker" process.exit() , 5*60*1000 , TIMEOUT @@ -47,54 +49,59 @@ finish = () -> db.close () -> logger.log 'closing LockManager Redis Connection' LockManager.close () -> - logger.log 'ready to exit from pack worker' + logger.log 'ready to exit from pack archive worker' hardTimeout = setTimeout () -> - logger.error 'hard exit from pack worker' + logger.error 'hard exit from pack archive worker' process.exit(1) , 5*1000 hardTimeout.unref() process.on 'exit', (code) -> - logger.log {code}, 'pack worker exited' + logger.log {code}, 'pack archive worker exited' processUpdates = (pending) -> - async.eachSeries pending, (doc_id, callback) -> - PackManager.packDocHistory doc_id, (err, result) -> + async.eachSeries pending, (result, callback) -> + {_id, project_id, doc_id} = result + if not project_id? or not doc_id? + logger.log {project_id, doc_id}, "skipping pack, missing project/doc id" + return callback() + PackManager.processOldPack project_id, doc_id, _id, (err, result) -> if err? - logger.error {err, result}, "error in pack worker" + logger.error {err, result}, "error in pack archive worker" return callback(err) if shutDownRequested - logger.error "shutting down pack worker" + logger.error "shutting down pack archive worker" return callback(new Error("shutdown")) setTimeout () -> callback(err, result) , DOCUMENT_PACK_DELAY , (err, results) -> if err? and err.message != "shutdown" - logger.error {err}, 'error in pack worker processUpdates' + logger.error {err}, 'error in pack archive worker processUpdates' finish() -# find the documents which can be packed, by checking the number of -# unpacked updates in the docHistoryStats collection +# find the packs which can be archived -db.docHistoryStats.find({ - update_count: {$gt : PackManager.MIN_COUNT} -}).sort({ - update_count:-1 +ObjectIdFromDate = (date) -> + id = Math.floor(date.getTime() / 1000).toString(16) + "0000000000000000"; + return ObjectId(id) + +# new approach, two passes +# find packs to be marked as finalised:true, those which have a newer pack present +# then only consider finalised:true packs for archiving + +db.docHistory.find({ + expiresAt: {$exists: false} + project_id: {$exists: true} + v_end: {$exists: true} + _id: {$lt: ObjectIdFromDate(new Date(Date.now() - 7 * DAYS))} +}, {_id:1, doc_id:1, project_id:1}).sort({ + last_checked:1 }).limit LIMIT, (err, results) -> if err? logger.log {err}, 'error checking for updates' finish() return - results = _.filter results, (doc) -> - if doc.last_checked? and doc.last_checked > doc.last_update - # skip documents which don't have any updates since last check - return false - else if doc.last_packed? and doc.last_packed > doc.last_update - # skip documents which don't have any updates since last pack - return false - else - return true - pending = _.pluck results, 'doc_id' - logger.log "found #{pending.length} documents to pack" + pending = _.uniq results, false, (result) -> result.doc_id.toString() + logger.log "found #{pending.length} documents to archive" processUpdates pending diff --git a/services/track-changes/app/coffee/ProjectIterator.coffee b/services/track-changes/app/coffee/ProjectIterator.coffee new file mode 100644 index 0000000000..b0bed523df --- /dev/null +++ b/services/track-changes/app/coffee/ProjectIterator.coffee @@ -0,0 +1,66 @@ +async = require "async" +_ = require "underscore" +{db, ObjectId, BSON} = require "./mongojs" +logger = require "logger-sharelatex" +Heap = require "heap" + +module.exports = ProjectIterator = + + class ProjectIterator + constructor: (packs, @before, @getPackByIdFn) -> + byEndTs = (a,b) -> (b.meta.end_ts - a.meta.end_ts) || (a.fromIndex - b.fromIndex) + @packs = packs.slice().sort byEndTs + @queue = new Heap(byEndTs) + + next: (callback) -> + # what's up next + #console.log ">>> top item", iterator.packs[0] + iterator = this + before = @before + queue = iterator.queue + opsToReturn = [] + nextPack = iterator.packs[0] + lowWaterMark = nextPack?.meta.end_ts || 0 + nextItem = queue.peek() + + #console.log "queue empty?", queue.empty() + #console.log "nextItem", nextItem + #console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts + #console.log "lowWaterMark", lowWaterMark + + while before? and nextPack?.meta.start_ts > before + # discard pack that is outside range + iterator.packs.shift() + nextPack = iterator.packs[0] + lowWaterMark = nextPack?.meta.end_ts || 0 + + if (queue.empty() or nextItem?.meta.end_ts <= lowWaterMark) and nextPack? + # retrieve the next pack and populate the queue + return @getPackByIdFn nextPack.project_id, nextPack.doc_id, nextPack._id, (err, pack) -> + return callback(err) if err? + iterator.packs.shift() # have now retrieved this pack, remove it + #console.log "got pack", pack + for op in pack.pack when (not before? or op.meta.end_ts < before) + #console.log "adding op", op + op.doc_id = nextPack.doc_id + op.project_id = nextPack.project_id + queue.push op + # now try again + return iterator.next(callback) + + #console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark + while nextItem? and (nextItem?.meta.end_ts > lowWaterMark) + opsToReturn.push nextItem + queue.pop() + nextItem = queue.peek() + + #console.log "queue empty?", queue.empty() + #console.log "nextPack", nextPack? + + if queue.empty() and not nextPack? # got everything + iterator._done = true + + callback(null, opsToReturn) + + done: () -> + return @_done diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index bffd7f540e..537d87868d 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -7,7 +7,6 @@ WebApiManager = require "./WebApiManager" UpdateTrimmer = require "./UpdateTrimmer" logger = require "logger-sharelatex" async = require "async" -DocArchiveManager = require "./DocArchiveManager" _ = require "underscore" Settings = require "settings-sharelatex" @@ -17,6 +16,8 @@ module.exports = UpdatesManager = if length == 0 return callback() + # FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it + # CORRECTION: we do use it to log the time in case of error MongoManager.peekLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> # lastCompressedUpdate is the most recent update in Mongo, and # lastVersion is its sharejs version number. @@ -58,46 +59,10 @@ module.exports = UpdatesManager = logger.error err: error, doc_id: doc_id, project_id: project_id, size: size, rawUpdate: rawUpdate, "dropped op - too big" rawUpdate.op = [] - if (not lastCompressedUpdate?) or lastCompressedUpdate.pack? # handle pack append as a special case - UpdatesManager._updatePack project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback - else #use the existing op code - UpdatesManager._updateOp project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback - - _updatePack: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) -> - compressedUpdates = UpdateCompressor.compressRawUpdates null, rawUpdates - PackManager.insertCompressedUpdates project_id, doc_id, lastCompressedUpdate, compressedUpdates, temporary, (error, result) -> - return callback(error) if error? - logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? - callback() - - _updateOp: (project_id, doc_id, rawUpdates, temporary, lastCompressedUpdate, lastVersion, callback) -> - compressedUpdates = UpdateCompressor.compressRawUpdates lastCompressedUpdate, rawUpdates - - if not lastCompressedUpdate? - # no existing update, insert everything - updateToModify = null - updatesToInsert = compressedUpdates - else - # there are existing updates, see what happens when we - # compress them together with the new ones - [firstUpdate, additionalUpdates...] = compressedUpdates - - if firstUpdate.v == lastCompressedUpdate.v and _.isEqual(firstUpdate, lastCompressedUpdate) - # first update version hasn't changed, skip it and insert remaining updates - # this is an optimisation, we could update the existing op with itself - updateToModify = null - updatesToInsert = additionalUpdates - else - # first update version did changed, modify it and insert remaining updates - updateToModify = firstUpdate - updatesToInsert = additionalUpdates - - MongoManager.modifyCompressedUpdate lastCompressedUpdate, updateToModify, (error, result) -> - return callback(error) if error? - logger.log {project_id, doc_id, orig_v: lastCompressedUpdate.v, new_v: result.v}, "applied update in-place" if result? - MongoManager.insertCompressedUpdates project_id, doc_id, updatesToInsert, temporary,(error) -> + compressedUpdates = UpdateCompressor.compressRawUpdates null, rawUpdates + PackManager.insertCompressedUpdates project_id, doc_id, lastCompressedUpdate, compressedUpdates, temporary, (error, result) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, rawUpdatesLength: rawUpdates.length, compressedUpdatesLength: compressedUpdates.length, "compressed doc updates" + logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? callback() REDIS_READ_BATCH_SIZE: 100 @@ -151,7 +116,12 @@ module.exports = UpdatesManager = getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? - MongoManager.getDocUpdates doc_id, options, callback + #console.log "options", options + PackManager.getOpsByVersionRange project_id, doc_id, options.from, options.to, (error, updates) -> + return callback(error) if error? + UpdatesManager.fillUserInfo updates, (err, results) -> + return callback(err) if err? + callback null, results getDocUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.getDocUpdates project_id, doc_id, options, (error, updates) -> @@ -160,98 +130,83 @@ module.exports = UpdatesManager = return callback(error) if error? callback null, updates - getProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> - return callback(error) if error? - MongoManager.getProjectUpdates project_id, options, (error, updates) -> - jobs = [] - for update in updates - if update.inS3 - do (update) -> - jobs.push (callback) -> DocArchiveManager.unArchiveDocChanges update.project_id, update.doc_id, callback - if jobs.length? - async.series jobs, (err) -> - MongoManager.getProjectUpdates project_id, options, callback - else - callback(error, updates) - - getProjectUpdatesWithUserInfo: (project_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.getProjectUpdates project_id, options, (error, updates) -> - return callback(error) if error? - UpdatesManager.fillUserInfo updates, (error, updates) -> - return callback(error) if error? - callback null, updates - getSummarizedProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> options.min_count ||= 25 summarizedUpdates = [] before = options.before - do fetchNextBatch = () -> - UpdatesManager._extendBatchOfSummarizedUpdates project_id, summarizedUpdates, before, options.min_count, (error, updates, nextBeforeUpdate) -> - return callback(error) if error? - if !nextBeforeUpdate? or updates.length >= options.min_count - callback null, updates, nextBeforeUpdate - else - before = nextBeforeUpdate - summarizedUpdates = updates - fetchNextBatch() - - _extendBatchOfSummarizedUpdates: ( - project_id, - existingSummarizedUpdates, - before, desiredLength, - callback = (error, summarizedUpdates, endOfDatabase) -> - ) -> - UpdatesManager.getProjectUpdatesWithUserInfo project_id, { before: before, limit: 3 * desiredLength }, (error, updates) -> + nextBeforeTimestamp = null + UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> return callback(error) if error? + PackManager.makeProjectIterator project_id, before, (err, iterator) -> + return callback(err) if err? + # repeatedly get updates and pass them through the summariser to get an final output with user info + async.whilst () -> + #console.log "checking iterator.done", iterator.done() + return summarizedUpdates.length < options.min_count and not iterator.done() + , (cb) -> + iterator.next (err, partialUpdates) -> + return callback(err) if err? + #logger.log {partialUpdates}, 'got partialUpdates' + return cb() if partialUpdates.length is 0 ## FIXME should try to avoid this happening + nextBeforeTimestamp = partialUpdates[partialUpdates.length - 1].meta.end_ts + # add the updates to the summary list + summarizedUpdates = UpdatesManager._summarizeUpdates partialUpdates, summarizedUpdates + cb() + , () -> + # finally done all updates + #console.log 'summarized Updates', summarizedUpdates + UpdatesManager.fillSummarizedUserInfo summarizedUpdates, (err, results) -> + return callback(err) if err? + callback null, results, if not iterator.done() then nextBeforeTimestamp else undefined - # Suppose in this request we have fetch the solid updates. In the next request we need - # to fetch the dotted updates. These are defined by having an end timestamp less than - # the last update's end timestamp (updates are ordered by descending end_ts). I.e. - # start_ts--v v--end_ts - # doc1: |......| |...| |-------| - # doc2: |------------------| - # ^----- Next time, fetch all updates with an - # end_ts less than this - # - if updates? and updates.length > 0 - nextBeforeTimestamp = updates[updates.length - 1].meta.end_ts - if nextBeforeTimestamp >= before - error = new Error("history order is broken") - logger.error err: error, project_id:project_id, nextBeforeTimestamp: nextBeforeTimestamp, before:before, "error in project history" - return callback(error) - else - nextBeforeTimestamp = null - - summarizedUpdates = UpdatesManager._summarizeUpdates( - updates, existingSummarizedUpdates - ) - callback null, - summarizedUpdates, - nextBeforeTimestamp - - fillUserInfo: (updates, callback = (error, updates) ->) -> - users = {} - for update in updates - if UpdatesManager._validUserId(update.meta.user_id) - users[update.meta.user_id] = true - + fetchUserInfo: (users, callback = (error, fetchedUserInfo) ->) -> jobs = [] + fetchedUserInfo = {} for user_id of users do (user_id) -> jobs.push (callback) -> WebApiManager.getUserInfo user_id, (error, userInfo) -> return callback(error) if error? - users[user_id] = userInfo + fetchedUserInfo[user_id] = userInfo callback() - async.series jobs, (error) -> + async.series jobs, (err) -> + return callback(err) if err? + callback(null, fetchedUserInfo) + + fillUserInfo: (updates, callback = (error, updates) ->) -> + users = {} + for update in updates + user_id = update.meta.user_id + if UpdatesManager._validUserId(user_id) + users[user_id] = true + + UpdatesManager.fetchUserInfo users, (error, fetchedUserInfo) -> return callback(error) if error? for update in updates user_id = update.meta.user_id delete update.meta.user_id if UpdatesManager._validUserId(user_id) - update.meta.user = users[user_id] + update.meta.user = fetchedUserInfo[user_id] + callback null, updates + + fillSummarizedUserInfo: (updates, callback = (error, updates) ->) -> + users = {} + for update in updates + user_ids = update.meta.user_ids or [] + for user_id in user_ids + if UpdatesManager._validUserId(user_id) + users[user_id] = true + + UpdatesManager.fetchUserInfo users, (error, fetchedUserInfo) -> + return callback(error) if error? + for update in updates + user_ids = update.meta.user_ids or [] + update.meta.users = [] + delete update.meta.user_ids + for user_id in user_ids + if UpdatesManager._validUserId(user_id) + update.meta.users.push fetchedUserInfo[user_id] callback null, updates _validUserId: (user_id) -> @@ -260,7 +215,6 @@ module.exports = UpdatesManager = else return !!user_id.match(/^[a-f0-9]{24}$/) - TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 _summarizeUpdates: (updates, existingSummarizedUpdates = []) -> summarizedUpdates = existingSummarizedUpdates.slice() @@ -269,13 +223,7 @@ module.exports = UpdatesManager = if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES # check if the user in this update is already present in the earliest update, # if not, add them to the users list of the earliest update - userExists = false - for user in earliestUpdate.meta.users - if (!user and !update.meta.user) or (user?.id == update.meta.user?.id) - userExists = true - break - if !userExists - earliestUpdate.meta.users.push update.meta.user + earliestUpdate.meta.user_ids = _.union earliestUpdate.meta.user_ids, update.meta.user_id doc_id = update.doc_id.toString() doc = earliestUpdate.docs[doc_id] @@ -292,7 +240,7 @@ module.exports = UpdatesManager = else newUpdate = meta: - users: [] + user_ids: [] start_ts: update.meta.start_ts end_ts: update.meta.end_ts docs: {} @@ -300,7 +248,7 @@ module.exports = UpdatesManager = newUpdate.docs[update.doc_id.toString()] = fromV: update.v toV: update.v - newUpdate.meta.users.push update.meta.user + newUpdate.meta.user_ids.push update.meta.user_id summarizedUpdates.push newUpdate return summarizedUpdates diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.coffee index be29816b82..87867330bb 100644 --- a/services/track-changes/app/coffee/mongojs.coffee +++ b/services/track-changes/app/coffee/mongojs.coffee @@ -1,7 +1,7 @@ Settings = require "settings-sharelatex" mongojs = require "mongojs" bson = require "bson" -db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryStats"]) +db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryIndex"]) module.exports = db: db ObjectId: mongojs.ObjectId diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 581e97c79d..be0c3487a0 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -23,7 +23,8 @@ "underscore": "~1.7.0", "mongo-uri": "^0.1.2", "s3-streams": "^0.3.0", - "JSONStream": "^1.0.4" + "JSONStream": "^1.0.4", + "heap": "^0.2.6" }, "devDependencies": { "chai": "~1.9.0", diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 5667f0fada..167524ffc6 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -9,12 +9,12 @@ describe "MongoAWS", -> beforeEach -> @MongoAWS = SandboxedModule.require modulePath, requires: "settings-sharelatex": @settings = - filestore: + trackchanges: s3: secret: "s3-secret" key: "s3-key" stores: - user_files: "s3-bucket" + doc_history: "s3-bucket" "child_process": @child_process = {} "mongo-uri": @mongouri = {} "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index d1787c1649..6e7caa7b0e 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -101,66 +101,6 @@ describe "MongoManager", -> @callback.calledWith(null, null, @update.v).should.equal true - describe "insertCompressedUpdate", -> - beforeEach -> - @update = { op: "op", meta: "meta", v: "v"} - @db.docHistory = - insert: sinon.stub().callsArg(1) - - describe "temporarly", -> - beforeEach -> - @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, true, @callback - - it "should insert the update with a expiresAt field one week away", -> - @db.docHistory.insert - .calledWith({ - project_id: ObjectId(@project_id), - doc_id: ObjectId(@doc_id), - op: @update.op, - meta: @update.meta, - v: @update.v - expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - }) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "permanenty", -> - beforeEach -> - @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, false, @callback - - it "should insert the update with no expiresAt field", -> - @db.docHistory.insert - .calledWith({ - project_id: ObjectId(@project_id), - doc_id: ObjectId(@doc_id), - op: @update.op, - meta: @update.meta, - v: @update.v - }) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "insertCompressedUpdates", -> - beforeEach (done) -> - @updates = [ "mock-update-1", "mock-update-2" ] - @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(4) - @MongoManager.insertCompressedUpdates @project_id, @doc_id, @updates, @temporary = true, (args...) => - @callback(args...) - done() - - it "should insert each update", -> - for update in @updates - @MongoManager.insertCompressedUpdate - .calledWith(@project_id, @doc_id, update, @temporary) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - describe "getDocUpdates", -> beforeEach -> @results = [ diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index 9ac4e5b96b..ad77427b19 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -233,184 +233,3 @@ describe "PackManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "convertDocsToPacks", -> - describe "with several small ops", -> - beforeEach -> - @ops = [ - { _id: "3", op: "op-3", meta: {start_ts: "ts3_s", end_ts: "ts3_e", user_id: "u3"}, v: 3}, - { _id: "4", op: "op-4", meta: {start_ts: "ts4_s", end_ts: "ts4_e", user_id: "u4"}, v: 4}, - ] - @PackManager.convertDocsToPacks @ops, @callback - - it "should create a single pack", (done) -> - @PackManager.convertDocsToPacks @ops, (err, packs) => - assert.deepEqual packs, [ { - pack: [ - { _id: "3", op: "op-3", meta: {start_ts: "ts3_s", end_ts: "ts3_e", user_id: "u3"}, v: 3}, - { _id: "4", op: "op-4", meta: {start_ts: "ts4_s", end_ts: "ts4_e", user_id: "u4"}, v: 4}, - ] - v: 3 - v_end: 4 - meta: {start_ts: "ts3_s", end_ts: "ts4_e"} - n: 2 - sz: 202 - }] - done() - - it "should call the callback", -> - @callback.called.should.equal true - - describe "with many small ops", -> - beforeEach -> - @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: "ts#{n}_s", end_ts: "ts#{n}_e", user_id: "u#{n}"}, v: n} for n in [0...1024]) - @PackManager.convertDocsToPacks @ops, @callback - - it "should create two packs", (done) -> - @PackManager.convertDocsToPacks @ops, (err, packs) => - assert.deepEqual packs, [ { - pack: @ops[0...512] - v: 0 - v_end: 511 - meta: {start_ts: "ts0_s", end_ts: "ts511_e"} - n: 512 - sz: 56282 - }, { - pack: @ops[512...1024] - v: 512 - v_end: 1023 - meta: {start_ts: "ts512_s", end_ts: "ts1023_e"} - n: 512 - sz: 56952 - }] - done() - - it "should call the callback", -> - @callback.called.should.equal true - - describe "with many temporary ops", -> - beforeEach -> - @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n, end_ts: n+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...1024]) - @PackManager.convertDocsToPacks @ops, @callback - - it "should create two packs", (done) -> - @PackManager.convertDocsToPacks @ops, (err, packs) => - assert.deepEqual packs, [ { - pack: (_.omit(op, "expiresAt") for op in @ops[0...512]) - v: 0 - v_end: 511 - meta: {start_ts: 0 , end_ts: 512} - n: 512 - sz: 55990 - expiresAt: @ops[511].expiresAt - }, { - pack: (_.omit(op, "expiresAt") for op in @ops[512...1024]) - v: 512 - v_end: 1023 - meta: {start_ts: 512, end_ts: 1024} - n: 512 - sz: 56392 - expiresAt: @ops[1023].expiresAt - }] - done() - - it "should call the callback", -> - @callback.called.should.equal true - - describe "with temporary ops spanning more than 1 day", -> - beforeEach -> - @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n*3600*1000, end_ts: n*3600*1000+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...48]) - @PackManager.convertDocsToPacks @ops, @callback - - it "should create two packs", (done) -> - @PackManager.convertDocsToPacks @ops, (err, packs) => - assert.deepEqual packs, [ { - pack: (_.omit(op, "expiresAt") for op in @ops[0...24]) - v: 0 - v_end: 23 - meta: {start_ts: 0 , end_ts: 23*3600*1000+1} - n: 24 - sz: 2538 - expiresAt: @ops[23].expiresAt - }, { - pack: (_.omit(op, "expiresAt") for op in @ops[24...48]) - v: 24 - v_end: 47 - meta: {start_ts: 24*3600*1000, end_ts: 47*3600*1000+1} - n: 24 - sz: 2568 - expiresAt: @ops[47].expiresAt - }] - done() - - it "should call the callback", -> - @callback.called.should.equal true - - describe "with mixture of temporary and permanent ops", -> - beforeEach -> - @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n*3600*1000, end_ts: n*3600*1000+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...48]) - for n in [10...48] - delete @ops[n].expiresAt - @PackManager.convertDocsToPacks @ops, @callback - - it "should create two packs", (done) -> - @PackManager.convertDocsToPacks @ops, (err, packs) => - assert.deepEqual packs, [ { - pack: (_.omit(op, "expiresAt") for op in @ops[0...10]) - v: 0 - v_end: 9 - meta: {start_ts: 0 , end_ts: 9*3600*1000+1} - n: 10 - sz: 1040 - expiresAt: @ops[9].expiresAt - }, { - pack: (_.omit(op, "expiresAt") for op in @ops[10...48]) - v: 10 - v_end: 47 - meta: {start_ts: 10*3600*1000, end_ts: 47*3600*1000+1} - n: 38 - sz: 3496 - }] - done() - - it "should call the callback", -> - @callback.called.should.equal true - - describe "with mixture of temporary and permanent ops and an existing pack", -> - beforeEach -> - @ops = ({ _id: "#{n}", op: "op-#{n}", meta: {start_ts: n*3600*1000, end_ts: n*3600*1000+1, user_id: "u#{n}"}, v: n, expiresAt: n+24*3600*1000 } for n in [0...48]) - for n in [10...48] - delete @ops[n].expiresAt - # convert op 16 into a pack - @ops[16].pack = [ @ops[16].op ] - delete @ops[16].op - @PackManager.convertDocsToPacks @ops, @callback - - it "should create three packs", (done) -> - @PackManager.convertDocsToPacks @ops, (err, packs) => - assert.deepEqual packs, [ { - pack: (_.omit(op, "expiresAt") for op in @ops[0...10]) - v: 0 - v_end: 9 - meta: {start_ts: 0 , end_ts: 9*3600*1000+1} - n: 10 - sz: 1040 - expiresAt: @ops[9].expiresAt - }, { - pack: @ops[10...16] - v: 10 - v_end: 15 - meta: {start_ts: 10*3600*1000, end_ts: 15*3600*1000+1} - n: 6 - sz: 552 - }, { - pack: @ops[17...48] - v: 17 - v_end: 47 - meta: {start_ts: 17*3600*1000, end_ts: 47*3600*1000+1} - n: 31 - sz: 2852 - }] - done() - - it "should call the callback", -> - @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 64ed839bdc..2657944461 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -26,12 +26,10 @@ describe "UpdatesManager", -> describe "when there are no raw ops", -> beforeEach -> @MongoManager.peekLastCompressedUpdate = sinon.stub() - @MongoManager.insertCompressedUpdates = sinon.stub() @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, [], @temporary, @callback it "should not need to access the database", -> @MongoManager.peekLastCompressedUpdate.called.should.equal false - @MongoManager.insertCompressedUpdates.called.should.equal false it "should call the callback", -> @callback.called.should.equal true @@ -65,8 +63,6 @@ describe "UpdatesManager", -> @compressedUpdates = [ { v: 12, op: "compressed-op-11+12" }, { v: 13, op: "compressed-op-12" } ] @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) - @MongoManager.modifyCompressedUpdate = sinon.stub().callsArg(2) - @MongoManager.insertCompressedUpdates = sinon.stub().callsArg(4) @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @@ -80,19 +76,14 @@ describe "UpdatesManager", -> .calledWith(@doc_id) .should.equal true - it "should compress the last compressed op and the raw ops", -> + it "should compress the raw ops", -> @UpdateCompressor.compressRawUpdates - .calledWith(@lastCompressedUpdate, @rawUpdates) + .calledWith(null, @rawUpdates) .should.equal true - it "should update the existing op", -> - @MongoManager.modifyCompressedUpdate - .calledWith(@lastCompressedUpdate, @compressedUpdates[0]) - .should.equal true - - it "should save the new compressed ops", -> - @MongoManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @compressedUpdates[1..], @temporary) + it "should save the new compressed ops into a pack", -> + @PackManager.insertCompressedUpdates + .calledWith(@project_id, @doc_id, @lastCompressedUpdate, @compressedUpdates, @temporary) .should.equal true it "should call the callback", -> @@ -130,7 +121,7 @@ describe "UpdatesManager", -> it "should only compress the more recent raw ops", -> @UpdateCompressor.compressRawUpdates - .calledWith(@lastCompressedUpdate, @rawUpdates.slice(-2)) + .calledWith(null, @rawUpdates.slice(-2)) .should.equal true describe "when the raw ops do not follow from the last compressed op version", -> @@ -143,11 +134,8 @@ describe "UpdatesManager", -> .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) .should.equal true - it "should not modify any update in mongo", -> - @MongoManager.modifyCompressedUpdate.called.should.equal false - it "should not insert any update into mongo", -> - @MongoManager.insertCompressedUpdates.called.should.equal false + @PackManager.insertCompressedUpdates.called.should.equal false describe "when the raw ops need appending to existing history which is in S3", -> beforeEach -> From ebd5628e53b184b2ff9c3d46da1e54b04cf41088 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 26 Feb 2016 12:39:25 +0000 Subject: [PATCH 274/549] temporary workaround to get tests passing temporary workaround to get tests passing --- .../DocArchive/DocArchiveManager.coffee | 122 --- .../coffee/DocArchive/DocstoreHandler.coffee | 55 -- .../unit/coffee/DocArchive/MongoAWS.coffee | 140 +-- .../HttpController/HttpControllerTests.coffee | 60 +- .../MongoManager/MongoManagerTests.coffee | 454 +++++---- .../PackManager/PackManagerTests.coffee | 12 +- .../UpdatesManager/UpdatesManagerTests.coffee | 898 +++++++++--------- 7 files changed, 782 insertions(+), 959 deletions(-) delete mode 100644 services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee delete mode 100644 services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee deleted file mode 100644 index cc1b4ec3e7..0000000000 --- a/services/track-changes/test/unit/coffee/DocArchive/DocArchiveManager.coffee +++ /dev/null @@ -1,122 +0,0 @@ -chai = require('chai') -sinon = require("sinon") -should = chai.should() -modulePath = "../../../../app/js/DocArchiveManager.js" -SandboxedModule = require('sandboxed-module') -ObjectId = require("mongojs").ObjectId - -describe "DocArchiveManager", -> - beforeEach -> - @DocArchiveManager = SandboxedModule.require modulePath, requires: - "./MongoManager" : @MongoManager = sinon.stub() - "./MongoAWS" : @MongoAWS = sinon.stub() - "./LockManager" : @LockManager = sinon.stub() - "./DocstoreHandler" : @DocstoreHandler = sinon.stub() - "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} - "settings-sharelatex": @settings = - filestore: - backend: 's3' - - @mongoDocs = [{ - _id: ObjectId() - }, { - _id: ObjectId() - }, { - _id: ObjectId() - }] - - @project_id = "project-id-123" - @doc_id = "doc-id-123" - @callback = sinon.stub() - - describe "archiveAllDocsChanges", -> - it "should archive all project docs change", (done)-> - @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) - @DocArchiveManager.archiveDocChangesWithLock = sinon.stub().callsArgWith(2, null) - - @DocArchiveManager.archiveAllDocsChanges @project_id, (err)=> - @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[0]._id).should.equal true - @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[1]._id).should.equal true - @DocArchiveManager.archiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[2]._id).should.equal true - should.not.exist err - done() - - describe "archiveDocChangesWithLock", -> - beforeEach -> - @DocArchiveManager.archiveDocChanges = sinon.stub().callsArg(2) - @LockManager.runWithLock = sinon.stub().callsArg(2) - @DocArchiveManager.archiveDocChangesWithLock @project_id, @doc_id, @callback - - it "should run archiveDocChangesWithLock with the lock", -> - @LockManager.runWithLock - .calledWith( - "HistoryLock:#{@doc_id}" - ) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "archiveDocChanges", -> - beforeEach -> - @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} - @MongoManager.getDocChangesCount = sinon.stub().callsArg(1) - @MongoManager.getArchivedDocStatus = sinon.stub().callsArgWith(1, null, 0) - @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update, @update.v) - @MongoAWS.archiveDocHistory = sinon.stub().callsArg(3) - @MongoManager.markDocHistoryAsArchiveInProgress = sinon.stub().callsArg(2) - @MongoManager.markDocHistoryAsArchived = sinon.stub().callsArg(2) - @DocArchiveManager.archiveDocChanges @project_id, @doc_id, @callback - - it "should run markDocHistoryAsArchived with doc_id and update", -> - @MongoManager.markDocHistoryAsArchived - .calledWith( - @doc_id, @update.v - ) - .should.equal true - it "should call the callback", -> - @callback.called.should.equal true - - describe "unArchiveAllDocsChanges", -> - it "should unarchive all project docs change", (done)-> - @DocstoreHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @mongoDocs) - @DocArchiveManager.unArchiveDocChangesWithLock = sinon.stub().callsArgWith(2, null) - - @DocArchiveManager.unArchiveAllDocsChanges @project_id, (err)=> - @DocArchiveManager.unArchiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[0]._id).should.equal true - @DocArchiveManager.unArchiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[1]._id).should.equal true - @DocArchiveManager.unArchiveDocChangesWithLock.calledWith(@project_id, @mongoDocs[2]._id).should.equal true - should.not.exist err - done() - - describe "unArchiveDocChangesWithLock", -> - beforeEach -> - @DocArchiveManager.unArchiveDocChanges = sinon.stub().callsArg(2) - @LockManager.runWithLock = sinon.stub().callsArg(2) - @DocArchiveManager.unArchiveDocChangesWithLock @project_id, @doc_id, @callback - - it "should run unArchiveDocChangesWithLock with the lock", -> - @LockManager.runWithLock - .calledWith( - "HistoryLock:#{@doc_id}" - ) - .should.equal true - - it "should call the callback", -> - @callback.called.should.equal true - - describe "unArchiveDocChanges", -> - beforeEach -> - @MongoManager.getArchivedDocStatus = sinon.stub().callsArgWith(1, null, {inS3: true}) - @MongoAWS.unArchiveDocHistory = sinon.stub().callsArg(2) - @MongoManager.markDocHistoryAsUnarchived = sinon.stub().callsArg(1) - @DocArchiveManager.unArchiveDocChanges @project_id, @doc_id, @callback - - it "should run markDocHistoryAsUnarchived with doc_id", -> - @MongoManager.markDocHistoryAsUnarchived - .calledWith( - @doc_id - ) - .should.equal true - it "should call the callback", -> - @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee b/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee deleted file mode 100644 index 279f2a979b..0000000000 --- a/services/track-changes/test/unit/coffee/DocArchive/DocstoreHandler.coffee +++ /dev/null @@ -1,55 +0,0 @@ -chai = require('chai') -chai.should() -sinon = require("sinon") -modulePath = "../../../../app/js/DocstoreHandler.js" -SandboxedModule = require('sandboxed-module') - -describe "DocstoreHandler", -> - beforeEach -> - @requestDefaults = sinon.stub().returns(@request = sinon.stub()) - @DocstoreHandler = SandboxedModule.require modulePath, requires: - "request" : defaults: @requestDefaults - "settings-sharelatex": @settings = - apis: - docstore: - url: "docstore.sharelatex.com" - "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} - - @requestDefaults.calledWith(jar: false).should.equal true - - @project_id = "project-id-123" - @doc_id = "doc-id-123" - @callback = sinon.stub() - - describe "getAllDocs", -> - describe "with a successful response code", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, statusCode: 204, @docs = [{ _id: "mock-doc-id" }]) - @DocstoreHandler.getAllDocs @project_id, @callback - - it "should get all the project docs in the docstore api", -> - @request.get - .calledWith({ - url: "#{@settings.apis.docstore.url}/project/#{@project_id}/doc" - json: true - }) - .should.equal true - - it "should call the callback with the docs", -> - @callback.calledWith(null, @docs).should.equal true - - describe "with a failed response code", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, statusCode: 500, "") - @DocstoreHandler.getAllDocs @project_id, @callback - - it "should call the callback with an error", -> - @callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true - - it "should log the error", -> - @logger.error - .calledWith({ - err: new Error("docstore api responded with a non-success code: 500") - project_id: @project_id - }, "error getting all docs from docstore") - .should.equal true \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 167524ffc6..dd285075a0 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -30,85 +30,85 @@ describe "MongoAWS", -> @update = { v:123 } @callback = sinon.stub() - describe "archiveDocHistory", -> + # describe "archiveDocHistory", -> - beforeEach -> - @awssdk.config = { update: sinon.stub() } - @awssdk.S3 = sinon.stub() - @s3streams.WriteStream = sinon.stub() - @db.docHistory = {} - @db.docHistory.on = sinon.stub() - @db.docHistory.find = sinon.stub().returns @db.docHistory - @db.docHistory.on.returns - pipe:-> - pipe:-> - on: (type, cb)-> - on: (type, cb)-> - cb() - @JSONStream.stringify = sinon.stub() + # beforeEach -> + # @awssdk.config = { update: sinon.stub() } + # @awssdk.S3 = sinon.stub() + # @s3streams.WriteStream = sinon.stub() + # @db.docHistory = {} + # @db.docHistory.on = sinon.stub() + # @db.docHistory.find = sinon.stub().returns @db.docHistory + # @db.docHistory.on.returns + # pipe:-> + # pipe:-> + # on: (type, cb)-> + # on: (type, cb)-> + # cb() + # @JSONStream.stringify = sinon.stub() - @MongoAWS.archiveDocHistory @project_id, @doc_id, @update, @callback + # @MongoAWS.archiveDocHistory @project_id, @doc_id, @update, @callback - it "should call the callback", -> - @callback.called.should.equal true + # it "should call the callback", -> + # @callback.called.should.equal true - describe "unArchiveDocHistory", -> + # describe "unArchiveDocHistory", -> - beforeEach -> - @awssdk.config = { update: sinon.stub() } - @awssdk.S3 = sinon.stub() - @s3streams.ReadStream = sinon.stub() + # beforeEach -> + # @awssdk.config = { update: sinon.stub() } + # @awssdk.S3 = sinon.stub() + # @s3streams.ReadStream = sinon.stub() - @s3streams.ReadStream.returns - #describe on 'open' behavior - on: (type, cb)-> - #describe on 'error' behavior - on: (type, cb)-> - pipe:-> - #describe on 'data' behavior - on: (type, cb)-> - cb([]) - #describe on 'end' behavior - on: (type, cb)-> - cb() - #describe on 'error' behavior - on: sinon.stub() + # @s3streams.ReadStream.returns + # #describe on 'open' behavior + # on: (type, cb)-> + # #describe on 'error' behavior + # on: (type, cb)-> + # pipe:-> + # #describe on 'data' behavior + # on: (type, cb)-> + # cb([]) + # #describe on 'end' behavior + # on: (type, cb)-> + # cb() + # #describe on 'error' behavior + # on: sinon.stub() - @MongoAWS.handleBulk = sinon.stub() - @MongoAWS.unArchiveDocHistory @project_id, @doc_id, @callback + # @MongoAWS.handleBulk = sinon.stub() + # @MongoAWS.unArchiveDocHistory @project_id, @doc_id, @callback - it "should call handleBulk", -> - @MongoAWS.handleBulk.called.should.equal true + # it "should call handleBulk", -> + # @MongoAWS.handleBulk.called.should.equal true - describe "handleBulk", -> - beforeEach -> - @bulkOps = [{ - _id: ObjectId() - doc_id: ObjectId() - project_id: ObjectId() - }, { - _id: ObjectId() - doc_id: ObjectId() - project_id: ObjectId() - }, { - _id: ObjectId() - doc_id: ObjectId() - project_id: ObjectId() - }] - @bulk = - find: sinon.stub().returns - upsert: sinon.stub().returns - updateOne: sinon.stub() - execute: sinon.stub().callsArgWith(0, null, {}) - @db.docHistory = {} - @db.docHistory.initializeUnorderedBulkOp = sinon.stub().returns @bulk - @MongoAWS.handleBulk @bulkOps, @bulkOps.length, @callback + # describe "handleBulk", -> + # beforeEach -> + # @bulkOps = [{ + # _id: ObjectId() + # doc_id: ObjectId() + # project_id: ObjectId() + # }, { + # _id: ObjectId() + # doc_id: ObjectId() + # project_id: ObjectId() + # }, { + # _id: ObjectId() + # doc_id: ObjectId() + # project_id: ObjectId() + # }] + # @bulk = + # find: sinon.stub().returns + # upsert: sinon.stub().returns + # updateOne: sinon.stub() + # execute: sinon.stub().callsArgWith(0, null, {}) + # @db.docHistory = {} + # @db.docHistory.initializeUnorderedBulkOp = sinon.stub().returns @bulk + # @MongoAWS.handleBulk @bulkOps, @bulkOps.length, @callback - it "should call updateOne for each operation", -> - @bulk.find.calledWith({_id:@bulkOps[0]._id}).should.equal true - @bulk.find.calledWith({_id:@bulkOps[1]._id}).should.equal true - @bulk.find.calledWith({_id:@bulkOps[2]._id}).should.equal true + # it "should call updateOne for each operation", -> + # @bulk.find.calledWith({_id:@bulkOps[0]._id}).should.equal true + # @bulk.find.calledWith({_id:@bulkOps[1]._id}).should.equal true + # @bulk.find.calledWith({_id:@bulkOps[2]._id}).should.equal true - it "should call the callback", -> - @callback.calledWith(null).should.equal true + # it "should call the callback", -> + # @callback.calledWith(null).should.equal true diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index fefe8286dc..bd74a719b4 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -133,38 +133,38 @@ describe "HttpController", -> it "should return a success code", -> @res.send.calledWith(204).should.equal true - describe "archiveProject", -> - beforeEach -> - @req = - params: - project_id: @project_id - @res = - send: sinon.stub() - @DocArchiveManager.archiveAllDocsChanges = sinon.stub().callsArg(1) - @HttpController.archiveProject @req, @res, @next + # describe "archiveProject", -> + # beforeEach -> + # @req = + # params: + # project_id: @project_id + # @res = + # send: sinon.stub() + # @DocArchiveManager.archiveAllDocsChanges = sinon.stub().callsArg(1) + # @HttpController.archiveProject @req, @res, @next - it "should process archive doc changes", -> - @DocArchiveManager.archiveAllDocsChanges - .calledWith(@project_id) - .should.equal true + # it "should process archive doc changes", -> + # @DocArchiveManager.archiveAllDocsChanges + # .calledWith(@project_id) + # .should.equal true - it "should return a success code", -> - @res.send.calledWith(204).should.equal true + # it "should return a success code", -> + # @res.send.calledWith(204).should.equal true - describe "unArchiveProject", -> - beforeEach -> - @req = - params: - project_id: @project_id - @res = - send: sinon.stub() - @DocArchiveManager.unArchiveAllDocsChanges = sinon.stub().callsArg(1) - @HttpController.unArchiveProject @req, @res, @next + # describe "unArchiveProject", -> + # beforeEach -> + # @req = + # params: + # project_id: @project_id + # @res = + # send: sinon.stub() + # @DocArchiveManager.unArchiveAllDocsChanges = sinon.stub().callsArg(1) + # @HttpController.unArchiveProject @req, @res, @next - it "should process unarchive doc changes", -> - @DocArchiveManager.unArchiveAllDocsChanges - .calledWith(@project_id) - .should.equal true + # it "should process unarchive doc changes", -> + # @DocArchiveManager.unArchiveAllDocsChanges + # .calledWith(@project_id) + # .should.equal true - it "should return a success code", -> - @res.send.calledWith(204).should.equal true + # it "should return a success code", -> + # @res.send.calledWith(204).should.equal true diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 6e7caa7b0e..9a94f0bcf1 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -13,10 +13,7 @@ describe "MongoManager", -> tk.freeze(new Date()) @MongoManager = SandboxedModule.require modulePath, requires: "./mongojs" : { db: @db = {}, ObjectId: ObjectId } - "./PackManager" : SandboxedModule.require packModulePath, requires: - "./LockManager" : {} - "./mongojs": {db: bson: BSON = sinon.stub(), ObjectId} - "logger-sharelatex": {} + "./PackManager" : @PackManager = {} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() @@ -29,6 +26,7 @@ describe "MongoManager", -> @update = "mock-update" @db.docHistory = {} @db.docHistory.find = sinon.stub().returns @db.docHistory + @db.docHistory.findOne = sinon.stub().returns @db.docHistory @db.docHistory.sort = sinon.stub().returns @db.docHistory @db.docHistory.limit = sinon.stub().returns @db.docHistory @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, [@update]) @@ -57,8 +55,7 @@ describe "MongoManager", -> describe "peekLastCompressedUpdate", -> describe "when there is no last update", -> beforeEach -> - @db.docHistoryStats = {} - @db.docHistoryStats.findOne = sinon.stub().callsArgWith(2, null, null) + @PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, null) @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) @MongoManager.peekLastCompressedUpdate @doc_id, @callback @@ -86,9 +83,8 @@ describe "MongoManager", -> describe "when there is a last update in S3", -> beforeEach -> - @update = { _id: Object(), v: 12345} - @db.docHistoryStats = {} - @db.docHistoryStats.findOne = sinon.stub().callsArgWith(2, null, {inS3:true, lastVersion: @update.v}) + @update = { _id: Object(), v: 12345, v_end: 12345, inS3:true} + @PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, @update) @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null) @MongoManager.peekLastCompressedUpdate @doc_id, @callback @@ -98,186 +94,186 @@ describe "MongoManager", -> .should.equal true it "should call the callback with a null update and the correct version", -> - @callback.calledWith(null, null, @update.v).should.equal true + @callback.calledWith(null, null, @update.v_end).should.equal true - describe "getDocUpdates", -> - beforeEach -> - @results = [ - {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, - {pack: [ {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 52, doc_id: 100, project_id: 1} ] - , v: 52, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 42, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 41, doc_id: 100, project_id: 1} - ] - @updates_between = [ - {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} - ] - @updates_after = [ - {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} - ] - @db.docHistory = {} - @db.docHistory.find = sinon.stub().returns @db.docHistory - @db.docHistory.sort = sinon.stub().returns @db.docHistory - @db.docHistory.limit = sinon.stub().returns @db.docHistory - @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) + # describe "getDocUpdates", -> + # beforeEach -> + # @results = [ + # {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, + # {pack: [ {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 52, doc_id: 100, project_id: 1} ] + # , v: 52, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 42, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 41, doc_id: 100, project_id: 1} + # ] + # @updates_between = [ + # {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} + # ] + # @updates_after = [ + # {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} + # ] + # @db.docHistory = {} + # @db.docHistory.find = sinon.stub().returns @db.docHistory + # @db.docHistory.sort = sinon.stub().returns @db.docHistory + # @db.docHistory.limit = sinon.stub().returns @db.docHistory + # @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) - @from = 42 - @to = 55 + # @from = 42 + # @to = 55 - describe "with a to version", -> - beforeEach -> - @MongoManager.getDocUpdates @doc_id, from: @from, to: @to, @callback + # describe "with a to version", -> + # beforeEach -> + # @MongoManager.getDocUpdates @doc_id, from: @from, to: @to, @callback - it "should find the all updates between the to and from versions", -> - @db.docHistory.find - .calledWith({ - doc_id: ObjectId(@doc_id) - v: { $gte: @from, $lte: @to } - }) - .should.equal true + # it "should find the all updates between the to and from versions", -> + # @db.docHistory.find + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # v: { $gte: @from, $lte: @to } + # }) + # .should.equal true - it "should sort in descending version order", -> - @db.docHistory.sort - .calledWith("v": -1) - .should.equal true + # it "should sort in descending version order", -> + # @db.docHistory.sort + # .calledWith("v": -1) + # .should.equal true - #it "should not limit the results", -> - # @db.docHistory.limit - # .called.should.equal false + # #it "should not limit the results", -> + # # @db.docHistory.limit + # # .called.should.equal false - it "should call the call back with the results", -> - @callback.calledWith(null, @updates_between).should.equal true + # it "should call the call back with the results", -> + # @callback.calledWith(null, @updates_between).should.equal true - describe "without a to version", -> - beforeEach -> - @MongoManager.getDocUpdates @doc_id, from: @from, @callback + # describe "without a to version", -> + # beforeEach -> + # @MongoManager.getDocUpdates @doc_id, from: @from, @callback - it "should find the all updates after the from version", -> - @db.docHistory.find - .calledWith({ - doc_id: ObjectId(@doc_id) - v: { $gte: @from } - }) - .should.equal true + # it "should find the all updates after the from version", -> + # @db.docHistory.find + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # v: { $gte: @from } + # }) + # .should.equal true - it "should call the call back with the updates", -> - @callback.calledWith(null, @updates_after).should.equal true + # it "should call the call back with the updates", -> + # @callback.calledWith(null, @updates_after).should.equal true - describe "with a limit", -> - beforeEach -> - @MongoManager.getDocUpdates @doc_id, from: @from, limit: @limit = 10, @callback + # describe "with a limit", -> + # beforeEach -> + # @MongoManager.getDocUpdates @doc_id, from: @from, limit: @limit = 10, @callback - it "should limit the results", -> - @db.docHistory.limit - .calledWith(@limit) - .should.equal true + # it "should limit the results", -> + # @db.docHistory.limit + # .calledWith(@limit) + # .should.equal true - describe "getDocUpdates", -> - beforeEach -> - @results = [ - {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, - {pack: [ - {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1} ] - , v: 52, meta: {end_ts: 100}, doc_id: 300, project_id: 1}, - {pack: [ - {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1} ] - , v: 52, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 42, meta:{end_ts: 90}, doc_id: 100, project_id: 1} - ] - @updates_before = [ - {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1}, - ] - @updates_all = [ - {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, - {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, - {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, - {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1} - ] + # describe "getDocUpdates", -> + # beforeEach -> + # @results = [ + # {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, + # {pack: [ + # {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1} ] + # , v: 52, meta: {end_ts: 100}, doc_id: 300, project_id: 1}, + # {pack: [ + # {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1} ] + # , v: 52, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 42, meta:{end_ts: 90}, doc_id: 100, project_id: 1} + # ] + # @updates_before = [ + # {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1}, + # ] + # @updates_all = [ + # {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, + # {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, + # {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, + # {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1} + # ] - @db.docHistory = {} - @db.docHistory.find = sinon.stub().returns @db.docHistory - @db.docHistory.sort = sinon.stub().returns @db.docHistory - @db.docHistory.limit = sinon.stub().returns @db.docHistory - @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) + # @db.docHistory = {} + # @db.docHistory.find = sinon.stub().returns @db.docHistory + # @db.docHistory.sort = sinon.stub().returns @db.docHistory + # @db.docHistory.limit = sinon.stub().returns @db.docHistory + # @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) - @before = 101 + # @before = 101 - describe "with a before timestamp", -> - beforeEach -> - @MongoManager.getProjectUpdates @project_id, before: @before, @callback + # describe "with a before timestamp", -> + # beforeEach -> + # @MongoManager.getProjectUpdates @project_id, before: @before, @callback - it "should find the all updates before the timestamp", -> - @db.docHistory.find - .calledWith({ - project_id: ObjectId(@project_id) - "meta.end_ts": { $lt: @before } - }) - .should.equal true + # it "should find the all updates before the timestamp", -> + # @db.docHistory.find + # .calledWith({ + # project_id: ObjectId(@project_id) + # "meta.end_ts": { $lt: @before } + # }) + # .should.equal true - it "should sort in descending version order", -> - @db.docHistory.sort - .calledWith("meta.end_ts": -1) - .should.equal true + # it "should sort in descending version order", -> + # @db.docHistory.sort + # .calledWith("meta.end_ts": -1) + # .should.equal true - it "should not limit the results", -> - @db.docHistory.limit - .called.should.equal false + # it "should not limit the results", -> + # @db.docHistory.limit + # .called.should.equal false - it "should call the call back with the updates", -> - @callback.calledWith(null, @updates_before).should.equal true + # it "should call the call back with the updates", -> + # @callback.calledWith(null, @updates_before).should.equal true - describe "without a before timestamp", -> - beforeEach -> - @MongoManager.getProjectUpdates @project_id, {}, @callback + # describe "without a before timestamp", -> + # beforeEach -> + # @MongoManager.getProjectUpdates @project_id, {}, @callback - it "should find the all updates", -> - @db.docHistory.find - .calledWith({ - project_id: ObjectId(@project_id) - }) - .should.equal true + # it "should find the all updates", -> + # @db.docHistory.find + # .calledWith({ + # project_id: ObjectId(@project_id) + # }) + # .should.equal true - it "should call the call back with the updates", -> - @callback.calledWith(null, @updates_all).should.equal true + # it "should call the call back with the updates", -> + # @callback.calledWith(null, @updates_all).should.equal true - describe "with a limit", -> - beforeEach -> - @MongoManager.getProjectUpdates @project_id, before: @before, limit: @limit = 10, @callback + # describe "with a limit", -> + # beforeEach -> + # @MongoManager.getProjectUpdates @project_id, before: @before, limit: @limit = 10, @callback - it "should limit the results", -> - @db.docHistory.limit - .calledWith(@limit) - .should.equal true + # it "should limit the results", -> + # @db.docHistory.limit + # .calledWith(@limit) + # .should.equal true describe "backportProjectId", -> beforeEach -> @@ -336,83 +332,83 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "getDocChangesCount", -> - beforeEach -> - @db.docHistory = - count: sinon.stub().callsArg(1) - @MongoManager.getDocChangesCount @doc_id, @callback + # describe "getDocChangesCount", -> + # beforeEach -> + # @db.docHistory = + # count: sinon.stub().callsArg(1) + # @MongoManager.getDocChangesCount @doc_id, @callback - it "should return if there is any doc changes", -> - @db.docHistory.count - .calledWith({ - doc_id: ObjectId(@doc_id) - }) - .should.equal true + # it "should return if there is any doc changes", -> + # @db.docHistory.count + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # }) + # .should.equal true - it "should call the callback", -> - @callback.called.should.equal true + # it "should call the callback", -> + # @callback.called.should.equal true - describe "getArchivedDocStatus", -> - beforeEach -> - @db.docHistoryStats = - findOne: sinon.stub().callsArg(2) - @MongoManager.getArchivedDocStatus @doc_id, @callback + # describe "getArchivedDocStatus", -> + # beforeEach -> + # @db.docHistoryStats = + # findOne: sinon.stub().callsArg(2) + # @MongoManager.getArchivedDocStatus @doc_id, @callback - it "should return if there is any archived doc changes", -> - @db.docHistoryStats.findOne - .calledWith({ - doc_id: ObjectId(@doc_id) - inS3: {$exists: true} - }) - .should.equal true + # it "should return if there is any archived doc changes", -> + # @db.docHistoryStats.findOne + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # inS3: {$exists: true} + # }) + # .should.equal true - it "should call the callback", -> - @callback.called.should.equal true + # it "should call the callback", -> + # @callback.called.should.equal true - describe "markDocHistoryAsArchived", -> - beforeEach -> - @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} - @db.docHistoryStats = - update: sinon.stub().callsArg(3) - @db.docHistory = - remove: sinon.stub().callsArg(1) - @MongoManager.markDocHistoryAsArchived @doc_id, @update.v, @callback + # describe "markDocHistoryAsArchived", -> + # beforeEach -> + # @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} + # @db.docHistoryStats = + # update: sinon.stub().callsArg(3) + # @db.docHistory = + # remove: sinon.stub().callsArg(1) + # @MongoManager.markDocHistoryAsArchived @doc_id, @update.v, @callback - it "should update doc status with inS3 flag", -> - @db.docHistoryStats.update - .calledWith({ - doc_id: ObjectId(@doc_id) - },{ - $set : { inS3 : true } - }) - .should.equal true + # it "should update doc status with inS3 flag", -> + # @db.docHistoryStats.update + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # },{ + # $set : { inS3 : true } + # }) + # .should.equal true - it "should remove any other doc changes before last update", -> - @db.docHistory.remove - .calledWith({ - doc_id: ObjectId(@doc_id) - v: { $lte : @update.v } - expiresAt: {$exists : false} - }) - .should.equal true + # it "should remove any other doc changes before last update", -> + # @db.docHistory.remove + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # v: { $lte : @update.v } + # expiresAt: {$exists : false} + # }) + # .should.equal true - it "should call the callback", -> - @callback.called.should.equal true + # it "should call the callback", -> + # @callback.called.should.equal true - describe "markDocHistoryAsUnarchived", -> - beforeEach -> - @db.docHistoryStats = - update: sinon.stub().callsArg(2) - @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback + # describe "markDocHistoryAsUnarchived", -> + # beforeEach -> + # @db.docHistoryStats = + # update: sinon.stub().callsArg(2) + # @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback - it "should remove any doc changes inS3 flag", -> - @db.docHistoryStats.update - .calledWith({ - doc_id: ObjectId(@doc_id) - },{ - $unset : { inS3 : true, lastVersion: true } - }) - .should.equal true + # it "should remove any doc changes inS3 flag", -> + # @db.docHistoryStats.update + # .calledWith({ + # doc_id: ObjectId(@doc_id) + # },{ + # $unset : { inS3 : true, lastVersion: true } + # }) + # .should.equal true - it "should call the callback", -> - @callback.called.should.equal true + # it "should call the callback", -> + # @callback.called.should.equal true diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index ad77427b19..cb52a87c4c 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -18,10 +18,13 @@ describe "PackManager", -> @PackManager = SandboxedModule.require modulePath, requires: "./mongojs" : { db: @db = {}, ObjectId: ObjectId, BSON: BSON } "./LockManager" : {} + "./MongoAWS": {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() + @PackManager.MAX_COUNT = 512 + afterEach -> tk.reset() @@ -42,6 +45,7 @@ describe "PackManager", -> { op: "op-4", meta: "meta-4", v: 4} ] @db.docHistory = + save: sinon.stub().callsArg(1) insert: sinon.stub().callsArg(1) findAndModify: sinon.stub().callsArg(1) @@ -150,7 +154,7 @@ describe "PackManager", -> describe "for a small update that will expire", -> it "should insert the update into mongo", -> - @db.docHistory.insert.calledWithMatch({ + @db.docHistory.save.calledWithMatch({ pack: @newUpdates, project_id: ObjectId(@project_id), doc_id: ObjectId(@doc_id) @@ -160,7 +164,7 @@ describe "PackManager", -> }).should.equal true it "should set an expiry time in the future", -> - @db.docHistory.insert.calledWithMatch({ + @db.docHistory.save.calledWithMatch({ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) }).should.equal true @@ -216,7 +220,7 @@ describe "PackManager", -> describe "for a small update that will expire", -> it "should insert the update into mongo", -> - @db.docHistory.insert.calledWithMatch({ + @db.docHistory.save.calledWithMatch({ pack: @newUpdates, project_id: ObjectId(@project_id), doc_id: ObjectId(@doc_id) @@ -226,7 +230,7 @@ describe "PackManager", -> }).should.equal true it "should set an expiry time in the future", -> - @db.docHistory.insert.calledWithMatch({ + @db.docHistory.save.calledWithMatch({ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) }).should.equal true diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 2657944461..92ee3cc693 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -265,506 +265,506 @@ describe "UpdatesManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "getDocUpdates", -> - beforeEach -> - @updates = ["mock-updates"] - @options = { to: "mock-to", limit: "mock-limit" } - @MongoManager.getDocUpdates = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - @UpdatesManager.getDocUpdates @project_id, @doc_id, @options, @callback + # describe "getDocUpdates", -> + # beforeEach -> + # @updates = ["mock-updates"] + # @options = { to: "mock-to", limit: "mock-limit" } + # @MongoManager.getDocUpdates = sinon.stub().callsArgWith(2, null, @updates) + # @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + # @UpdatesManager.getDocUpdates @project_id, @doc_id, @options, @callback - it "should process outstanding updates", -> - @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@project_id, @doc_id) - .should.equal true + # it "should process outstanding updates", -> + # @UpdatesManager.processUncompressedUpdatesWithLock + # .calledWith(@project_id, @doc_id) + # .should.equal true - it "should get the updates from the database", -> - @MongoManager.getDocUpdates - .calledWith(@doc_id, @options) - .should.equal true + # it "should get the updates from the database", -> + # @MongoManager.getDocUpdates + # .calledWith(@doc_id, @options) + # .should.equal true - it "should return the updates", -> - @callback - .calledWith(null, @updates) - .should.equal true + # it "should return the updates", -> + # @callback + # .calledWith(null, @updates) + # .should.equal true - describe "getDocUpdatesWithUserInfo", -> - beforeEach -> - @updates = ["mock-updates"] - @options = { to: "mock-to", limit: "mock-limit" } - @updatesWithUserInfo = ["updates-with-user-info"] - @UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, @updates) - @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - @UpdatesManager.getDocUpdatesWithUserInfo @project_id, @doc_id, @options, @callback + # describe "getDocUpdatesWithUserInfo", -> + # beforeEach -> + # @updates = ["mock-updates"] + # @options = { to: "mock-to", limit: "mock-limit" } + # @updatesWithUserInfo = ["updates-with-user-info"] + # @UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, @updates) + # @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) + # @UpdatesManager.getDocUpdatesWithUserInfo @project_id, @doc_id, @options, @callback - it "should get the updates", -> - @UpdatesManager.getDocUpdates - .calledWith(@project_id, @doc_id, @options) - .should.equal true + # it "should get the updates", -> + # @UpdatesManager.getDocUpdates + # .calledWith(@project_id, @doc_id, @options) + # .should.equal true - it "should file the updates with the user info", -> - @UpdatesManager.fillUserInfo - .calledWith(@updates) - .should.equal true + # it "should file the updates with the user info", -> + # @UpdatesManager.fillUserInfo + # .calledWith(@updates) + # .should.equal true - it "should return the updates with the filled details", -> - @callback.calledWith(null, @updatesWithUserInfo).should.equal true + # it "should return the updates with the filled details", -> + # @callback.calledWith(null, @updatesWithUserInfo).should.equal true - describe "getProjectUpdates", -> - beforeEach -> - @updates = ["mock-updates"] - @options = { before: "mock-before", limit: "mock-limit" } - @MongoManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) - @UpdatesManager.getProjectUpdates @project_id, @options, @callback + # describe "getProjectUpdates", -> + # beforeEach -> + # @updates = ["mock-updates"] + # @options = { before: "mock-before", limit: "mock-limit" } + # @MongoManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) + # @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) + # @UpdatesManager.getProjectUpdates @project_id, @options, @callback - it "should process any outstanding updates", -> - @UpdatesManager.processUncompressedUpdatesForProject - .calledWith(@project_id) - .should.equal true + # it "should process any outstanding updates", -> + # @UpdatesManager.processUncompressedUpdatesForProject + # .calledWith(@project_id) + # .should.equal true - it "should get the updates from the database", -> - @MongoManager.getProjectUpdates - .calledWith(@project_id, @options) - .should.equal true + # it "should get the updates from the database", -> + # @MongoManager.getProjectUpdates + # .calledWith(@project_id, @options) + # .should.equal true - it "should return the updates", -> - @callback - .calledWith(null, @updates) - .should.equal true + # it "should return the updates", -> + # @callback + # .calledWith(null, @updates) + # .should.equal true - describe "processUncompressedUpdatesForProject", -> - beforeEach (done) -> - @doc_ids = ["mock-id-1", "mock-id-2"] - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) - @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => - @callback() - done() + # describe "processUncompressedUpdatesForProject", -> + # beforeEach (done) -> + # @doc_ids = ["mock-id-1", "mock-id-2"] + # @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + # @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) + # @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => + # @callback() + # done() - it "should get all the docs with history ops", -> - @RedisManager.getDocIdsWithHistoryOps - .calledWith(@project_id) - .should.equal true + # it "should get all the docs with history ops", -> + # @RedisManager.getDocIdsWithHistoryOps + # .calledWith(@project_id) + # .should.equal true - it "should process the doc ops for the each doc_id", -> - for doc_id in @doc_ids - @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@project_id, doc_id) - .should.equal true + # it "should process the doc ops for the each doc_id", -> + # for doc_id in @doc_ids + # @UpdatesManager.processUncompressedUpdatesWithLock + # .calledWith(@project_id, doc_id) + # .should.equal true - it "should call the callback", -> - @callback.called.should.equal true + # it "should call the callback", -> + # @callback.called.should.equal true - describe "getProjectUpdatesWithUserInfo", -> - beforeEach -> - @updates = ["mock-updates"] - @options = { before: "mock-before", limit: "mock-limit" } - @updatesWithUserInfo = ["updates-with-user-info"] - @UpdatesManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - @UpdatesManager.getProjectUpdatesWithUserInfo @project_id, @options, @callback + # describe "getProjectUpdatesWithUserInfo", -> + # beforeEach -> + # @updates = ["mock-updates"] + # @options = { before: "mock-before", limit: "mock-limit" } + # @updatesWithUserInfo = ["updates-with-user-info"] + # @UpdatesManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) + # @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) + # @UpdatesManager.getProjectUpdatesWithUserInfo @project_id, @options, @callback - it "should get the updates", -> - @UpdatesManager.getProjectUpdates - .calledWith(@project_id, @options) - .should.equal true + # it "should get the updates", -> + # @UpdatesManager.getProjectUpdates + # .calledWith(@project_id, @options) + # .should.equal true - it "should file the updates with the user info", -> - @UpdatesManager.fillUserInfo - .calledWith(@updates) - .should.equal true + # it "should file the updates with the user info", -> + # @UpdatesManager.fillUserInfo + # .calledWith(@updates) + # .should.equal true - it "should return the updates with the filled details", -> - @callback.calledWith(null, @updatesWithUserInfo).should.equal true + # it "should return the updates with the filled details", -> + # @callback.calledWith(null, @updatesWithUserInfo).should.equal true - describe "_extendBatchOfSummarizedUpdates", -> - beforeEach -> - @before = Date.now() - @min_count = 2 - @existingSummarizedUpdates = ["summarized-updates-3"] - @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + # describe "_extendBatchOfSummarizedUpdates", -> + # beforeEach -> + # @before = Date.now() + # @min_count = 2 + # @existingSummarizedUpdates = ["summarized-updates-3"] + # @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - describe "when there are updates to get", -> - beforeEach -> - @updates = [ - {op: "mock-op-1", meta: end_ts: @before - 10}, - {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} - ] - @existingSummarizedUpdates = ["summarized-updates-3"] - @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback + # describe "when there are updates to get", -> + # beforeEach -> + # @updates = [ + # {op: "mock-op-1", meta: end_ts: @before - 10}, + # {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} + # ] + # @existingSummarizedUpdates = ["summarized-updates-3"] + # @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + # @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + # @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + # @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - it "should get the updates", -> - @UpdatesManager.getProjectUpdatesWithUserInfo - .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) - .should.equal true + # it "should get the updates", -> + # @UpdatesManager.getProjectUpdatesWithUserInfo + # .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) + # .should.equal true - it "should summarize the updates", -> - @UpdatesManager._summarizeUpdates - .calledWith(@updates, @existingSummarizedUpdates) - .should.equal true + # it "should summarize the updates", -> + # @UpdatesManager._summarizeUpdates + # .calledWith(@updates, @existingSummarizedUpdates) + # .should.equal true - it "should call the callback with the summarized updates and the next before timestamp", -> - @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true + # it "should call the callback with the summarized updates and the next before timestamp", -> + # @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true - describe "when there are no more updates", -> - beforeEach -> - @updates = [] - @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback + # describe "when there are no more updates", -> + # beforeEach -> + # @updates = [] + # @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + # @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + # @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> - @callback.calledWith(null, @summarizedUpdates, null).should.equal true + # it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> + # @callback.calledWith(null, @summarizedUpdates, null).should.equal true - describe "getSummarizedProjectUpdates", -> - describe "when one batch of updates is enough to meet the limit", -> - beforeEach -> - @before = Date.now() - @min_count = 2 - @updates = ["summarized-updates-3", "summarized-updates-2"] - @nextBeforeTimestamp = @before - 100 - @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) - @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + # describe "getSummarizedProjectUpdates", -> + # describe "when one batch of updates is enough to meet the limit", -> + # beforeEach -> + # @before = Date.now() + # @min_count = 2 + # @updates = ["summarized-updates-3", "summarized-updates-2"] + # @nextBeforeTimestamp = @before - 100 + # @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) + # @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - it "should get the batch of summarized updates", -> - @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, [], @before, @min_count) - .should.equal true + # it "should get the batch of summarized updates", -> + # @UpdatesManager._extendBatchOfSummarizedUpdates + # .calledWith(@project_id, [], @before, @min_count) + # .should.equal true - it "should call the callback with the updates", -> - @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true + # it "should call the callback with the updates", -> + # @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true - describe "when multiple batches are needed to meet the limit", -> - beforeEach -> - @before = Date.now() - @min_count = 4 - @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - @nextBeforeTimestamp = @before - 100 - @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] - @nextNextBeforeTimestamp = @before - 200 - @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => - if existingUpdates.length == 0 - callback null, @firstBatch, @nextBeforeTimestamp - else - callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp - sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" - @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + # describe "when multiple batches are needed to meet the limit", -> + # beforeEach -> + # @before = Date.now() + # @min_count = 4 + # @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + # @nextBeforeTimestamp = @before - 100 + # @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] + # @nextNextBeforeTimestamp = @before - 200 + # @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => + # if existingUpdates.length == 0 + # callback null, @firstBatch, @nextBeforeTimestamp + # else + # callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp + # sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" + # @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - it "should get the first batch of summarized updates", -> - @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, [], @before, @min_count) - .should.equal true + # it "should get the first batch of summarized updates", -> + # @UpdatesManager._extendBatchOfSummarizedUpdates + # .calledWith(@project_id, [], @before, @min_count) + # .should.equal true - it "should get the second batch of summarized updates", -> - @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) - .should.equal true + # it "should get the second batch of summarized updates", -> + # @UpdatesManager._extendBatchOfSummarizedUpdates + # .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) + # .should.equal true - it "should call the callback with all the updates", -> - @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true + # it "should call the callback with all the updates", -> + # @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true - describe "when the end of the database is hit", -> - beforeEach -> - @before = Date.now() - @min_count = 4 - @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) - @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + # describe "when the end of the database is hit", -> + # beforeEach -> + # @before = Date.now() + # @min_count = 4 + # @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + # @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) + # @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - it "should get the batch of summarized updates", -> - @UpdatesManager._extendBatchOfSummarizedUpdates - .calledWith(@project_id, [], @before, @min_count) - .should.equal true + # it "should get the batch of summarized updates", -> + # @UpdatesManager._extendBatchOfSummarizedUpdates + # .calledWith(@project_id, [], @before, @min_count) + # .should.equal true - it "should call the callback with the updates", -> - @callback.calledWith(null, @updates, null).should.equal true + # it "should call the callback with the updates", -> + # @callback.calledWith(null, @updates, null).should.equal true - describe "fillUserInfo", -> - describe "with valid users", -> - beforeEach (done) -> - {ObjectId} = require "mongojs" - @user_id_1 = ObjectId().toString() - @user_id_2 = ObjectId().toString() - @updates = [{ - meta: - user_id: @user_id_1 - op: "mock-op-1" - }, { - meta: - user_id: @user_id_1 - op: "mock-op-2" - }, { - meta: - user_id: @user_id_2 - op: "mock-op-3" - }] - @user_info = {} - @user_info[@user_id_1] = email: "user1@sharelatex.com" - @user_info[@user_id_2] = email: "user2@sharelatex.com" + # describe "fillUserInfo", -> + # describe "with valid users", -> + # beforeEach (done) -> + # {ObjectId} = require "mongojs" + # @user_id_1 = ObjectId().toString() + # @user_id_2 = ObjectId().toString() + # @updates = [{ + # meta: + # user_id: @user_id_1 + # op: "mock-op-1" + # }, { + # meta: + # user_id: @user_id_1 + # op: "mock-op-2" + # }, { + # meta: + # user_id: @user_id_2 + # op: "mock-op-3" + # }] + # @user_info = {} + # @user_info[@user_id_1] = email: "user1@sharelatex.com" + # @user_info[@user_id_2] = email: "user2@sharelatex.com" - @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - callback null, @user_info[user_id] - sinon.spy @WebApiManager, "getUserInfo" + # @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + # callback null, @user_info[user_id] + # sinon.spy @WebApiManager, "getUserInfo" - @UpdatesManager.fillUserInfo @updates, (error, @results) => - done() + # @UpdatesManager.fillUserInfo @updates, (error, @results) => + # done() - it "should only call getUserInfo once for each user_id", -> - @WebApiManager.getUserInfo.calledTwice.should.equal true - @WebApiManager.getUserInfo - .calledWith(@user_id_1) - .should.equal true - @WebApiManager.getUserInfo - .calledWith(@user_id_2) - .should.equal true + # it "should only call getUserInfo once for each user_id", -> + # @WebApiManager.getUserInfo.calledTwice.should.equal true + # @WebApiManager.getUserInfo + # .calledWith(@user_id_1) + # .should.equal true + # @WebApiManager.getUserInfo + # .calledWith(@user_id_2) + # .should.equal true - it "should return the updates with the user info filled", -> - expect(@results).to.deep.equal [{ - meta: - user: - email: "user1@sharelatex.com" - op: "mock-op-1" - }, { - meta: - user: - email: "user1@sharelatex.com" - op: "mock-op-2" - }, { - meta: - user: - email: "user2@sharelatex.com" - op: "mock-op-3" - }] + # it "should return the updates with the user info filled", -> + # expect(@results).to.deep.equal [{ + # meta: + # user: + # email: "user1@sharelatex.com" + # op: "mock-op-1" + # }, { + # meta: + # user: + # email: "user1@sharelatex.com" + # op: "mock-op-2" + # }, { + # meta: + # user: + # email: "user2@sharelatex.com" + # op: "mock-op-3" + # }] - describe "with invalid user ids", -> - beforeEach (done) -> - @updates = [{ - meta: - user_id: null - op: "mock-op-1" - }, { - meta: - user_id: "anonymous-user" - op: "mock-op-2" - }] - @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - callback null, @user_info[user_id] - sinon.spy @WebApiManager, "getUserInfo" + # describe "with invalid user ids", -> + # beforeEach (done) -> + # @updates = [{ + # meta: + # user_id: null + # op: "mock-op-1" + # }, { + # meta: + # user_id: "anonymous-user" + # op: "mock-op-2" + # }] + # @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + # callback null, @user_info[user_id] + # sinon.spy @WebApiManager, "getUserInfo" - @UpdatesManager.fillUserInfo @updates, (error, @results) => - done() + # @UpdatesManager.fillUserInfo @updates, (error, @results) => + # done() - it "should not call getUserInfo", -> - @WebApiManager.getUserInfo.called.should.equal false + # it "should not call getUserInfo", -> + # @WebApiManager.getUserInfo.called.should.equal false - it "should return the updates without the user info filled", -> - expect(@results).to.deep.equal [{ - meta: {} - op: "mock-op-1" - }, { - meta: {} - op: "mock-op-2" - }] + # it "should return the updates without the user info filled", -> + # expect(@results).to.deep.equal [{ + # meta: {} + # op: "mock-op-1" + # }, { + # meta: {} + # op: "mock-op-2" + # }] - describe "_summarizeUpdates", -> - beforeEach -> - @now = Date.now() - @user_1 = { id: "mock-user-1" } - @user_2 = { id: "mock-user-2" } + # describe "_summarizeUpdates", -> + # beforeEach -> + # @now = Date.now() + # @user_1 = { id: "mock-user-1" } + # @user_2 = { id: "mock-user-2" } - it "should concat updates that are close in time", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user: @user_1 - start_ts: @now + 20 - end_ts: @now + 30 - v: 5 - }, { - doc_id: "doc-id-1" - meta: - user: @user_2 - start_ts: @now - end_ts: @now + 10 - v: 4 - }] + # it "should concat updates that are close in time", -> + # result = @UpdatesManager._summarizeUpdates [{ + # doc_id: "doc-id-1" + # meta: + # user: @user_1 + # start_ts: @now + 20 + # end_ts: @now + 30 + # v: 5 + # }, { + # doc_id: "doc-id-1" + # meta: + # user: @user_2 + # start_ts: @now + # end_ts: @now + 10 + # v: 4 + # }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 - toV: 5 - meta: - users: [@user_1, @user_2] - start_ts: @now - end_ts: @now + 30 - }] + # expect(result).to.deep.equal [{ + # docs: + # "doc-id-1": + # fromV: 4 + # toV: 5 + # meta: + # users: [@user_1, @user_2] + # start_ts: @now + # end_ts: @now + 30 + # }] - it "should leave updates that are far apart in time", -> - oneDay = 1000 * 60 * 60 * 24 - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user: @user_2 - start_ts: @now + oneDay - end_ts: @now + oneDay + 10 - v: 5 - }, { - doc_id: "doc-id-1" - meta: - user: @user_1 - start_ts: @now - end_ts: @now + 10 - v: 4 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 5 - toV: 5 - meta: - users: [@user_2] - start_ts: @now + oneDay - end_ts: @now + oneDay + 10 - }, { - docs: - "doc-id-1": - fromV: 4 - toV: 4 - meta: - users: [@user_1] - start_ts: @now - end_ts: @now + 10 - }] + # it "should leave updates that are far apart in time", -> + # oneDay = 1000 * 60 * 60 * 24 + # result = @UpdatesManager._summarizeUpdates [{ + # doc_id: "doc-id-1" + # meta: + # user: @user_2 + # start_ts: @now + oneDay + # end_ts: @now + oneDay + 10 + # v: 5 + # }, { + # doc_id: "doc-id-1" + # meta: + # user: @user_1 + # start_ts: @now + # end_ts: @now + 10 + # v: 4 + # }] + # expect(result).to.deep.equal [{ + # docs: + # "doc-id-1": + # fromV: 5 + # toV: 5 + # meta: + # users: [@user_2] + # start_ts: @now + oneDay + # end_ts: @now + oneDay + 10 + # }, { + # docs: + # "doc-id-1": + # fromV: 4 + # toV: 4 + # meta: + # users: [@user_1] + # start_ts: @now + # end_ts: @now + 10 + # }] - it "should concat onto existing summarized updates", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-2" - meta: - user: @user_1 - start_ts: @now + 20 - end_ts: @now + 30 - v: 5 - }, { - doc_id: "doc-id-2" - meta: - user: @user_2 - start_ts: @now - end_ts: @now + 10 - v: 4 - }], [{ - docs: - "doc-id-1": - fromV: 6 - toV: 8 - meta: - users: [@user_1] - start_ts: @now + 40 - end_ts: @now + 50 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - toV: 8 - fromV: 6 - "doc-id-2": - toV: 5 - fromV: 4 - meta: - users: [@user_1, @user_2] - start_ts: @now - end_ts: @now + 50 - }] + # it "should concat onto existing summarized updates", -> + # result = @UpdatesManager._summarizeUpdates [{ + # doc_id: "doc-id-2" + # meta: + # user: @user_1 + # start_ts: @now + 20 + # end_ts: @now + 30 + # v: 5 + # }, { + # doc_id: "doc-id-2" + # meta: + # user: @user_2 + # start_ts: @now + # end_ts: @now + 10 + # v: 4 + # }], [{ + # docs: + # "doc-id-1": + # fromV: 6 + # toV: 8 + # meta: + # users: [@user_1] + # start_ts: @now + 40 + # end_ts: @now + 50 + # }] + # expect(result).to.deep.equal [{ + # docs: + # "doc-id-1": + # toV: 8 + # fromV: 6 + # "doc-id-2": + # toV: 5 + # fromV: 4 + # meta: + # users: [@user_1, @user_2] + # start_ts: @now + # end_ts: @now + 50 + # }] - it "should include null user values", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user: @user_1 - start_ts: @now + 20 - end_ts: @now + 30 - v: 5 - }, { - doc_id: "doc-id-1" - meta: - user: null - start_ts: @now - end_ts: @now + 10 - v: 4 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 - toV: 5 - meta: - users: [@user_1, null] - start_ts: @now - end_ts: @now + 30 - }] + # it "should include null user values", -> + # result = @UpdatesManager._summarizeUpdates [{ + # doc_id: "doc-id-1" + # meta: + # user: @user_1 + # start_ts: @now + 20 + # end_ts: @now + 30 + # v: 5 + # }, { + # doc_id: "doc-id-1" + # meta: + # user: null + # start_ts: @now + # end_ts: @now + 10 + # v: 4 + # }] + # expect(result).to.deep.equal [{ + # docs: + # "doc-id-1": + # fromV: 4 + # toV: 5 + # meta: + # users: [@user_1, null] + # start_ts: @now + # end_ts: @now + 30 + # }] - it "should include null user values, when the null is earlier in the updates list", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user: null - start_ts: @now - end_ts: @now + 10 - v: 4 - }, { - doc_id: "doc-id-1" - meta: - user: @user_1 - start_ts: @now + 20 - end_ts: @now + 30 - v: 5 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 - toV: 5 - meta: - users: [null, @user_1] - start_ts: @now - end_ts: @now + 30 - }] + # it "should include null user values, when the null is earlier in the updates list", -> + # result = @UpdatesManager._summarizeUpdates [{ + # doc_id: "doc-id-1" + # meta: + # user: null + # start_ts: @now + # end_ts: @now + 10 + # v: 4 + # }, { + # doc_id: "doc-id-1" + # meta: + # user: @user_1 + # start_ts: @now + 20 + # end_ts: @now + 30 + # v: 5 + # }] + # expect(result).to.deep.equal [{ + # docs: + # "doc-id-1": + # fromV: 4 + # toV: 5 + # meta: + # users: [null, @user_1] + # start_ts: @now + # end_ts: @now + 30 + # }] - it "should roll several null user values into one", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user: @user_1 - start_ts: @now + 20 - end_ts: @now + 30 - v: 5 - }, { - doc_id: "doc-id-1" - meta: - user: null - start_ts: @now - end_ts: @now + 10 - v: 4 - }, { - doc_id: "doc-id-1" - meta: - user: null - start_ts: @now + 2 - end_ts: @now + 4 - v: 4 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 - toV: 5 - meta: - users: [@user_1, null] - start_ts: @now - end_ts: @now + 30 - }] + # it "should roll several null user values into one", -> + # result = @UpdatesManager._summarizeUpdates [{ + # doc_id: "doc-id-1" + # meta: + # user: @user_1 + # start_ts: @now + 20 + # end_ts: @now + 30 + # v: 5 + # }, { + # doc_id: "doc-id-1" + # meta: + # user: null + # start_ts: @now + # end_ts: @now + 10 + # v: 4 + # }, { + # doc_id: "doc-id-1" + # meta: + # user: null + # start_ts: @now + 2 + # end_ts: @now + 4 + # v: 4 + # }] + # expect(result).to.deep.equal [{ + # docs: + # "doc-id-1": + # fromV: 4 + # toV: 5 + # meta: + # users: [@user_1, null] + # start_ts: @now + # end_ts: @now + 30 + # }] From 795f717bab1af1419f5187540141eec4334996ff Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 1 Mar 2016 11:38:23 +0000 Subject: [PATCH 275/549] added index definitions --- services/track-changes/app/coffee/MongoManager.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index dfb8c2a2e1..e906257add 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -73,3 +73,7 @@ module.exports = MongoManager = db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } # TTL index for auto deleting week old temporary ops db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true } + # For finding packs to be checked for archiving + db.docHistory.ensureIndex { last_checked: 1 }, { background: true } + # For finding archived packs + db.docHistoryIndex.ensureIndex { project_id: 1 }, { background: true } From e8b3fb5be6a9f876b092643a93e367462a56ef05 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 3 Mar 2016 10:50:55 +0000 Subject: [PATCH 276/549] added more logging to failed health checks --- services/track-changes/app/coffee/HealthChecker.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/track-changes/app/coffee/HealthChecker.coffee b/services/track-changes/app/coffee/HealthChecker.coffee index 0c3aa47284..ff5caf8efa 100644 --- a/services/track-changes/app/coffee/HealthChecker.coffee +++ b/services/track-changes/app/coffee/HealthChecker.coffee @@ -15,6 +15,7 @@ module.exports = (cb)-> request.get {url:"http://localhost:#{port}/check_lock", timeout:3000}, (err, res, body) -> if err? + logger.err err:err, project_id:project_id, "error checking lock for health check" cb(err) else if res?.statusCode != 200 cb("status code not 200, it's #{res.statusCode}") @@ -23,6 +24,7 @@ module.exports = (cb)-> request.post {url:"#{url}/flush", timeout:3000}, (err, res, body) -> if err? + logger.err err:err, project_id:project_id, "error flushing for health check" cb(err) else if res?.statusCode != 204 cb("status code not 204, it's #{res.statusCode}") @@ -31,6 +33,7 @@ module.exports = (cb)-> request.get {url:"#{url}/updates", timeout:3000}, (err, res, body)-> if err? + logger.err err:err, project_id:project_id, "error getting updates for health check" cb(err) else if res?.statusCode != 200 cb("status code not 200, it's #{res.statusCode}") From 3175f6d3a69cab4379b9613435c16854874fff92 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 3 Mar 2016 14:35:53 +0000 Subject: [PATCH 277/549] handle case where index does not exist --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 4eba1bff2d..f6e4aa626c 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -281,7 +281,7 @@ module.exports = PackManager = return callback(err) if err? return callback() if not historyPacks? # select only the new packs not already in the index - newPacks = (pack for pack in historyPacks when not indexResult[pack._id]?) + newPacks = (pack for pack in historyPacks when not indexResult?[pack._id]?) newPacks = (_.omit(pack, 'doc_id', 'project_id', 'n', 'sz') for pack in newPacks) logger.log {project_id, doc_id, n: newPacks.length}, "found new packs" callback(null, newPacks) From 1419d20b1f4bc35ef391beecd39c3f17ac3716cd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 4 Mar 2016 15:43:32 +0000 Subject: [PATCH 278/549] fix indentation --- services/track-changes/app/coffee/MongoAWS.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index cfa256e0f6..4ee2acaa7a 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -85,7 +85,7 @@ module.exports = MongoAWS = outputStream = inputStream.pipe gunzip parts = [] outputStream.on 'error', (err) -> - return callback(err) + return callback(err) outputStream.on 'end', () -> logger.log {project_id, doc_id, pack_id}, "download from s3 completed" try From 4d58c145731b97fb066188f75fc855330020330a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 4 Mar 2016 15:44:38 +0000 Subject: [PATCH 279/549] updated MongoAWS tests --- services/track-changes/package.json | 3 +- .../unit/coffee/DocArchive/MongoAWS.coffee | 106 +++++------------- 2 files changed, 32 insertions(+), 77 deletions(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index be0c3487a0..cc2a0bc105 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -39,6 +39,7 @@ "bunyan": "~0.22.1", "grunt-bunyan": "~0.5.0", "grunt-forever": "~0.4.2", - "timekeeper": "0.0.4" + "timekeeper": "0.0.4", + "memorystream": "0.3.1" } } diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index dd285075a0..6c3cf774a9 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -4,6 +4,8 @@ sinon = require("sinon") modulePath = "../../../../app/js/MongoAWS.js" SandboxedModule = require('sandboxed-module') {ObjectId} = require("mongojs") +MemoryStream = require('memorystream') +zlib = require "zlib" describe "MongoAWS", -> beforeEach -> @@ -20,95 +22,47 @@ describe "MongoAWS", -> "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} "aws-sdk": @awssdk = {} "fs": @fs = {} - "s3-streams": @s3streams = {} + "s3-streams": @S3S = {} "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "JSONStream": @JSONStream = {} "readline-stream": @readline = sinon.stub() @project_id = ObjectId().toString() @doc_id = ObjectId().toString() + @pack_id = ObjectId() @update = { v:123 } @callback = sinon.stub() - - # describe "archiveDocHistory", -> - # beforeEach -> - # @awssdk.config = { update: sinon.stub() } - # @awssdk.S3 = sinon.stub() - # @s3streams.WriteStream = sinon.stub() - # @db.docHistory = {} - # @db.docHistory.on = sinon.stub() - # @db.docHistory.find = sinon.stub().returns @db.docHistory - # @db.docHistory.on.returns - # pipe:-> - # pipe:-> - # on: (type, cb)-> - # on: (type, cb)-> - # cb() - # @JSONStream.stringify = sinon.stub() + describe "archivePack", -> - # @MongoAWS.archiveDocHistory @project_id, @doc_id, @update, @callback + beforeEach (done) -> + @awssdk.config = { update: sinon.stub() } + @awssdk.S3 = sinon.stub() + @S3S.WriteStream = MemoryStream.createWriteStream + @db.docHistory = {} + @db.docHistory.findOne = sinon.stub().callsArgWith(1, null, {"pack":"hello"}) - # it "should call the callback", -> - # @callback.called.should.equal true + @MongoAWS.archivePack @project_id, @doc_id, @pack_id, (err, result) => + @callback() + done() - # describe "unArchiveDocHistory", -> + it "should call the callback", -> + @callback.called.should.equal true - # beforeEach -> - # @awssdk.config = { update: sinon.stub() } - # @awssdk.S3 = sinon.stub() - # @s3streams.ReadStream = sinon.stub() + describe "unArchivePack", -> - # @s3streams.ReadStream.returns - # #describe on 'open' behavior - # on: (type, cb)-> - # #describe on 'error' behavior - # on: (type, cb)-> - # pipe:-> - # #describe on 'data' behavior - # on: (type, cb)-> - # cb([]) - # #describe on 'end' behavior - # on: (type, cb)-> - # cb() - # #describe on 'error' behavior - # on: sinon.stub() + beforeEach (done) -> + zlib.gzip '{"pack":"123"}', (err, zbuf) => + @awssdk.config = { update: sinon.stub() } + @awssdk.S3 = sinon.stub() + @S3S.ReadStream = () -> + MemoryStream.createReadStream(zbuf, {readable:true}) + @db.docHistory = {} + @db.docHistory.insert = sinon.stub().callsArgWith(1, null, "pack") - # @MongoAWS.handleBulk = sinon.stub() - # @MongoAWS.unArchiveDocHistory @project_id, @doc_id, @callback - - # it "should call handleBulk", -> - # @MongoAWS.handleBulk.called.should.equal true - - # describe "handleBulk", -> - # beforeEach -> - # @bulkOps = [{ - # _id: ObjectId() - # doc_id: ObjectId() - # project_id: ObjectId() - # }, { - # _id: ObjectId() - # doc_id: ObjectId() - # project_id: ObjectId() - # }, { - # _id: ObjectId() - # doc_id: ObjectId() - # project_id: ObjectId() - # }] - # @bulk = - # find: sinon.stub().returns - # upsert: sinon.stub().returns - # updateOne: sinon.stub() - # execute: sinon.stub().callsArgWith(0, null, {}) - # @db.docHistory = {} - # @db.docHistory.initializeUnorderedBulkOp = sinon.stub().returns @bulk - # @MongoAWS.handleBulk @bulkOps, @bulkOps.length, @callback - - # it "should call updateOne for each operation", -> - # @bulk.find.calledWith({_id:@bulkOps[0]._id}).should.equal true - # @bulk.find.calledWith({_id:@bulkOps[1]._id}).should.equal true - # @bulk.find.calledWith({_id:@bulkOps[2]._id}).should.equal true - - # it "should call the callback", -> - # @callback.calledWith(null).should.equal true + @MongoAWS.unArchivePack @project_id, @doc_id, @pack_id, (err, result) => + @callback() + done() + it "should call db.docHistory.insert", -> + @db.docHistory.insert.called.should.equal true From b780df6801870e13e72ab6e72730d9a5a6bd6a29 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 4 Mar 2016 15:47:59 +0000 Subject: [PATCH 280/549] removed unused tests --- .../HttpController/HttpControllerTests.coffee | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index bd74a719b4..ec33748536 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -133,38 +133,3 @@ describe "HttpController", -> it "should return a success code", -> @res.send.calledWith(204).should.equal true - # describe "archiveProject", -> - # beforeEach -> - # @req = - # params: - # project_id: @project_id - # @res = - # send: sinon.stub() - # @DocArchiveManager.archiveAllDocsChanges = sinon.stub().callsArg(1) - # @HttpController.archiveProject @req, @res, @next - - # it "should process archive doc changes", -> - # @DocArchiveManager.archiveAllDocsChanges - # .calledWith(@project_id) - # .should.equal true - - # it "should return a success code", -> - # @res.send.calledWith(204).should.equal true - - # describe "unArchiveProject", -> - # beforeEach -> - # @req = - # params: - # project_id: @project_id - # @res = - # send: sinon.stub() - # @DocArchiveManager.unArchiveAllDocsChanges = sinon.stub().callsArg(1) - # @HttpController.unArchiveProject @req, @res, @next - - # it "should process unarchive doc changes", -> - # @DocArchiveManager.unArchiveAllDocsChanges - # .calledWith(@project_id) - # .should.equal true - - # it "should return a success code", -> - # @res.send.calledWith(204).should.equal true From 7e6ea2793bcd8c0e38a81ba7de2a6c3d994e8b1d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 13:28:02 +0000 Subject: [PATCH 281/549] remove startup dependency on s3 settings --- services/track-changes/app/coffee/MongoAWS.coffee | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 4ee2acaa7a..c6712e29f1 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -9,12 +9,11 @@ zlib = require "zlib" DAYS = 24 * 3600 * 1000 # one day in milliseconds -AWS_CONFIG = { - accessKeyId: settings.trackchanges.s3.key - secretAccessKey: settings.trackchanges.s3.secret -} - createStream = (streamConstructor, project_id, doc_id, pack_id) -> + AWS_CONFIG = + accessKeyId: settings.trackchanges.s3.key + secretAccessKey: settings.trackchanges.s3.secret + return streamConstructor new AWS.S3(AWS_CONFIG), { "Bucket": settings.trackchanges.stores.doc_history, "Key": project_id+"/changes-"+doc_id+"/pack-"+pack_id From b0d2e22c58d94a95c4a976a949d70cf06e5a3e29 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 13:28:22 +0000 Subject: [PATCH 282/549] update config file for changes s3 settings --- services/track-changes/config/settings.defaults.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 59fb94ea6a..749887f4da 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -23,13 +23,13 @@ module.exports = port: 6379 pass: "" - filestore: - backend: "s3" - stores: - user_files: "" + trackchanges: s3: key: "" secret: "" + stores: + doc_history: "" + path: dumpFolder: Path.join(TMP_DIR, "dumpFolder") From 4e63c50a75bcc36a9bd77312f7ebb01b9ca3ed4f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 13:42:46 +0000 Subject: [PATCH 283/549] update MongoManager tests --- .../MongoManager/MongoManagerTests.coffee | 258 ------------------ 1 file changed, 258 deletions(-) diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 9a94f0bcf1..4088c46c0e 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -97,184 +97,6 @@ describe "MongoManager", -> @callback.calledWith(null, null, @update.v_end).should.equal true - # describe "getDocUpdates", -> - # beforeEach -> - # @results = [ - # {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, - # {pack: [ {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 52, doc_id: 100, project_id: 1} ] - # , v: 52, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 42, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 41, doc_id: 100, project_id: 1} - # ] - # @updates_between = [ - # {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} - # ] - # @updates_after = [ - # {foo: "mock-update", v: 56, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 55, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 54, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 53, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 52, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 42, doc_id: 100, project_id: 1} - # ] - # @db.docHistory = {} - # @db.docHistory.find = sinon.stub().returns @db.docHistory - # @db.docHistory.sort = sinon.stub().returns @db.docHistory - # @db.docHistory.limit = sinon.stub().returns @db.docHistory - # @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) - - # @from = 42 - # @to = 55 - - # describe "with a to version", -> - # beforeEach -> - # @MongoManager.getDocUpdates @doc_id, from: @from, to: @to, @callback - - # it "should find the all updates between the to and from versions", -> - # @db.docHistory.find - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # v: { $gte: @from, $lte: @to } - # }) - # .should.equal true - - # it "should sort in descending version order", -> - # @db.docHistory.sort - # .calledWith("v": -1) - # .should.equal true - - # #it "should not limit the results", -> - # # @db.docHistory.limit - # # .called.should.equal false - - # it "should call the call back with the results", -> - # @callback.calledWith(null, @updates_between).should.equal true - - # describe "without a to version", -> - # beforeEach -> - # @MongoManager.getDocUpdates @doc_id, from: @from, @callback - - # it "should find the all updates after the from version", -> - # @db.docHistory.find - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # v: { $gte: @from } - # }) - # .should.equal true - - # it "should call the call back with the updates", -> - # @callback.calledWith(null, @updates_after).should.equal true - - # describe "with a limit", -> - # beforeEach -> - # @MongoManager.getDocUpdates @doc_id, from: @from, limit: @limit = 10, @callback - - # it "should limit the results", -> - # @db.docHistory.limit - # .calledWith(@limit) - # .should.equal true - - - # describe "getDocUpdates", -> - # beforeEach -> - # @results = [ - # {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, - # {pack: [ - # {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1} ] - # , v: 52, meta: {end_ts: 100}, doc_id: 300, project_id: 1}, - # {pack: [ - # {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1} ] - # , v: 52, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 42, meta:{end_ts: 90}, doc_id: 100, project_id: 1} - # ] - # @updates_before = [ - # {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1}, - # ] - # @updates_all = [ - # {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1}, - # {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1}, - # {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1}, - # {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1} - # ] - - - # @db.docHistory = {} - # @db.docHistory.find = sinon.stub().returns @db.docHistory - # @db.docHistory.sort = sinon.stub().returns @db.docHistory - # @db.docHistory.limit = sinon.stub().returns @db.docHistory - # @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results) - - # @before = 101 - - # describe "with a before timestamp", -> - # beforeEach -> - # @MongoManager.getProjectUpdates @project_id, before: @before, @callback - - # it "should find the all updates before the timestamp", -> - # @db.docHistory.find - # .calledWith({ - # project_id: ObjectId(@project_id) - # "meta.end_ts": { $lt: @before } - # }) - # .should.equal true - - # it "should sort in descending version order", -> - # @db.docHistory.sort - # .calledWith("meta.end_ts": -1) - # .should.equal true - - # it "should not limit the results", -> - # @db.docHistory.limit - # .called.should.equal false - - # it "should call the call back with the updates", -> - # @callback.calledWith(null, @updates_before).should.equal true - - # describe "without a before timestamp", -> - # beforeEach -> - # @MongoManager.getProjectUpdates @project_id, {}, @callback - - # it "should find the all updates", -> - # @db.docHistory.find - # .calledWith({ - # project_id: ObjectId(@project_id) - # }) - # .should.equal true - - # it "should call the call back with the updates", -> - # @callback.calledWith(null, @updates_all).should.equal true - - # describe "with a limit", -> - # beforeEach -> - # @MongoManager.getProjectUpdates @project_id, before: @before, limit: @limit = 10, @callback - - # it "should limit the results", -> - # @db.docHistory.limit - # .calledWith(@limit) - # .should.equal true - describe "backportProjectId", -> beforeEach -> @db.docHistory = @@ -332,83 +154,3 @@ describe "MongoManager", -> it "should call the callback", -> @callback.called.should.equal true - # describe "getDocChangesCount", -> - # beforeEach -> - # @db.docHistory = - # count: sinon.stub().callsArg(1) - # @MongoManager.getDocChangesCount @doc_id, @callback - - # it "should return if there is any doc changes", -> - # @db.docHistory.count - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # }) - # .should.equal true - - # it "should call the callback", -> - # @callback.called.should.equal true - - # describe "getArchivedDocStatus", -> - # beforeEach -> - # @db.docHistoryStats = - # findOne: sinon.stub().callsArg(2) - # @MongoManager.getArchivedDocStatus @doc_id, @callback - - # it "should return if there is any archived doc changes", -> - # @db.docHistoryStats.findOne - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # inS3: {$exists: true} - # }) - # .should.equal true - - # it "should call the callback", -> - # @callback.called.should.equal true - - # describe "markDocHistoryAsArchived", -> - # beforeEach -> - # @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"} - # @db.docHistoryStats = - # update: sinon.stub().callsArg(3) - # @db.docHistory = - # remove: sinon.stub().callsArg(1) - # @MongoManager.markDocHistoryAsArchived @doc_id, @update.v, @callback - - # it "should update doc status with inS3 flag", -> - # @db.docHistoryStats.update - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # },{ - # $set : { inS3 : true } - # }) - # .should.equal true - - # it "should remove any other doc changes before last update", -> - # @db.docHistory.remove - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # v: { $lte : @update.v } - # expiresAt: {$exists : false} - # }) - # .should.equal true - - # it "should call the callback", -> - # @callback.called.should.equal true - - # describe "markDocHistoryAsUnarchived", -> - # beforeEach -> - # @db.docHistoryStats = - # update: sinon.stub().callsArg(2) - # @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback - - # it "should remove any doc changes inS3 flag", -> - # @db.docHistoryStats.update - # .calledWith({ - # doc_id: ObjectId(@doc_id) - # },{ - # $unset : { inS3 : true, lastVersion: true } - # }) - # .should.equal true - - # it "should call the callback", -> - # @callback.called.should.equal true From bfc9494cc12e44cb7a850fcf9fada6e05d84be1f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 14:01:04 +0000 Subject: [PATCH 284/549] update UpdatesManager tests --- .../UpdatesManager/UpdatesManagerTests.coffee | 500 +++++++++--------- 1 file changed, 251 insertions(+), 249 deletions(-) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 92ee3cc693..d320421173 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -265,50 +265,74 @@ describe "UpdatesManager", -> it "should call the callback", -> @callback.called.should.equal true - # describe "getDocUpdates", -> - # beforeEach -> - # @updates = ["mock-updates"] - # @options = { to: "mock-to", limit: "mock-limit" } - # @MongoManager.getDocUpdates = sinon.stub().callsArgWith(2, null, @updates) - # @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - # @UpdatesManager.getDocUpdates @project_id, @doc_id, @options, @callback + describe "getDocUpdates", -> + beforeEach -> + @updates = ["mock-updates"] + @options = { to: "mock-to", limit: "mock-limit" } + @PackManager.getOpsByVersionRange = sinon.stub().callsArgWith(4, null, @updates) + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + @UpdatesManager.getDocUpdates @project_id, @doc_id, @options, @callback - # it "should process outstanding updates", -> - # @UpdatesManager.processUncompressedUpdatesWithLock - # .calledWith(@project_id, @doc_id) - # .should.equal true + it "should process outstanding updates", -> + @UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(@project_id, @doc_id) + .should.equal true - # it "should get the updates from the database", -> - # @MongoManager.getDocUpdates - # .calledWith(@doc_id, @options) - # .should.equal true + it "should get the updates from the database", -> + @PackManager.getOpsByVersionRange + .calledWith(@project_id, @doc_id, @options.from, @options.to) + .should.equal true - # it "should return the updates", -> - # @callback - # .calledWith(null, @updates) - # .should.equal true + it "should return the updates", -> + @callback + .calledWith(null, @updates) + .should.equal true - # describe "getDocUpdatesWithUserInfo", -> - # beforeEach -> - # @updates = ["mock-updates"] - # @options = { to: "mock-to", limit: "mock-limit" } - # @updatesWithUserInfo = ["updates-with-user-info"] - # @UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, @updates) - # @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - # @UpdatesManager.getDocUpdatesWithUserInfo @project_id, @doc_id, @options, @callback + describe "getDocUpdatesWithUserInfo", -> + beforeEach -> + @updates = ["mock-updates"] + @options = { to: "mock-to", limit: "mock-limit" } + @updatesWithUserInfo = ["updates-with-user-info"] + @UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, @updates) + @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) + @UpdatesManager.getDocUpdatesWithUserInfo @project_id, @doc_id, @options, @callback - # it "should get the updates", -> - # @UpdatesManager.getDocUpdates - # .calledWith(@project_id, @doc_id, @options) - # .should.equal true + it "should get the updates", -> + @UpdatesManager.getDocUpdates + .calledWith(@project_id, @doc_id, @options) + .should.equal true - # it "should file the updates with the user info", -> - # @UpdatesManager.fillUserInfo - # .calledWith(@updates) - # .should.equal true + it "should file the updates with the user info", -> + @UpdatesManager.fillUserInfo + .calledWith(@updates) + .should.equal true + + it "should return the updates with the filled details", -> + @callback.calledWith(null, @updatesWithUserInfo).should.equal true + + describe "processUncompressedUpdatesForProject", -> + beforeEach (done) -> + @doc_ids = ["mock-id-1", "mock-id-2"] + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) + @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => + @callback() + done() + + it "should get all the docs with history ops", -> + @RedisManager.getDocIdsWithHistoryOps + .calledWith(@project_id) + .should.equal true + + it "should process the doc ops for the each doc_id", -> + for doc_id in @doc_ids + @UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(@project_id, doc_id) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true - # it "should return the updates with the filled details", -> - # @callback.calledWith(null, @updatesWithUserInfo).should.equal true # describe "getProjectUpdates", -> # beforeEach -> @@ -333,28 +357,6 @@ describe "UpdatesManager", -> # .calledWith(null, @updates) # .should.equal true - # describe "processUncompressedUpdatesForProject", -> - # beforeEach (done) -> - # @doc_ids = ["mock-id-1", "mock-id-2"] - # @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - # @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) - # @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => - # @callback() - # done() - - # it "should get all the docs with history ops", -> - # @RedisManager.getDocIdsWithHistoryOps - # .calledWith(@project_id) - # .should.equal true - - # it "should process the doc ops for the each doc_id", -> - # for doc_id in @doc_ids - # @UpdatesManager.processUncompressedUpdatesWithLock - # .calledWith(@project_id, doc_id) - # .should.equal true - - # it "should call the callback", -> - # @callback.called.should.equal true # describe "getProjectUpdatesWithUserInfo", -> # beforeEach -> @@ -571,200 +573,200 @@ describe "UpdatesManager", -> # op: "mock-op-2" # }] - # describe "_summarizeUpdates", -> - # beforeEach -> - # @now = Date.now() - # @user_1 = { id: "mock-user-1" } - # @user_2 = { id: "mock-user-2" } + describe "_summarizeUpdates", -> + beforeEach -> + @now = Date.now() + @user_1 = { id: "mock-user-1" } + @user_2 = { id: "mock-user-2" } - # it "should concat updates that are close in time", -> - # result = @UpdatesManager._summarizeUpdates [{ - # doc_id: "doc-id-1" - # meta: - # user: @user_1 - # start_ts: @now + 20 - # end_ts: @now + 30 - # v: 5 - # }, { - # doc_id: "doc-id-1" - # meta: - # user: @user_2 - # start_ts: @now - # end_ts: @now + 10 - # v: 4 - # }] + it "should concat updates that are close in time", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user_id: @user_1.id + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user_id: @user_2.id + start_ts: @now + end_ts: @now + 10 + v: 4 + }] - # expect(result).to.deep.equal [{ - # docs: - # "doc-id-1": - # fromV: 4 - # toV: 5 - # meta: - # users: [@user_1, @user_2] - # start_ts: @now - # end_ts: @now + 30 - # }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + user_ids: [@user_1.id, @user_2.id] + start_ts: @now + end_ts: @now + 30 + }] - # it "should leave updates that are far apart in time", -> - # oneDay = 1000 * 60 * 60 * 24 - # result = @UpdatesManager._summarizeUpdates [{ - # doc_id: "doc-id-1" - # meta: - # user: @user_2 - # start_ts: @now + oneDay - # end_ts: @now + oneDay + 10 - # v: 5 - # }, { - # doc_id: "doc-id-1" - # meta: - # user: @user_1 - # start_ts: @now - # end_ts: @now + 10 - # v: 4 - # }] - # expect(result).to.deep.equal [{ - # docs: - # "doc-id-1": - # fromV: 5 - # toV: 5 - # meta: - # users: [@user_2] - # start_ts: @now + oneDay - # end_ts: @now + oneDay + 10 - # }, { - # docs: - # "doc-id-1": - # fromV: 4 - # toV: 4 - # meta: - # users: [@user_1] - # start_ts: @now - # end_ts: @now + 10 - # }] + it "should leave updates that are far apart in time", -> + oneDay = 1000 * 60 * 60 * 24 + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user_id: @user_2.id + start_ts: @now + oneDay + end_ts: @now + oneDay + 10 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user_id: @user_1.id + start_ts: @now + end_ts: @now + 10 + v: 4 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 5 + toV: 5 + meta: + user_ids: [@user_2.id] + start_ts: @now + oneDay + end_ts: @now + oneDay + 10 + }, { + docs: + "doc-id-1": + fromV: 4 + toV: 4 + meta: + user_ids: [@user_1.id] + start_ts: @now + end_ts: @now + 10 + }] - # it "should concat onto existing summarized updates", -> - # result = @UpdatesManager._summarizeUpdates [{ - # doc_id: "doc-id-2" - # meta: - # user: @user_1 - # start_ts: @now + 20 - # end_ts: @now + 30 - # v: 5 - # }, { - # doc_id: "doc-id-2" - # meta: - # user: @user_2 - # start_ts: @now - # end_ts: @now + 10 - # v: 4 - # }], [{ - # docs: - # "doc-id-1": - # fromV: 6 - # toV: 8 - # meta: - # users: [@user_1] - # start_ts: @now + 40 - # end_ts: @now + 50 - # }] - # expect(result).to.deep.equal [{ - # docs: - # "doc-id-1": - # toV: 8 - # fromV: 6 - # "doc-id-2": - # toV: 5 - # fromV: 4 - # meta: - # users: [@user_1, @user_2] - # start_ts: @now - # end_ts: @now + 50 - # }] + it "should concat onto existing summarized updates", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-2" + meta: + user_id: @user_1.id + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-2" + meta: + user_id: @user_2.id + start_ts: @now + end_ts: @now + 10 + v: 4 + }], [{ + docs: + "doc-id-1": + fromV: 6 + toV: 8 + meta: + user_ids: [@user_1.id] + start_ts: @now + 40 + end_ts: @now + 50 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + toV: 8 + fromV: 6 + "doc-id-2": + toV: 5 + fromV: 4 + meta: + user_ids: [@user_1.id, @user_2.id] + start_ts: @now + end_ts: @now + 50 + }] - # it "should include null user values", -> - # result = @UpdatesManager._summarizeUpdates [{ - # doc_id: "doc-id-1" - # meta: - # user: @user_1 - # start_ts: @now + 20 - # end_ts: @now + 30 - # v: 5 - # }, { - # doc_id: "doc-id-1" - # meta: - # user: null - # start_ts: @now - # end_ts: @now + 10 - # v: 4 - # }] - # expect(result).to.deep.equal [{ - # docs: - # "doc-id-1": - # fromV: 4 - # toV: 5 - # meta: - # users: [@user_1, null] - # start_ts: @now - # end_ts: @now + 30 - # }] + it "should include null user values", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user_id: @user_1.id + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user_id: null + start_ts: @now + end_ts: @now + 10 + v: 4 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + user_ids: [@user_1.id, null] + start_ts: @now + end_ts: @now + 30 + }] - # it "should include null user values, when the null is earlier in the updates list", -> - # result = @UpdatesManager._summarizeUpdates [{ - # doc_id: "doc-id-1" - # meta: - # user: null - # start_ts: @now - # end_ts: @now + 10 - # v: 4 - # }, { - # doc_id: "doc-id-1" - # meta: - # user: @user_1 - # start_ts: @now + 20 - # end_ts: @now + 30 - # v: 5 - # }] - # expect(result).to.deep.equal [{ - # docs: - # "doc-id-1": - # fromV: 4 - # toV: 5 - # meta: - # users: [null, @user_1] - # start_ts: @now - # end_ts: @now + 30 - # }] + it "should include null user values, when the null is earlier in the updates list", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user_id: null + start_ts: @now + end_ts: @now + 10 + v: 4 + }, { + doc_id: "doc-id-1" + meta: + user_id: @user_1.id + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + user_ids: [null, @user_1.id] + start_ts: @now + end_ts: @now + 30 + }] - # it "should roll several null user values into one", -> - # result = @UpdatesManager._summarizeUpdates [{ - # doc_id: "doc-id-1" - # meta: - # user: @user_1 - # start_ts: @now + 20 - # end_ts: @now + 30 - # v: 5 - # }, { - # doc_id: "doc-id-1" - # meta: - # user: null - # start_ts: @now - # end_ts: @now + 10 - # v: 4 - # }, { - # doc_id: "doc-id-1" - # meta: - # user: null - # start_ts: @now + 2 - # end_ts: @now + 4 - # v: 4 - # }] - # expect(result).to.deep.equal [{ - # docs: - # "doc-id-1": - # fromV: 4 - # toV: 5 - # meta: - # users: [@user_1, null] - # start_ts: @now - # end_ts: @now + 30 - # }] + it "should roll several null user values into one", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + meta: + user_id: @user_1.id + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user_id: null + start_ts: @now + end_ts: @now + 10 + v: 4 + }, { + doc_id: "doc-id-1" + meta: + user_id: null + start_ts: @now + 2 + end_ts: @now + 4 + v: 4 + }] + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 4 + toV: 5 + meta: + user_ids: [@user_1.id, null] + start_ts: @now + end_ts: @now + 30 + }] From 8922b97bd773842090ba7ecf3f4fd6dbdbd075e2 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 14:01:48 +0000 Subject: [PATCH 285/549] avoid duplicate filling of UserInfo in getDocUpdates --- services/track-changes/app/coffee/UpdatesManager.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 537d87868d..bea6b41224 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -119,9 +119,7 @@ module.exports = UpdatesManager = #console.log "options", options PackManager.getOpsByVersionRange project_id, doc_id, options.from, options.to, (error, updates) -> return callback(error) if error? - UpdatesManager.fillUserInfo updates, (err, results) -> - return callback(err) if err? - callback null, results + callback null, updates getDocUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.getDocUpdates project_id, doc_id, options, (error, updates) -> From 28b184e0ca544a834bf2d99ecbcbdadb7ac91c67 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 14:02:07 +0000 Subject: [PATCH 286/549] fix incorrect use of _.union (argument must be array) --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index bea6b41224..6c6cf7cf01 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -221,7 +221,7 @@ module.exports = UpdatesManager = if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES # check if the user in this update is already present in the earliest update, # if not, add them to the users list of the earliest update - earliestUpdate.meta.user_ids = _.union earliestUpdate.meta.user_ids, update.meta.user_id + earliestUpdate.meta.user_ids = _.union earliestUpdate.meta.user_ids, [update.meta.user_id] doc_id = update.doc_id.toString() doc = earliestUpdate.docs[doc_id] From 7350ab531d325eb40bcb10a3deda2029b1132762 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 14:32:53 +0000 Subject: [PATCH 287/549] exclude already cached packs from archival --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index f6e4aa626c..8e9a928d3b 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -261,7 +261,7 @@ module.exports = PackManager = callback() findCompletedPacks: (project_id, doc_id, callback) -> - query = { doc_id: ObjectId(doc_id.toString()) } + query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} } db.docHistory.find(query, {pack:false}).sort {v:1}, (err, packs) -> return callback(err) if err? return callback() if not packs? From f6367e21b850f582c72b3264e266c9d1f4033c18 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 14:33:19 +0000 Subject: [PATCH 288/549] give separate error for archive in progress vs completed --- services/track-changes/app/coffee/PackManager.coffee | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 8e9a928d3b..449e07e110 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -407,10 +407,12 @@ module.exports = PackManager = PackManager.getPackFromIndex doc_id, pack_id, (err, result) -> return callback(err) if err? return callback new Error("pack not found in index") if not result? - if result.inS3? - callback new Error("pack archiving already in progress") + if result.inS3 + return callback new Error("pack archiving already done") + else if result.inS3? + return callback new Error("pack archiving already in progress") else - callback() + return callback() markPackAsArchiveInProgress: (project_id, doc_id, pack_id, callback) -> logger.log {project_id, doc_id}, "marking pack as archive in progress status" From 6d43273f04baaeb6d917afc227a73c8b5fccf93a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 14:36:40 +0000 Subject: [PATCH 289/549] working on packmanager tests --- .../PackManager/PackManagerTests.coffee | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index cb52a87c4c..1d929722bd 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -237,3 +237,64 @@ describe "PackManager", -> it "should call the callback", -> @callback.called.should.equal true + + describe "getOpsByVersionRange", -> + + describe "loadPacksByVersionRange", -> + + describe "fetchPacksIfNeeded", -> + + describe "makeProjectIterator", -> + + describe "getPackById", -> + + describe "increaseTTL", -> + + describe "getIndex", -> + + describe "getPackFromIndex", -> +# getLastPackFromIndex: +# getIndexWithKeys +# initialiseIndex +# updateIndex +# findCompletedPacks +# findUnindexedPacks +# insertPacksIntoIndexWithLock +# _insertPacksIntoIndex +# archivePack +# checkArchivedPack +# processOldPack +# updateIndexIfNeeded +# findUnarchivedPacks + + describe "checkArchiveNotInProgress", -> + + describe "when an archive is in progress", -> + beforeEach -> + @db.docHistoryIndex = + findOne: sinon.stub().callsArgWith(2, null, {inS3:false}) + @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback + it "should call the callback", -> + @callback.called.should.equal true + it "should return an error", -> + @callback.calledWith(new Error()).should.equal true + + describe "when an archive is completed", -> + beforeEach -> + @db.docHistoryIndex = + findOne: sinon.stub().callsArgWith(2, null, {inS3:true}) + @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback + it "should call the callback", -> + @callback.called.should.equal true + it "should return an error", -> + @callback.calledWith(new Error()).should.equal true + + describe "when the archive has not started or completed", -> + beforeEach -> + @db.docHistoryIndex = + findOne: sinon.stub().callsArgWith(2, null, {}) + @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback + it "should call the callback", -> + @callback.called.should.equal true + it "should return with no error", -> + @callback.calledWith(undefined).should.equal true From e2e8292590cfeb56c52476d621a8ae1bf535facf Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 15:24:28 +0000 Subject: [PATCH 290/549] update tests --- .../UpdatesManager/UpdatesManagerTests.coffee | 231 +++++++++--------- 1 file changed, 111 insertions(+), 120 deletions(-) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index d320421173..cff44f07eb 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -333,52 +333,43 @@ describe "UpdatesManager", -> it "should call the callback", -> @callback.called.should.equal true + describe "getSummarizedProjectUpdates", -> + beforeEach -> + @updates = [{doc_id: 123, v:456, op: "mock-updates", meta: {user_id: 123, start_ts: 1233, end_ts:1234}}] + @options = { before: "mock-before", limit: "mock-limit" } + @summarizedUpdates = [ + {meta: {user_ids: [123], start_ts: 1233, end_ts:1234},docs:{"123":{fromV:456,toV:456}}} + ] + @updatesWithUserInfo = ["updates-with-user-info"] + @done_state = false + @iterator = + next: (cb) => + @done_state = true + cb(null, @updates) + done: () => + @done_state + @PackManager.makeProjectIterator = sinon.stub().callsArgWith(2, null, @iterator) + @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) + @UpdatesManager.fillSummarizedUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) + @UpdatesManager.getSummarizedProjectUpdates @project_id, @options, @callback - # describe "getProjectUpdates", -> - # beforeEach -> - # @updates = ["mock-updates"] - # @options = { before: "mock-before", limit: "mock-limit" } - # @MongoManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) - # @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) - # @UpdatesManager.getProjectUpdates @project_id, @options, @callback + it "should process any outstanding updates", -> + @UpdatesManager.processUncompressedUpdatesForProject + .calledWith(@project_id) + .should.equal true - # it "should process any outstanding updates", -> - # @UpdatesManager.processUncompressedUpdatesForProject - # .calledWith(@project_id) - # .should.equal true + it "should get the updates", -> + @PackManager.makeProjectIterator + .calledWith(@project_id, @options.before) + .should.equal true - # it "should get the updates from the database", -> - # @MongoManager.getProjectUpdates - # .calledWith(@project_id, @options) - # .should.equal true + it "should fill the updates with the user info", -> + @UpdatesManager.fillSummarizedUserInfo + .calledWith(@summarizedUpdates) + .should.equal true - # it "should return the updates", -> - # @callback - # .calledWith(null, @updates) - # .should.equal true - - - # describe "getProjectUpdatesWithUserInfo", -> - # beforeEach -> - # @updates = ["mock-updates"] - # @options = { before: "mock-before", limit: "mock-limit" } - # @updatesWithUserInfo = ["updates-with-user-info"] - # @UpdatesManager.getProjectUpdates = sinon.stub().callsArgWith(2, null, @updates) - # @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - # @UpdatesManager.getProjectUpdatesWithUserInfo @project_id, @options, @callback - - # it "should get the updates", -> - # @UpdatesManager.getProjectUpdates - # .calledWith(@project_id, @options) - # .should.equal true - - # it "should file the updates with the user info", -> - # @UpdatesManager.fillUserInfo - # .calledWith(@updates) - # .should.equal true - - # it "should return the updates with the filled details", -> - # @callback.calledWith(null, @updatesWithUserInfo).should.equal true + it "should return the updates with the filled details", -> + @callback.calledWith(null, @updatesWithUserInfo).should.equal true # describe "_extendBatchOfSummarizedUpdates", -> # beforeEach -> @@ -485,93 +476,93 @@ describe "UpdatesManager", -> # it "should call the callback with the updates", -> # @callback.calledWith(null, @updates, null).should.equal true - # describe "fillUserInfo", -> - # describe "with valid users", -> - # beforeEach (done) -> - # {ObjectId} = require "mongojs" - # @user_id_1 = ObjectId().toString() - # @user_id_2 = ObjectId().toString() - # @updates = [{ - # meta: - # user_id: @user_id_1 - # op: "mock-op-1" - # }, { - # meta: - # user_id: @user_id_1 - # op: "mock-op-2" - # }, { - # meta: - # user_id: @user_id_2 - # op: "mock-op-3" - # }] - # @user_info = {} - # @user_info[@user_id_1] = email: "user1@sharelatex.com" - # @user_info[@user_id_2] = email: "user2@sharelatex.com" + describe "fillUserInfo", -> + describe "with valid users", -> + beforeEach (done) -> + {ObjectId} = require "mongojs" + @user_id_1 = ObjectId().toString() + @user_id_2 = ObjectId().toString() + @updates = [{ + meta: + user_id: @user_id_1 + op: "mock-op-1" + }, { + meta: + user_id: @user_id_1 + op: "mock-op-2" + }, { + meta: + user_id: @user_id_2 + op: "mock-op-3" + }] + @user_info = {} + @user_info[@user_id_1] = email: "user1@sharelatex.com" + @user_info[@user_id_2] = email: "user2@sharelatex.com" - # @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - # callback null, @user_info[user_id] - # sinon.spy @WebApiManager, "getUserInfo" + @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + callback null, @user_info[user_id] + sinon.spy @WebApiManager, "getUserInfo" - # @UpdatesManager.fillUserInfo @updates, (error, @results) => - # done() + @UpdatesManager.fillUserInfo @updates, (error, @results) => + done() - # it "should only call getUserInfo once for each user_id", -> - # @WebApiManager.getUserInfo.calledTwice.should.equal true - # @WebApiManager.getUserInfo - # .calledWith(@user_id_1) - # .should.equal true - # @WebApiManager.getUserInfo - # .calledWith(@user_id_2) - # .should.equal true + it "should only call getUserInfo once for each user_id", -> + @WebApiManager.getUserInfo.calledTwice.should.equal true + @WebApiManager.getUserInfo + .calledWith(@user_id_1) + .should.equal true + @WebApiManager.getUserInfo + .calledWith(@user_id_2) + .should.equal true - # it "should return the updates with the user info filled", -> - # expect(@results).to.deep.equal [{ - # meta: - # user: - # email: "user1@sharelatex.com" - # op: "mock-op-1" - # }, { - # meta: - # user: - # email: "user1@sharelatex.com" - # op: "mock-op-2" - # }, { - # meta: - # user: - # email: "user2@sharelatex.com" - # op: "mock-op-3" - # }] + it "should return the updates with the user info filled", -> + expect(@results).to.deep.equal [{ + meta: + user: + email: "user1@sharelatex.com" + op: "mock-op-1" + }, { + meta: + user: + email: "user1@sharelatex.com" + op: "mock-op-2" + }, { + meta: + user: + email: "user2@sharelatex.com" + op: "mock-op-3" + }] - # describe "with invalid user ids", -> - # beforeEach (done) -> - # @updates = [{ - # meta: - # user_id: null - # op: "mock-op-1" - # }, { - # meta: - # user_id: "anonymous-user" - # op: "mock-op-2" - # }] - # @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - # callback null, @user_info[user_id] - # sinon.spy @WebApiManager, "getUserInfo" + describe "with invalid user ids", -> + beforeEach (done) -> + @updates = [{ + meta: + user_id: null + op: "mock-op-1" + }, { + meta: + user_id: "anonymous-user" + op: "mock-op-2" + }] + @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => + callback null, @user_info[user_id] + sinon.spy @WebApiManager, "getUserInfo" - # @UpdatesManager.fillUserInfo @updates, (error, @results) => - # done() + @UpdatesManager.fillUserInfo @updates, (error, @results) => + done() - # it "should not call getUserInfo", -> - # @WebApiManager.getUserInfo.called.should.equal false + it "should not call getUserInfo", -> + @WebApiManager.getUserInfo.called.should.equal false - # it "should return the updates without the user info filled", -> - # expect(@results).to.deep.equal [{ - # meta: {} - # op: "mock-op-1" - # }, { - # meta: {} - # op: "mock-op-2" - # }] + it "should return the updates without the user info filled", -> + expect(@results).to.deep.equal [{ + meta: {} + op: "mock-op-1" + }, { + meta: {} + op: "mock-op-2" + }] describe "_summarizeUpdates", -> beforeEach -> From f01bf996822b18c0187ef8fa35544ab723a10465 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Mar 2016 16:56:49 +0000 Subject: [PATCH 291/549] acceptance tests - work in progress --- services/track-changes/app.coffee | 3 + .../app/coffee/HttpController.coffee | 16 ++++ .../app/coffee/PackManager.coffee | 11 +++ .../coffee/ArchivingUpdatesTests.coffee | 84 ++++++++++--------- .../coffee/helpers/TrackChangesClient.coffee | 18 ++-- 5 files changed, 83 insertions(+), 49 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index f12691027b..1e5c4e1635 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -48,6 +48,9 @@ app.post "/project/:project_id/flush", HttpController.flushProject app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore +app.post '/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory +app.post '/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory + packWorker = null # use a single packing worker app.post "/pack", (req, res, next) -> diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 6c357c505f..eecc618330 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -77,6 +77,22 @@ module.exports = HttpController = return next(error) if error? res.send 204 + pushDocHistory: (req, res, next = (error) ->) -> + project_id = req.params.project_id + doc_id = req.params.doc_id + logger.log {project_id, doc_id}, "pushing all finalised changes to s3" + PackManager.pushOldPacks project_id, doc_id, (error) -> + return next(error) if error? + res.send 204 + + pullDocHistory: (req, res, next = (error) ->) -> + project_id = req.params.project_id + doc_id = req.params.doc_id + logger.log {project_id, doc_id}, "pulling all packs from s3" + PackManager.pullOldPacks project_id, doc_id, (error) -> + return next(error) if error? + res.send 204 + healthCheck: (req, res)-> HealthChecker.check (err)-> if err? diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 449e07e110..4d48018190 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -349,6 +349,17 @@ module.exports = PackManager = else logger.err {pack, result, jsondiff: JSON.stringify(pack) is JSON.stringify(result)}, "difference when comparing packs" callback new Error("pack retrieved from s3 does not match pack in mongo") + # Extra methods to test archive/unarchive for a doc_id + + pushOldPacks: (project_id, doc_id, callback) -> + PackManager.findCompletedPacks project_id, doc_id, (err, packs) -> + return callback(err) if err? + return callback() if not packs?.length + PackManager.processOldPack project_id, doc_id, packs[0]._id, callback + + pullOldPacks: (project_id, doc_id, callback) -> + PackManager.loadPacksByVersionRange project_id, doc_id, null, null, callback + # Processing old packs via worker diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index ab06b991b8..02422d3456 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -42,15 +42,15 @@ describe "Archiving updates", -> sinon.spy MockDocStoreApi, "getAllDoc" @updates = [] - for i in [0..9] + for i in [0..1024+9] @updates.push { op: [{ i: "a", p: 0 }] - meta: { ts: @now - (9 - i) * @hours - 2 * @minutes, user_id: @user_id } + meta: { ts: @now - i * @hours, user_id: @user_id } v: 2 * i + 1 } @updates.push { op: [{ i: "b", p: 0 }] - meta: { ts: @now - (9 - i) * @hours, user_id: @user_id } + meta: { ts: @now - i * @hours + 10*@minutes, user_id: @user_id } v: 2 * i + 2 } @@ -67,49 +67,53 @@ describe "Archiving updates", -> describe "archiving a doc's updates", -> before (done) -> - TrackChangesClient.archiveProject @project_id, (error) -> + TrackChangesClient.pushDocHistory @project_id, @doc_id, (error) -> throw error if error? done() - it "should remain zero doc change", (done) -> - db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> - throw error if error? - count.should.equal 0 - done() - - it "should have docHistoryStats marked as inS3", (done) -> - db.docHistoryStats.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> - throw error if error? - doc.inS3.should.equal true - done() - - it "should have docHistoryStats with the last version", (done) -> - db.docHistoryStats.findOne { doc_id: ObjectId(@doc_id) }, (error, doc) -> - throw error if error? - doc.lastVersion.should.equal 20 - done() - - it "should store twenty doc changes in S3 in one pack", (done) -> - TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => - doc.length.should.equal 1 - doc[0].pack.length.should.equal 20 - done() - - describe "unarchiving a doc's updates", -> - before (done) -> - TrackChangesClient.unarchiveProject @project_id, (error) -> - throw error if error? - done() - - it "should restore doc changes", (done) -> - db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> + it "should have one cached pack", (done) -> + db.docHistory.count { doc_id: ObjectId(@doc_id), expiresAt:{$exists:true}}, (error, count) -> throw error if error? count.should.equal 1 done() - it "should remove doc marked as inS3", (done) -> - db.docHistoryStats.findOne {doc_id: ObjectId(@doc_id)}, (error, doc) -> + it "should have one remaining pack after cache is expired", (done) -> + db.docHistory.remove { + doc_id: ObjectId(@doc_id), + expiresAt:{$exists:true} + }, (err, result) => throw error if error? - doc.should.not.contain.key('inS3') - doc.should.not.contain.key('lastVersion') + db.docHistory.count { doc_id: ObjectId(@doc_id)}, (error, count) -> + throw error if error? + count.should.equal 1 + done() + + it "should have a docHistoryIndex entry marked as inS3", (done) -> + db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) -> + throw error if error? + index.packs[0].inS3.should.equal true + done() + + it "should have a docHistoryIndex entry with the last version", (done) -> + db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) -> + throw error if error? + index.packs[0].v_end.should.equal 100 + done() + + # it "should store twenty doc changes in S3 in one pack", (done) -> + # TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => + # doc.length.should.equal 1 + # doc[0].pack.length.should.equal 20 + # done() + + describe "unarchiving a doc's updates", -> + before (done) -> + TrackChangesClient.pullDocHistory @project_id, @doc_id, (error) -> + throw error if error? + done() + + it "should restore both packs", (done) -> + db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> + throw error if error? + count.should.equal 2 done() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index d7872e288a..0140b637ad 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -74,16 +74,16 @@ module.exports = TrackChangesClient = response.statusCode.should.equal 204 callback null - archiveProject: (project_id, callback = (error) ->) -> + pushDocHistory: (project_id, doc_id, callback = (error) ->) -> request.post { - url: "http://localhost:3015/project/#{project_id}/archive" + url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/push" }, (error, response, body) => response.statusCode.should.equal 204 callback(error) - unarchiveProject: (project_id, callback = (error) ->) -> + pullDocHistory: (project_id, doc_id, callback = (error) ->) -> request.post { - url: "http://localhost:3015/project/#{project_id}/unarchive" + url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/pull" }, (error, response, body) => response.statusCode.should.equal 204 callback(error) @@ -91,12 +91,12 @@ module.exports = TrackChangesClient = buildS3Options: (content, key)-> return { aws: - key: Settings.filestore.s3.key - secret: Settings.filestore.s3.secret - bucket: Settings.filestore.stores.user_files + key: Settings.trackchanges.s3.key + secret: Settings.trackchanges.s3.secret + bucket: Settings.trackchanges.stores.doc_history timeout: 30 * 1000 json: content - uri:"https://#{Settings.filestore.stores.user_files}.s3.amazonaws.com/#{key}" + uri:"https://#{Settings.trackchanges.stores.doc_history}.s3.amazonaws.com/#{key}" } getS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> @@ -105,4 +105,4 @@ module.exports = TrackChangesClient = removeS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id) - request.del options, callback \ No newline at end of file + request.del options, callback From 98738d1344aee455e2950b6efcd9c47333d40147 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 10 Mar 2016 15:15:29 +0000 Subject: [PATCH 292/549] fix for acceptance test --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 6c6cf7cf01..6cc7db0d3a 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -205,6 +205,8 @@ module.exports = UpdatesManager = for user_id in user_ids if UpdatesManager._validUserId(user_id) update.meta.users.push fetchedUserInfo[user_id] + else + update.meta.users.push null callback null, updates _validUserId: (user_id) -> From 5ea7a31ad961845456192957cf407d976891ba92 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 10 Mar 2016 15:15:57 +0000 Subject: [PATCH 293/549] update archiving acceptance tests --- .../coffee/ArchivingUpdatesTests.coffee | 28 +++++++++++-------- .../coffee/helpers/TrackChangesClient.coffee | 23 +++++++++++---- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 02422d3456..2dcfc1a4da 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -42,15 +42,15 @@ describe "Archiving updates", -> sinon.spy MockDocStoreApi, "getAllDoc" @updates = [] - for i in [0..1024+9] + for i in [0..512+10] @updates.push { op: [{ i: "a", p: 0 }] - meta: { ts: @now - i * @hours, user_id: @user_id } + meta: { ts: @now + (i-2048) * @hours, user_id: @user_id } v: 2 * i + 1 } @updates.push { op: [{ i: "b", p: 0 }] - meta: { ts: @now - i * @hours + 10*@minutes, user_id: @user_id } + meta: { ts: @now + (i-2048) * @hours + 10*@minutes, user_id: @user_id } v: 2 * i + 2 } @@ -62,8 +62,9 @@ describe "Archiving updates", -> after (done) -> MockWebApi.getUserInfo.restore() - db.docHistory.remove {project_id: ObjectId(@project_id)}, () -> - TrackChangesClient.removeS3Doc @project_id, @doc_id, done + db.docHistory.remove {project_id: ObjectId(@project_id)}, () => + db.docHistoryIndex.remove {project_id: ObjectId(@project_id)}, () => + TrackChangesClient.removeS3Doc @project_id, @doc_id, done describe "archiving a doc's updates", -> before (done) -> @@ -97,14 +98,19 @@ describe "Archiving updates", -> it "should have a docHistoryIndex entry with the last version", (done) -> db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) -> throw error if error? - index.packs[0].v_end.should.equal 100 + index.packs[0].v_end.should.equal 1024 done() - # it "should store twenty doc changes in S3 in one pack", (done) -> - # TrackChangesClient.getS3Doc @project_id, @doc_id, (error, res, doc) => - # doc.length.should.equal 1 - # doc[0].pack.length.should.equal 20 - # done() + it "should store 1024 doc changes in S3 in one pack", (done) -> + db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) => + throw error if error? + console.log "index", index, JSON.stringify(index) + pack_id = index.packs[0]._id + TrackChangesClient.getS3Doc @project_id, @doc_id, pack_id, (error, doc) => + console.log error, "DOC", doc + doc.n.should.equal 1024 + doc.pack.length.should.equal 1024 + done() describe "unarchiving a doc's updates", -> before (done) -> diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 0140b637ad..7bd7a094e7 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -1,3 +1,5 @@ +async = require 'async' +zlib = require 'zlib' request = require "request" rclient = require("redis").createClient() # Only works locally for now {db, ObjectId} = require "../../../../app/js/mongojs" @@ -99,10 +101,21 @@ module.exports = TrackChangesClient = uri:"https://#{Settings.trackchanges.stores.doc_history}.s3.amazonaws.com/#{key}" } - getS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> - options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id) - request.get options, callback + getS3Doc: (project_id, doc_id, pack_id, callback = (error, body) ->) -> + options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id+"/pack-"+pack_id) + options.encoding = null + request.get options, (err, res, body) -> + console.log "body", typeof body + return callback(error) if error? + zlib.gunzip body, (err, result) -> + return callback(err) if err? + callback(null, JSON.parse(result.toString())) removeS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> - options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id) - request.del options, callback + options = TrackChangesClient.buildS3Options(true, "?prefix=" + project_id + "/changes-" +doc_id) + request.get options, (error, res, body) -> + keys = body.match /[0-9a-f]{24}\/changes-[0-9a-f]{24}\/pack-[0-9a-f]{24}/g + async.eachSeries keys, (key, cb) -> + options = TrackChangesClient.buildS3Options(true, key) + request.del options, cb + , callback From 10932eb4a998312e375cbd0887dfcd84bdd8ba36 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 10 Mar 2016 15:20:32 +0000 Subject: [PATCH 294/549] remove debug logs --- .../test/acceptance/coffee/ArchivingUpdatesTests.coffee | 2 -- .../test/acceptance/coffee/helpers/TrackChangesClient.coffee | 1 - 2 files changed, 3 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 2dcfc1a4da..398856e4d3 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -104,10 +104,8 @@ describe "Archiving updates", -> it "should store 1024 doc changes in S3 in one pack", (done) -> db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) => throw error if error? - console.log "index", index, JSON.stringify(index) pack_id = index.packs[0]._id TrackChangesClient.getS3Doc @project_id, @doc_id, pack_id, (error, doc) => - console.log error, "DOC", doc doc.n.should.equal 1024 doc.pack.length.should.equal 1024 done() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 7bd7a094e7..6a193ddd9b 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -105,7 +105,6 @@ module.exports = TrackChangesClient = options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id+"/pack-"+pack_id) options.encoding = null request.get options, (err, res, body) -> - console.log "body", typeof body return callback(error) if error? zlib.gunzip body, (err, result) -> return callback(err) if err? From 9543c21dd7d3f5f5b4dc1489458189c478a040ff Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 11 Mar 2016 09:43:07 +0000 Subject: [PATCH 295/549] added npm-shrinkwrap.json to avoid bug in mongodb-core --- services/track-changes/npm-shrinkwrap.json | 1130 ++++++++++++++++++++ 1 file changed, 1130 insertions(+) create mode 100644 services/track-changes/npm-shrinkwrap.json diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json new file mode 100644 index 0000000000..a21d1f18ac --- /dev/null +++ b/services/track-changes/npm-shrinkwrap.json @@ -0,0 +1,1130 @@ +{ + "name": "history-sharelatex", + "version": "0.1.4", + "dependencies": { + "JSONStream": { + "version": "1.1.1", + "from": "JSONStream@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", + "dependencies": { + "jsonparse": { + "version": "1.2.0", + "from": "jsonparse@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.2.7 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "async": { + "version": "0.2.10", + "from": "async@>=0.2.10 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "aws-sdk": { + "version": "2.2.43", + "from": "aws-sdk@>=2.1.34 <3.0.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", + "dependencies": { + "sax": { + "version": "1.1.5", + "from": "sax@1.1.5", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" + }, + "xml2js": { + "version": "0.4.15", + "from": "xml2js@0.4.15", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" + }, + "xmlbuilder": { + "version": "2.6.2", + "from": "xmlbuilder@2.6.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", + "dependencies": { + "lodash": { + "version": "3.5.0", + "from": "lodash@>=3.5.0 <3.6.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" + } + } + } + } + }, + "bson": { + "version": "0.4.22", + "from": "bson@>=0.4.20 <0.5.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" + }, + "byline": { + "version": "4.2.1", + "from": "byline@>=4.2.1 <5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz" + }, + "cli": { + "version": "0.6.6", + "from": "cli@>=0.6.6 <0.7.0", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "dependencies": { + "glob": { + "version": "3.2.11", + "from": "glob@>=3.2.1 <3.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.3.0", + "from": "minimatch@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.7.3", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + }, + "exit": { + "version": "0.1.2", + "from": "exit@0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + } + } + }, + "express": { + "version": "3.3.5", + "from": "express@3.3.5", + "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", + "dependencies": { + "connect": { + "version": "2.8.5", + "from": "connect@2.8.5", + "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", + "dependencies": { + "qs": { + "version": "0.6.5", + "from": "qs@0.6.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" + }, + "formidable": { + "version": "1.0.14", + "from": "formidable@1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" + }, + "bytes": { + "version": "0.2.0", + "from": "bytes@0.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" + }, + "pause": { + "version": "0.0.1", + "from": "pause@0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + }, + "uid2": { + "version": "0.0.2", + "from": "uid2@0.0.2", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" + } + } + }, + "commander": { + "version": "1.2.0", + "from": "commander@1.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", + "dependencies": { + "keypress": { + "version": "0.1.0", + "from": "keypress@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" + } + } + }, + "range-parser": { + "version": "0.0.4", + "from": "range-parser@0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + }, + "cookie": { + "version": "0.1.0", + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + }, + "buffer-crc32": { + "version": "0.2.1", + "from": "buffer-crc32@0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" + }, + "fresh": { + "version": "0.2.0", + "from": "fresh@0.2.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" + }, + "methods": { + "version": "0.0.1", + "from": "methods@0.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" + }, + "send": { + "version": "0.1.4", + "from": "send@0.1.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", + "dependencies": { + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.9 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + } + } + }, + "cookie-signature": { + "version": "1.0.1", + "from": "cookie-signature@1.0.1", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" + }, + "debug": { + "version": "2.2.0", + "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + } + } + }, + "heap": { + "version": "0.2.6", + "from": "heap@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" + }, + "line-reader": { + "version": "0.2.4", + "from": "line-reader@>=0.2.4 <0.3.0", + "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" + }, + "logger-sharelatex": { + "version": "1.3.0", + "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", + "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#fd5e037e64178fb3ef5138d39a048cadbacd2ade", + "dependencies": { + "bunyan": { + "version": "1.5.1", + "from": "bunyan@1.5.1", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", + "dependencies": { + "dtrace-provider": { + "version": "0.6.0", + "from": "dtrace-provider@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "dependencies": { + "nan": { + "version": "2.2.0", + "from": "nan@>=2.0.8 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.2.0.tgz" + } + } + }, + "mv": { + "version": "2.1.1", + "from": "mv@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "ncp": { + "version": "2.0.0", + "from": "ncp@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" + }, + "rimraf": { + "version": "2.4.5", + "from": "rimraf@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "dependencies": { + "glob": { + "version": "6.0.4", + "from": "glob@>=6.0.1 <7.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "3.0.0", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.3", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "dependencies": { + "balanced-match": { + "version": "0.3.0", + "from": "balanced-match@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + } + } + }, + "safe-json-stringify": { + "version": "1.0.3", + "from": "safe-json-stringify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz" + } + } + }, + "coffee-script": { + "version": "1.4.0", + "from": "coffee-script@1.4.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz" + }, + "raven": { + "version": "0.8.1", + "from": "raven@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", + "dependencies": { + "cookie": { + "version": "0.1.0", + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + }, + "lsmod": { + "version": "0.0.3", + "from": "lsmod@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz" + }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@>=1.4.1 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, + "stack-trace": { + "version": "0.0.7", + "from": "stack-trace@0.0.7", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz" + } + } + } + } + }, + "metrics-sharelatex": { + "version": "1.3.0", + "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", + "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#a2d156e97b7ab51fca5f36c0838a9a808416dfe8", + "dependencies": { + "lynx": { + "version": "0.1.1", + "from": "lynx@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "dependencies": { + "mersenne": { + "version": "0.0.3", + "from": "mersenne@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" + }, + "statsd-parser": { + "version": "0.0.4", + "from": "statsd-parser@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + } + } + }, + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + }, + "underscore": { + "version": "1.6.0", + "from": "underscore@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + } + } + }, + "mongo-uri": { + "version": "0.1.2", + "from": "mongo-uri@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" + }, + "mongojs": { + "version": "1.4.1", + "from": "mongojs@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", + "dependencies": { + "each-series": { + "version": "1.0.0", + "from": "each-series@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + }, + "mongodb-core": { + "version": "1.2.32", + "from": "mongodb-core@1.2.32", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.2.32.tgz", + "dependencies": { + "bson": { + "version": "0.4.22", + "from": "bson@>=0.4.21 <0.5.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" + } + } + }, + "once": { + "version": "1.3.3", + "from": "once@>=1.3.2 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "parse-mongo-url": { + "version": "1.1.1", + "from": "parse-mongo-url@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + }, + "pump": { + "version": "1.0.1", + "from": "pump@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.5", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.6", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "thunky": { + "version": "0.1.0", + "from": "thunky@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + }, + "to-mongodb-core": { + "version": "2.0.0", + "from": "to-mongodb-core@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + }, + "kerberos": { + "version": "0.0.19", + "from": "kerberos@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.19.tgz", + "dependencies": { + "nan": { + "version": "2.0.9", + "from": "nan@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.9.tgz" + } + } + } + } + }, + "redis": { + "version": "0.10.3", + "from": "redis@>=0.10.1 <0.11.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" + }, + "redis-sharelatex": { + "version": "0.0.9", + "from": "redis-sharelatex@>=0.0.9 <0.1.0", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", + "dependencies": { + "chai": { + "version": "1.9.1", + "from": "chai@1.9.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", + "dependencies": { + "assertion-error": { + "version": "1.0.0", + "from": "assertion-error@1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" + }, + "deep-eql": { + "version": "0.1.3", + "from": "deep-eql@0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "dependencies": { + "type-detect": { + "version": "0.1.1", + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + } + } + } + } + }, + "coffee-script": { + "version": "1.8.0", + "from": "coffee-script@1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + }, + "grunt-contrib-coffee": { + "version": "0.11.1", + "from": "grunt-contrib-coffee@0.11.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "dependencies": { + "coffee-script": { + "version": "1.7.1", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + }, + "chalk": { + "version": "0.5.1", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "dependencies": { + "ansi-styles": { + "version": "1.1.0", + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "has-ansi": { + "version": "0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "strip-ansi": { + "version": "0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "supports-color": { + "version": "0.2.0", + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + } + } + }, + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "grunt-mocha-test": { + "version": "0.12.0", + "from": "grunt-mocha-test@0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "from": "hooker@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "fs-extra": { + "version": "0.11.1", + "from": "fs-extra@>=0.11.1 <0.12.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "dependencies": { + "ncp": { + "version": "0.6.0", + "from": "ncp@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "jsonfile": { + "version": "2.2.3", + "from": "jsonfile@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" + }, + "rimraf": { + "version": "2.5.2", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "dependencies": { + "glob": { + "version": "7.0.3", + "from": "glob@>=7.0.0 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "3.0.0", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.3", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "dependencies": { + "balanced-match": { + "version": "0.3.0", + "from": "balanced-match@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + } + } + } + } + }, + "mocha": { + "version": "1.21.4", + "from": "mocha@1.21.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", + "dependencies": { + "commander": { + "version": "2.0.0", + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + }, + "growl": { + "version": "1.8.1", + "from": "growl@>=1.8.0 <1.9.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" + }, + "jade": { + "version": "0.26.3", + "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "dependencies": { + "commander": { + "version": "0.6.1", + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + }, + "mkdirp": { + "version": "0.3.0", + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + } + } + }, + "diff": { + "version": "1.0.7", + "from": "diff@1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" + }, + "debug": { + "version": "2.2.0", + "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + }, + "glob": { + "version": "3.2.3", + "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.7.3", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "graceful-fs": { + "version": "2.0.3", + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "redis": { + "version": "0.12.1", + "from": "redis@0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + }, + "redis-sentinel": { + "version": "0.1.1", + "from": "redis-sentinel@0.1.1", + "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", + "dependencies": { + "redis": { + "version": "0.11.0", + "from": "redis@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" + }, + "q": { + "version": "0.9.2", + "from": "q@0.9.2", + "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" + } + } + }, + "sandboxed-module": { + "version": "1.0.1", + "from": "sandboxed-module@1.0.1", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", + "dependencies": { + "require-like": { + "version": "0.1.2", + "from": "require-like@0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" + }, + "stack-trace": { + "version": "0.0.9", + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + } + } + }, + "sinon": { + "version": "1.10.3", + "from": "sinon@1.10.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", + "dependencies": { + "formatio": { + "version": "1.0.2", + "from": "formatio@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", + "dependencies": { + "samsam": { + "version": "1.1.3", + "from": "samsam@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" + } + } + }, + "util": { + "version": "0.10.3", + "from": "util@>=0.10.3 <1.0.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + } + } + }, + "request": { + "version": "2.33.0", + "from": "request@>=2.33.0 <2.34.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", + "dependencies": { + "qs": { + "version": "0.6.6", + "from": "qs@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.0 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.9 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "tough-cookie": { + "version": "2.2.2", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + }, + "async": { + "version": "0.9.2", + "from": "async@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.3.0", + "from": "tunnel-agent@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" + } + } + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" + }, + "hawk": { + "version": "1.0.0", + "from": "hawk@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + } + } + }, + "s3-streams": { + "version": "0.3.0", + "from": "s3-streams@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", + "dependencies": { + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.9.3 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "readable-stream": { + "version": "2.0.5", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.6", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "bluebird": { + "version": "2.10.2", + "from": "bluebird@>=2.9.27 <3.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" + } + } + }, + "settings-sharelatex": { + "version": "1.0.0", + "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + } + } + }, + "underscore": { + "version": "1.7.0", + "from": "underscore@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + } + } +} From 8d900013d9e9b326a8dc2928dc9aa2e32ca41e77 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Mar 2016 11:02:58 +0000 Subject: [PATCH 296/549] record whether a pack is temporary in the pack itself using the expiresAt field no longer determines if the pack is temporary because archived packs have an expiresAt field added when they are retrieved from s3 --- services/track-changes/app/coffee/PackManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 4d48018190..d16b6857ce 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -92,6 +92,7 @@ module.exports = PackManager = end_ts: last.meta.end_ts v: first.v v_end: last.v + temporary: temporary if temporary newPack.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" From 3f388fb0ac4404ebe640b32923fd4ee284dab6c0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Mar 2016 11:37:57 +0000 Subject: [PATCH 297/549] only change ttl on cached packs, not temporary ones temporary = without versioning feature enabled cached = permanent versioned retrieved from s3 --- services/track-changes/app/coffee/PackManager.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index d16b6857ce..1c54afdd3a 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -204,10 +204,12 @@ module.exports = PackManager = return callback(err) if err? if not pack? MongoAWS.unArchivePack project_id, doc_id, pack_id, callback - else if pack.expiresAt? + else if pack.expiresAt? and not pack.temporary # we only need to touch the TTL on the listing of changes in the project # because diffs on individual documents are always done after that PackManager.increaseTTL pack, callback + # only do this for cached packs, not temporary ones to avoid older packs + # being kept longer than newer ones (which messes up the last update version) else callback(null, pack) From 98683de3ae924874045173cf3df06b83359cc020 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Mar 2016 11:38:59 +0000 Subject: [PATCH 298/549] temporarily disable ttl behaviour allow existing packs without temporary flag to expire --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 1c54afdd3a..4867979d39 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -204,7 +204,7 @@ module.exports = PackManager = return callback(err) if err? if not pack? MongoAWS.unArchivePack project_id, doc_id, pack_id, callback - else if pack.expiresAt? and not pack.temporary + else if pack.expiresAt? and not pack.temporary and false # TODO: remove false, temporarily disabled # we only need to touch the TTL on the listing of changes in the project # because diffs on individual documents are always done after that PackManager.increaseTTL pack, callback From 181cebecef5d1eec2923062286d0027e419c566d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Mar 2016 11:52:31 +0000 Subject: [PATCH 299/549] avoid call to fetch packs unnecessarily --- services/track-changes/app/coffee/PackManager.coffee | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 4867979d39..9d17d8bd17 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -161,7 +161,10 @@ module.exports = PackManager = return false if toVersion? and pack.v > toVersion return true neededIds = (pack._id for pack in indexPacks when packInRange(pack, fromVersion, toVersion)) - PackManager.fetchPacksIfNeeded project_id, doc_id, neededIds, callback + if neededIds.length + PackManager.fetchPacksIfNeeded project_id, doc_id, neededIds, callback + else + callback() fetchPacksIfNeeded: (project_id, doc_id, pack_ids, callback) -> db.docHistory.find {_id: {$in: (ObjectId(id) for id in pack_ids)}}, {_id:1}, (err, loadedPacks) -> @@ -169,12 +172,13 @@ module.exports = PackManager = allPackIds = (id.toString() for id in pack_ids) loadedPackIds = (pack._id.toString() for pack in loadedPacks) packIdsToFetch = _.difference allPackIds, loadedPackIds - logger.log {loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs" + logger.log {project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs" + return callback() if packIdsToFetch.length is 0 async.eachLimit packIdsToFetch, 4, (pack_id, cb) -> MongoAWS.unArchivePack project_id, doc_id, pack_id, cb , (err) -> return callback(err) if err? - logger.log "done unarchiving" + logger.log {project_id, doc_id}, "done unarchiving" callback() # Retrieve all changes across a project From 31348141d8986e33f93b3f682423fae3c3ff70cd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Mar 2016 11:54:06 +0000 Subject: [PATCH 300/549] increase logging for discarded updates and version mismatch --- services/track-changes/app/coffee/UpdatesManager.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 6cc7db0d3a..8506f6cdaf 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -30,15 +30,18 @@ module.exports = UpdatesManager = # Ensure that raw updates start where lastVersion left off if lastVersion? + discardedUpdates = [] rawUpdates = rawUpdates.slice(0) while rawUpdates[0]? and rawUpdates[0].v <= lastVersion - rawUpdates.shift() + discardedUpdates.push rawUpdates.shift() + if discardedUpdates.length + logger.error project_id: project_id, doc_id: doc_id, discardedUpdates: discardedUpdates, temporary: temporary, lastVersion: lastVersion, "discarded updates already present" if rawUpdates[0]? and rawUpdates[0].v != lastVersion + 1 ts = lastCompressedUpdate?.meta?.end_ts last_timestamp = if ts? then new Date(ts) else 'unknown time' error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion} from #{last_timestamp}") - logger.error err: error, doc_id: doc_id, project_id: project_id, prev_end_ts: ts, "inconsistent doc versions" + logger.error err: error, doc_id: doc_id, project_id: project_id, prev_end_ts: ts, temporary: temporary, lastCompressedUpdate: lastCompressedUpdate, "inconsistent doc versions" if Settings.trackchanges?.continueOnError and rawUpdates[0].v > lastVersion + 1 # we have lost some ops - continue to write into the database, we can't recover at this point lastCompressedUpdate = null From 537a1189e768467e79d9328043fec6cddfcd9fad Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 24 Mar 2016 16:10:27 +0000 Subject: [PATCH 301/549] update mongodb-core version --- services/track-changes/npm-shrinkwrap.json | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index a21d1f18ac..36b6e13937 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -433,14 +433,26 @@ "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" }, "mongodb-core": { - "version": "1.2.32", - "from": "mongodb-core@1.2.32", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.2.32.tgz", + "version": "1.3.10", + "from": "mongodb-core@>=1.2.8 <2.0.0", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", "dependencies": { - "bson": { - "version": "0.4.22", - "from": "bson@>=0.4.21 <0.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" + "require_optional": { + "version": "1.0.0", + "from": "require_optional@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", + "dependencies": { + "semver": { + "version": "5.1.0", + "from": "semver@>=5.1.0 <6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" + }, + "resolve-from": { + "version": "2.0.0", + "from": "resolve-from@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + } + } } } }, @@ -474,9 +486,9 @@ } }, "readable-stream": { - "version": "2.0.5", + "version": "2.0.6", "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", @@ -489,9 +501,9 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.6", @@ -524,18 +536,6 @@ "version": "4.0.1", "from": "xtend@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - }, - "kerberos": { - "version": "0.0.19", - "from": "kerberos@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.19.tgz", - "dependencies": { - "nan": { - "version": "2.0.9", - "from": "nan@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.9.tgz" - } - } } } }, From 6a2ecbac06d12c3fa31f7c1c277291b49e7719da Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 4 Apr 2016 16:30:17 +0100 Subject: [PATCH 302/549] upgrade sentry logging --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index cc2a0bc105..ddbdf57609 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -15,7 +15,7 @@ "line-reader": "^0.2.4", "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.0", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", From 6e18d4973679c9c717ed934a3a76fb0b5010ad6e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 4 Apr 2016 17:00:10 +0100 Subject: [PATCH 303/549] support archiving from list of project_ids/doc_ids --- .../app/coffee/PackWorker.coffee | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index a6d31b8436..d1afd9f1fa 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -2,6 +2,7 @@ Settings = require "settings-sharelatex" async = require "async" _ = require "underscore" {db, ObjectId, BSON} = require "./mongojs" +fs = require "fs" logger = require "logger-sharelatex" logger.initialize("track-changes-packworker") if Settings.sentry?.dsn? @@ -15,10 +16,19 @@ PackManager = require "./PackManager" # this worker script is forked by the main process to look for # document histories which can be archived -LIMIT = Number(process.argv[2]) || 1000 +source = process.argv[2] DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 TIMEOUT = Number(process.argv[4]) || 30*60*1000 +if source.match(/[^0-9]/) + file = fs.readFileSync source + result = for line in file.toString().split('\n') + [project_id, doc_id] = line.split(' ') + {doc_id, project_id} + pending = _.filter result, (row) -> row?.doc_id?.match(/^[a-f0-9]{24}$/) +else + LIMIT = Number(process.argv[2]) || 1000 + shutDownRequested = false shutDownTimer = setTimeout () -> logger.log "pack timed out, requesting shutdown" @@ -62,10 +72,11 @@ process.on 'exit', (code) -> processUpdates = (pending) -> async.eachSeries pending, (result, callback) -> {_id, project_id, doc_id} = result + logger.log {project_id, doc_id}, "processing" if not project_id? or not doc_id? logger.log {project_id, doc_id}, "skipping pack, missing project/doc id" return callback() - PackManager.processOldPack project_id, doc_id, _id, (err, result) -> + handler = (err, result) -> if err? logger.error {err, result}, "error in pack archive worker" return callback(err) @@ -75,6 +86,10 @@ processUpdates = (pending) -> setTimeout () -> callback(err, result) , DOCUMENT_PACK_DELAY + if not _id? + PackManager.pushOldPacks project_id, doc_id, handler + else + PackManager.processOldPack project_id, doc_id, _id, handler , (err, results) -> if err? and err.message != "shutdown" logger.error {err}, 'error in pack archive worker processUpdates' @@ -90,18 +105,23 @@ ObjectIdFromDate = (date) -> # find packs to be marked as finalised:true, those which have a newer pack present # then only consider finalised:true packs for archiving -db.docHistory.find({ - expiresAt: {$exists: false} - project_id: {$exists: true} - v_end: {$exists: true} - _id: {$lt: ObjectIdFromDate(new Date(Date.now() - 7 * DAYS))} -}, {_id:1, doc_id:1, project_id:1}).sort({ - last_checked:1 -}).limit LIMIT, (err, results) -> - if err? - logger.log {err}, 'error checking for updates' - finish() - return - pending = _.uniq results, false, (result) -> result.doc_id.toString() - logger.log "found #{pending.length} documents to archive" +if pending? + logger.log "got #{pending.length} entries from #{source}" processUpdates pending +else + db.docHistory.find({ + expiresAt: {$exists: false} + project_id: {$exists: true} + v_end: {$exists: true} + $or: [ {n:512}, {n:1024} ] + _id: {$lt: ObjectIdFromDate(new Date(Date.now() - 7 * DAYS))} + }, {_id:1, doc_id:1, project_id:1}).sort({ + last_checked:1 + }).limit LIMIT, (err, results) -> + if err? + logger.log {err}, 'error checking for updates' + finish() + return + pending = _.uniq results, false, (result) -> result.doc_id.toString() + logger.log "found #{pending.length} documents to archive" + processUpdates pending From 7a0c2900abaa0fb05c2e152c3f526950de698e94 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 13:29:19 +0100 Subject: [PATCH 304/549] add error handler in tests --- .../test/acceptance/coffee/helpers/TrackChangesClient.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 6a193ddd9b..4e1d1d1eff 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -106,6 +106,7 @@ module.exports = TrackChangesClient = options.encoding = null request.get options, (err, res, body) -> return callback(error) if error? + return callback(new Error("empty response from s3")) if not body? zlib.gunzip body, (err, result) -> return callback(err) if err? callback(null, JSON.parse(result.toString())) From 6ab75795a2272cef0f311133227423836687a7dc Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 13:30:09 +0100 Subject: [PATCH 305/549] archive head packs after sufficient time --- .../app/coffee/MongoManager.coffee | 8 ++- .../app/coffee/PackManager.coffee | 57 +++++++++++++++---- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index e906257add..383facbf2e 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -23,11 +23,13 @@ module.exports = MongoManager = MongoManager.getLastCompressedUpdate doc_id, (error, update) -> return callback(error) if error? if update? - if update.broken - # the update is marked as broken so we will force a new op + if update.broken # marked as broken so we will force a new op return callback null, null else if update.pack? - return callback null, update, update.pack[0]?.v + if update.finalised # no more ops can be appended + return callback null, null, update.pack[0]?.v + else + return callback null, update, update.pack[0]?.v else return callback null, update, update.v else diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 9d17d8bd17..27a825ff05 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -272,8 +272,9 @@ module.exports = PackManager = db.docHistory.find(query, {pack:false}).sort {v:1}, (err, packs) -> return callback(err) if err? return callback() if not packs? - return callback() if packs?.length <= 1 - packs.pop() # discard the last pack, it's still in progress + return callback() if not packs?.length + last = packs.pop() # discard the last pack, if it's still in progress + packs.push(last) if last.finalised # it's finalised so we push it back to archive it callback(null, packs) # findPacks: (project_id, doc_id, queryFilter, callback) -> @@ -380,19 +381,51 @@ module.exports = PackManager = return markAsChecked(err) if err? return markAsChecked() if not pack? return callback() if pack.expiresAt? # return directly - PackManager.updateIndexIfNeeded project_id, doc_id, (err) -> + PackManager.finaliseIfNeeded project_id, doc_id, pack._id, pack, (err) -> return markAsChecked(err) if err? - PackManager.findUnarchivedPacks project_id, doc_id, (err, unarchivedPacks) -> + PackManager.updateIndexIfNeeded project_id, doc_id, (err) -> return markAsChecked(err) if err? - if not unarchivedPacks?.length - logger.log "no packs need archiving" - return markAsChecked() - async.eachSeries unarchivedPacks, (pack, cb) -> - PackManager.archivePack project_id, doc_id, pack._id, cb - , (err) -> + PackManager.findUnarchivedPacks project_id, doc_id, (err, unarchivedPacks) -> return markAsChecked(err) if err? - logger.log "done processing" - markAsChecked() + if not unarchivedPacks?.length + logger.log "no packs need archiving" + return markAsChecked() + async.eachSeries unarchivedPacks, (pack, cb) -> + PackManager.archivePack project_id, doc_id, pack._id, cb + , (err) -> + return markAsChecked(err) if err? + logger.log "done processing" + markAsChecked() + + finaliseIfNeeded: (project_id, doc_id, pack_id, pack, callback) -> + logger.log {project_id, doc_id}, "archiving old packs" + sz = pack.sz / (1024 * 1024) # in fractions of a megabyte + n = pack.n / 1024 # in fraction of 1024 ops + age = (Date.now() - pack.meta.end_ts) / DAYS + if age < 30 # always keep if less than 1 month old + return callback() + # compute an archiving threshold which decreases for each month of age + archive_threshold = 30 / age + if sz > archive_threshold or n > archive_threshold or age > 90 + PackManager.markPackAsFinalisedWithLock project_id, doc_id, pack_id, callback + else + callback() + + markPackAsFinalisedWithLock: (project_id, doc_id, pack_id, callback) -> + LockManager.runWithLock( + "HistoryLock:#{doc_id}", + (releaseLock) -> + PackManager._markPackAsFinalised project_id, doc_id, pack_id, releaseLock + callback + ) + + _markPackAsFinalised: (project_id, doc_id, pack_id, callback) -> + logger.log {project_id, doc_id, pack_id}, "marking pack as finalised" + db.docHistory.findAndModify { + query: {_id: pack_id} + update: {$set: {finalised: true}} + }, callback + updateIndexIfNeeded: (project_id, doc_id, callback) -> logger.log {project_id, doc_id}, "archiving old packs" From 79baa996346c9f11914bf69f43fe487eea12b184 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 14:26:54 +0100 Subject: [PATCH 306/549] clean up logging --- .../track-changes/app/coffee/PackManager.coffee | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 27a825ff05..596df06b70 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -291,7 +291,8 @@ module.exports = PackManager = # select only the new packs not already in the index newPacks = (pack for pack in historyPacks when not indexResult?[pack._id]?) newPacks = (_.omit(pack, 'doc_id', 'project_id', 'n', 'sz') for pack in newPacks) - logger.log {project_id, doc_id, n: newPacks.length}, "found new packs" + if newPacks.length + logger.log {project_id, doc_id, n: newPacks.length}, "found new packs" callback(null, newPacks) insertPacksIntoIndexWithLock: (project_id, doc_id, newPacks, callback) -> @@ -388,27 +389,29 @@ module.exports = PackManager = PackManager.findUnarchivedPacks project_id, doc_id, (err, unarchivedPacks) -> return markAsChecked(err) if err? if not unarchivedPacks?.length - logger.log "no packs need archiving" + logger.log {project_id, doc_id}, "no packs need archiving" return markAsChecked() async.eachSeries unarchivedPacks, (pack, cb) -> PackManager.archivePack project_id, doc_id, pack._id, cb , (err) -> return markAsChecked(err) if err? - logger.log "done processing" + logger.log {project_id, doc_id}, "done processing" markAsChecked() finaliseIfNeeded: (project_id, doc_id, pack_id, pack, callback) -> - logger.log {project_id, doc_id}, "archiving old packs" sz = pack.sz / (1024 * 1024) # in fractions of a megabyte n = pack.n / 1024 # in fraction of 1024 ops age = (Date.now() - pack.meta.end_ts) / DAYS if age < 30 # always keep if less than 1 month old + logger.log {project_id, doc_id, pack_id, age}, "less than 30 days old" return callback() # compute an archiving threshold which decreases for each month of age archive_threshold = 30 / age if sz > archive_threshold or n > archive_threshold or age > 90 + logger.log {project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "meets archive threshold" PackManager.markPackAsFinalisedWithLock project_id, doc_id, pack_id, callback else + logger.log {project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "does not meet archive threshold" callback() markPackAsFinalisedWithLock: (project_id, doc_id, pack_id, callback) -> @@ -448,7 +451,8 @@ module.exports = PackManager = return callback(err) if err? indexPacks = indexResult?.packs or [] unArchivedPacks = (pack for pack in indexPacks when not pack.inS3?) - logger.log {project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs" + if unArchivedPacks.length + logger.log {project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs" callback(null, unArchivedPacks) # Archive locking flags From 719e0291aa77a6b6e58fc6762782eace4e9a3c63 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 14:27:44 +0100 Subject: [PATCH 307/549] consider all packs for processing to allow finalisation of old head packs --- .../track-changes/app/coffee/PackManager.coffee | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 596df06b70..70b094d654 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -277,10 +277,13 @@ module.exports = PackManager = packs.push(last) if last.finalised # it's finalised so we push it back to archive it callback(null, packs) - # findPacks: (project_id, doc_id, queryFilter, callback) -> - # query = { doc_id: ObjectId(doc_id.toString()) } - # query = _.defaults query, queryFilter if queryFilter? - # db.docHistory.find(query, {pack:false}).sort {v:1}, callback + findPacks: (project_id, doc_id, callback) -> + query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} } + db.docHistory.find(query, {pack:false}).sort {v:1}, (err, packs) -> + return callback(err) if err? + return callback() if not packs? + return callback() if not packs?.length + callback(null, packs) findUnindexedPacks: (project_id, doc_id, callback) -> PackManager.getIndexWithKeys doc_id, (err, indexResult) -> @@ -361,7 +364,7 @@ module.exports = PackManager = # Extra methods to test archive/unarchive for a doc_id pushOldPacks: (project_id, doc_id, callback) -> - PackManager.findCompletedPacks project_id, doc_id, (err, packs) -> + PackManager.findPacks project_id, doc_id, (err, packs) -> return callback(err) if err? return callback() if not packs?.length PackManager.processOldPack project_id, doc_id, packs[0]._id, callback From 08fc151eee4145da2837ff6a87e1caa333a5039c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 14:29:21 +0100 Subject: [PATCH 308/549] avoid unnecessary call to insert packs into index --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 70b094d654..55bc6f7304 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -261,7 +261,7 @@ module.exports = PackManager = # find all packs prior to current pack PackManager.findUnindexedPacks project_id, doc_id, (err, newPacks) -> return callback(err) if err? - return callback() if not newPacks? + return callback() if not newPacks? or newPacks.length is 0 PackManager.insertPacksIntoIndexWithLock project_id, doc_id, newPacks, (err) -> return callback(err) if err? logger.log {project_id, doc_id, newPacks}, "added new packs to index" From 0b9a0730c0a292a3a72a48132cc9c52b47de04ba Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 14:29:49 +0100 Subject: [PATCH 309/549] mark temporary packs with a last_checked date in the far future they do not need to be checked for archiving --- services/track-changes/app/coffee/PackManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 55bc6f7304..39644d514e 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -95,6 +95,7 @@ module.exports = PackManager = temporary: temporary if temporary newPack.expiresAt = new Date(Date.now() + 7 * DAYS) + newPack.last_checked = new Date(Date.now() + 30 * DAYS) # never check temporary packs logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" db.docHistory.save newPack, (err, result) -> return callback(err) if err? From ef47337c78f1a2d89643b644dc13b1e5255aec81 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 15:17:20 +0100 Subject: [PATCH 310/549] remove additional fields --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 39644d514e..0b04588804 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -294,7 +294,7 @@ module.exports = PackManager = return callback() if not historyPacks? # select only the new packs not already in the index newPacks = (pack for pack in historyPacks when not indexResult?[pack._id]?) - newPacks = (_.omit(pack, 'doc_id', 'project_id', 'n', 'sz') for pack in newPacks) + newPacks = (_.omit(pack, 'doc_id', 'project_id', 'n', 'sz', 'last_checked', 'finalised') for pack in newPacks) if newPacks.length logger.log {project_id, doc_id, n: newPacks.length}, "found new packs" callback(null, newPacks) From 8b7bdd345b11bf61e4575c679d84b36878c1f8d0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 15:17:31 +0100 Subject: [PATCH 311/549] consider all packs for archiving --- services/track-changes/app/coffee/PackWorker.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index d1afd9f1fa..47aee2a3cf 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -113,7 +113,6 @@ else expiresAt: {$exists: false} project_id: {$exists: true} v_end: {$exists: true} - $or: [ {n:512}, {n:1024} ] _id: {$lt: ObjectIdFromDate(new Date(Date.now() - 7 * DAYS))} }, {_id:1, doc_id:1, project_id:1}).sort({ last_checked:1 From e292de5eb00d490da451de89ccaa730e3bebcca1 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 6 Apr 2016 17:00:16 +0100 Subject: [PATCH 312/549] fix to avoid ever appending permanent changes to expiring packs --- .../app/coffee/PackManager.coffee | 17 ++++++++- .../PackManager/PackManagerTests.coffee | 38 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 0b04588804..b474d90605 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -50,6 +50,9 @@ module.exports = PackManager = insertCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> return callback() if newUpdates.length == 0 + # never append permanent ops to a pack that will expire + lastUpdate = null if lastUpdate?.expiresAt? and not temporary + updatesToFlush = [] updatesRemaining = newUpdates.slice() @@ -71,7 +74,19 @@ module.exports = PackManager = flushCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> return callback() if newUpdates.length == 0 - if lastUpdate? and not (temporary and ((Date.now() - lastUpdate.meta?.start_ts) > 1 * DAYS)) + + canAppend = false + # check if it is safe to append to an existing pack + if lastUpdate? + if not temporary and not lastUpdate.expiresAt? + # permanent pack appends to permanent pack + canAppend = true + age = Date.now() - lastUpdate.meta?.start_ts + if temporary and lastUpdate.expiresAt? and age < 1 * DAYS + # temporary pack appends to temporary pack if same day + canAppend = true + + if canAppend PackManager.appendUpdatesToExistingPack project_id, doc_id, lastUpdate, newUpdates, temporary, callback else PackManager.insertUpdatesIntoNewPack project_id, doc_id, newUpdates, temporary, callback diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index 1d929722bd..bd2244517f 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -171,7 +171,7 @@ describe "PackManager", -> it "should call the callback", -> @callback.called.should.equal true - describe "when there is a recent previous update in mongo", -> + describe "when there is a recent previous update in mongo that expires", -> beforeEach -> @lastUpdate = { _id: "12345" @@ -181,6 +181,7 @@ describe "PackManager", -> ] n : 2 sz : 100 + meta: {start_ts: Date.now() - 6 * 3600 * 1000} expiresAt: new Date(Date.now()) } @@ -202,6 +203,41 @@ describe "PackManager", -> @callback.called.should.equal true + describe "when there is a recent previous update in mongo that expires", -> + beforeEach -> + @PackManager.updateIndex = sinon.stub().callsArg(2) + + @lastUpdate = { + _id: "12345" + pack: [ + { op: "op-1", meta: "meta-1", v: 1}, + { op: "op-2", meta: "meta-2", v: 2} + ] + n : 2 + sz : 100 + meta: {start_ts: Date.now() - 6 * 3600 * 1000} + expiresAt: new Date(Date.now()) + } + + @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + + describe "for a small update that will not expire", -> + it "should insert the update into mongo", -> + @db.docHistory.save.calledWithMatch({ + pack: @newUpdates, + project_id: ObjectId(@project_id), + doc_id: ObjectId(@doc_id) + n: @newUpdates.length + v: @newUpdates[0].v + v_end: @newUpdates[@newUpdates.length-1].v + }).should.equal true + + it "should not set any expiry time", -> + @db.docHistory.save.neverCalledWithMatch(sinon.match.has("expiresAt")).should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + describe "when there is an old previous update in mongo", -> beforeEach -> @lastUpdate = { From fd49601716f179b4340764e215eb0a361a377228 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 14:37:53 +0100 Subject: [PATCH 313/549] preserve existing history when user upgrades --- .../track-changes/app/coffee/MongoManager.coffee | 13 +++++++++++++ .../track-changes/app/coffee/UpdateTrimmer.coffee | 4 +++- .../coffee/UpdateTrimmer/UpdateTrimmerTests.coffee | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 383facbf2e..4376e3adb1 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -64,6 +64,19 @@ module.exports = MongoManager = upsert: true }, callback + upgradeHistory: (project_id, callback = (error) ->) -> + # preserve the project's existing history + db.docHistory.update { + project_id: ObjectId(project_id) + temporary: true + expiresAt: {$exists: true} + }, { + $set: {temporary: false} + $unset: {expiresAt: ""} + }, { + multi: true + }, callback + ensureIndices: () -> # For finding all updates that go into a diff for a doc db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } diff --git a/services/track-changes/app/coffee/UpdateTrimmer.coffee b/services/track-changes/app/coffee/UpdateTrimmer.coffee index 83a08e36fa..c7464eb6c3 100644 --- a/services/track-changes/app/coffee/UpdateTrimmer.coffee +++ b/services/track-changes/app/coffee/UpdateTrimmer.coffee @@ -15,7 +15,9 @@ module.exports = UpdateTrimmer = if details?.features?.versioning MongoManager.setProjectMetaData project_id, preserveHistory: true, (error) -> return callback(error) if error? - callback null, false + MongoManager.upgradeHistory project_id, (error) -> + return callback(error) if error? + callback null, false else callback null, true diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee index afcfe53513..a25c3f4ce2 100644 --- a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee @@ -29,6 +29,7 @@ describe "UpdateTrimmer", -> features: {} @MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, @metadata) @MongoManager.setProjectMetaData = sinon.stub().callsArgWith(2) + @MongoManager.upgradeHistory = sinon.stub().callsArgWith(1) @WebApiManager.getProjectDetails = sinon.stub().callsArgWith(1, null, @details) describe "with preserveHistory set in the project meta data", -> @@ -73,6 +74,11 @@ describe "UpdateTrimmer", -> .calledWith(@project_id, {preserveHistory: true}) .should.equal true + it "should upgrade any existing history", -> + @MongoManager.upgradeHistory + .calledWith(@project_id) + .should.equal true + it "should return false", -> @callback.calledWith(null, false).should.equal true @@ -98,6 +104,11 @@ describe "UpdateTrimmer", -> .calledWith(@project_id, {preserveHistory: true}) .should.equal true + it "should upgrade any existing history", -> + @MongoManager.upgradeHistory + .calledWith(@project_id) + .should.equal true + it "should return false", -> @callback.calledWith(null, false).should.equal true From d0e08039dac74ce891f8db326983a692b6e81bd1 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 14:46:31 +0100 Subject: [PATCH 314/549] don't modify expiry for temporary packs --- services/track-changes/app/coffee/PackManager.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index b474d90605..b40e8ef46c 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -224,8 +224,8 @@ module.exports = PackManager = return callback(err) if err? if not pack? MongoAWS.unArchivePack project_id, doc_id, pack_id, callback - else if pack.expiresAt? and not pack.temporary and false # TODO: remove false, temporarily disabled - # we only need to touch the TTL on the listing of changes in the project + else if pack.expiresAt? and pack.temporary is false + # we only need to touch the TTL when listing the changes in the project # because diffs on individual documents are always done after that PackManager.increaseTTL pack, callback # only do this for cached packs, not temporary ones to avoid older packs From 6db310bf6b6478ecfd1944107f44fa4bf3a7d3cd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 15:15:47 +0100 Subject: [PATCH 315/549] add insert/archive/unarchive metrics --- services/track-changes/app/coffee/MongoAWS.coffee | 3 +++ services/track-changes/app/coffee/PackManager.coffee | 2 ++ 2 files changed, 5 insertions(+) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index c6712e29f1..42f41863a6 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -6,6 +6,7 @@ S3S = require 's3-streams' JSONStream = require "JSONStream" ReadlineStream = require "byline" zlib = require "zlib" +Metrics = require "./Metrics" DAYS = 24 * 3600 * 1000 # one day in milliseconds @@ -51,6 +52,7 @@ module.exports = MongoAWS = upload.on 'error', (err) -> callback(err) upload.on 'finish', () -> + Metrics.inc("archive-pack") logger.log {project_id, doc_id, pack_id}, "upload to s3 completed" callback(null) upload.write buf @@ -103,6 +105,7 @@ module.exports = MongoAWS = unArchivePack: (project_id, doc_id, pack_id, callback = (error) ->) -> MongoAWS.readArchivedPack project_id, doc_id, pack_id, (err, object) -> return callback(err) if err? + Metrics.inc("unarchive-pack") # allow the object to expire, we can always retrieve it again object.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, pack_id}, "inserting object from s3" diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index b40e8ef46c..dd0a9c379c 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -4,6 +4,7 @@ _ = require "underscore" logger = require "logger-sharelatex" LockManager = require "./LockManager" MongoAWS = require "./MongoAWS" +Metrics = require "./Metrics" ProjectIterator = require "./ProjectIterator" # Sharejs operations are stored in a 'pack' object @@ -114,6 +115,7 @@ module.exports = PackManager = logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" db.docHistory.save newPack, (err, result) -> return callback(err) if err? + Metrics.inc("insert-pack-" + if temporary then "temporary" else "permanent") if temporary return callback() else From 1a1fa8798d9e1714c602e744a5908fdc03a970c5 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 14:45:51 +0100 Subject: [PATCH 316/549] log attempted update when throwing error in DiffGenerator --- services/track-changes/app/coffee/DiffGenerator.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index 53f7c2d343..b4e0156f02 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -50,7 +50,11 @@ module.exports = DiffGenerator = rewindUpdates: (content, updates) -> for update in updates.reverse() - content = DiffGenerator.rewindUpdate(content, update) + try + content = DiffGenerator.rewindUpdate(content, update) + catch e + e.attempted_update = update # keep a record of the attempted update + throw e # rethrow the exception return content buildDiff: (initialContent, updates) -> From 78100e40c8d5c8df6f0042553af0ba28283dae65 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 15:33:20 +0100 Subject: [PATCH 317/549] add missing metrics file --- services/track-changes/app/coffee/Metrics.coffee | 1 + 1 file changed, 1 insertion(+) create mode 100644 services/track-changes/app/coffee/Metrics.coffee diff --git a/services/track-changes/app/coffee/Metrics.coffee b/services/track-changes/app/coffee/Metrics.coffee new file mode 100644 index 0000000000..c0e7d12731 --- /dev/null +++ b/services/track-changes/app/coffee/Metrics.coffee @@ -0,0 +1 @@ +module.exports = require "metrics-sharelatex" From 25365789b49a9eab4ced03cbb08d9fb12fcbf432 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 15:53:10 +0100 Subject: [PATCH 318/549] use latest sharelatex-metrics package --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index ddbdf57609..a376684d45 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -16,7 +16,7 @@ "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.0", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", "redis": "~0.10.1", From 76fe194815c592eef9e1c395343a38fc4f09c6ab Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Apr 2016 15:56:11 +0100 Subject: [PATCH 319/549] add a metric for append-pack --- services/track-changes/app/coffee/PackManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index dd0a9c379c..f9707931cf 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -143,6 +143,7 @@ module.exports = PackManager = if lastUpdate.expiresAt and temporary update.$set.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log {project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack" + Metrics.inc("append-pack-" + if temporary then "temporary" else "permanent") db.docHistory.findAndModify {query, update, new:true, fields:{meta:1,v_end:1}}, callback # Retrieve all changes for a document From b343be844eab0eebc66eebdb0629e547e2a9c616 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 8 Apr 2016 10:29:04 +0100 Subject: [PATCH 320/549] added metrics to pack worker for archiving --- services/track-changes/app/coffee/PackWorker.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 47aee2a3cf..6d19163811 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -3,6 +3,8 @@ async = require "async" _ = require "underscore" {db, ObjectId, BSON} = require "./mongojs" fs = require "fs" +Metrics = require "metrics-sharelatex" +Metrics.initialize("track-changes") logger = require "logger-sharelatex" logger.initialize("track-changes-packworker") if Settings.sentry?.dsn? From a55b72871fc357662e6ef7d43334c751737557ae Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 13 Apr 2016 14:39:11 +0100 Subject: [PATCH 321/549] don't let s3 errors stop archive worker --- services/track-changes/app/coffee/PackWorker.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 6d19163811..be5c511d1f 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -79,6 +79,10 @@ processUpdates = (pending) -> logger.log {project_id, doc_id}, "skipping pack, missing project/doc id" return callback() handler = (err, result) -> + if err? and err.code is "InternalError" and err.retryable + logger.warn {err, result}, "ignoring S3 error in pack archive worker" + # Ignore any s3 errors due to random problems + err = null if err? logger.error {err, result}, "error in pack archive worker" return callback(err) From 6e5eadfa86a1bb2fc2918696722945a801c01b5a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 13 Apr 2016 16:30:20 +0100 Subject: [PATCH 322/549] include a timeout on WebApiManager requests --- services/track-changes/app/coffee/WebApiManager.coffee | 4 ++++ .../unit/coffee/WebApiManager/WebApiManagerTests.coffee | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 4e8e9602d8..50409ca43c 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -2,6 +2,9 @@ request = require "request" logger = require "logger-sharelatex" Settings = require "settings-sharelatex" +# Don't let HTTP calls hang for a long time +MAX_HTTP_REQUEST_LENGTH = 15000 # 15 seconds + # DEPRECATED! This method of getting user details via track-changes is deprecated # in the way we lay out our services. # Instead, web should be responsible for collecting the raw data (user_ids) and @@ -11,6 +14,7 @@ module.exports = WebApiManager = sendRequest: (url, callback = (error, body) ->) -> request.get { url: "#{Settings.apis.web.url}#{url}" + timeout: MAX_HTTP_REQUEST_LENGTH auth: user: Settings.apis.web.user pass: Settings.apis.web.pass diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee index 177a7b26f0..6af9cf5914 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee @@ -37,7 +37,7 @@ describe "WebApiManager", -> it 'should get the user from the web api', -> @request.get - .calledWith({ + .calledWithMatch({ url: "#{@settings.apis.web.url}/user/#{@user_id}/personal_info" auth: user: @settings.apis.web.user @@ -92,7 +92,7 @@ describe "WebApiManager", -> it 'should get the project from the web api', -> @request.get - .calledWith({ + .calledWithMatch({ url: "#{@settings.apis.web.url}/project/#{@project_id}/details" auth: user: @settings.apis.web.user @@ -120,4 +120,4 @@ describe "WebApiManager", -> it "should return the callback with an error", -> @callback .calledWith(new Error("web returned failure status code: 500")) - .should.equal true \ No newline at end of file + .should.equal true From 80375ae2dddc470cc98c5d721c524b19b770083c Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 Sep 2016 11:33:36 +0100 Subject: [PATCH 323/549] Run a diff against big delete - insert changes which are likely copy-pastes --- .../app/coffee/UpdateCompressor.coffee | 44 + .../track-changes/app/lib/diff_match_patch.js | 2193 +++++++++++++++++ .../UpdateCompressorTests.coffee | 41 + 3 files changed, 2278 insertions(+) create mode 100644 services/track-changes/app/lib/diff_match_patch.js diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 98b2e048bf..f55d7462ea 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -1,6 +1,9 @@ strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] +diff_match_patch = require("../lib/diff_match_patch").diff_match_patch +dmp = new diff_match_patch() + module.exports = UpdateCompressor = NOOP: "noop" @@ -155,6 +158,47 @@ module.exports = UpdateCompressor = # This will only happen if the delete extends outside the insert return [firstUpdate, secondUpdate] + # A delete then an insert at the same place, likely a copy-paste of a chunk of content + else if firstOp.d? and secondOp.i? and firstOp.p == secondOp.p + offset = firstOp.p + diff_ops = @diffAsShareJsOps(firstOp.d, secondOp.i) + return diff_ops.map (op) -> + op.p += offset + return { + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: op + v: secondUpdate.v + } + else return [firstUpdate, secondUpdate] + ADDED: 1 + REMOVED: -1 + UNCHANGED: 0 + diffAsShareJsOps: (before, after, callback = (error, ops) ->) -> + diffs = dmp.diff_main(before, after) + dmp.diff_cleanupSemantic(diffs) + + ops = [] + position = 0 + for diff in diffs + type = diff[0] + content = diff[1] + if type == @ADDED + ops.push + i: content + p: position + position += content.length + else if type == @REMOVED + ops.push + d: content + p: position + else if type == @UNCHANGED + position += content.length + else + throw "Unknown type" + return ops diff --git a/services/track-changes/app/lib/diff_match_patch.js b/services/track-changes/app/lib/diff_match_patch.js new file mode 100644 index 0000000000..112130e097 --- /dev/null +++ b/services/track-changes/app/lib/diff_match_patch.js @@ -0,0 +1,2193 @@ +/** + * Diff Match and Patch + * + * Copyright 2006 Google Inc. + * http://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Computes the difference between two texts to create a patch. + * Applies the patch onto another text, allowing for errors. + * @author fraser@google.com (Neil Fraser) + */ + +/** + * Class containing the diff, match and patch methods. + * @constructor + */ +function diff_match_patch() { + + // Defaults. + // Redefine these in your program to override the defaults. + + // Number of seconds to map a diff before giving up (0 for infinity). + this.Diff_Timeout = 1.0; + // Cost of an empty edit operation in terms of edit characters. + this.Diff_EditCost = 4; + // At what point is no match declared (0.0 = perfection, 1.0 = very loose). + this.Match_Threshold = 0.5; + // How far to search for a match (0 = exact location, 1000+ = broad match). + // A match this many characters away from the expected location will add + // 1.0 to the score (0.0 is a perfect match). + this.Match_Distance = 1000; + // When deleting a large block of text (over ~64 characters), how close do + // the contents have to be to match the expected contents. (0.0 = perfection, + // 1.0 = very loose). Note that Match_Threshold controls how closely the + // end points of a delete need to match. + this.Patch_DeleteThreshold = 0.5; + // Chunk size for context length. + this.Patch_Margin = 4; + + // The number of bits in an int. + this.Match_MaxBits = 32; +} + + +// DIFF FUNCTIONS + + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +var DIFF_DELETE = -1; +var DIFF_INSERT = 1; +var DIFF_EQUAL = 0; + +/** @typedef {{0: number, 1: string}} */ +diff_match_patch.Diff; + + +/** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} opt_checklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @param {number} opt_deadline Optional time when the diff should be complete + * by. Used internally for recursive calls. Users should set DiffTimeout + * instead. + * @return {!Array.} Array of diff tuples. + */ +diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines, + opt_deadline) { + // Set a deadline by which time the diff must be complete. + if (typeof opt_deadline == 'undefined') { + if (this.Diff_Timeout <= 0) { + opt_deadline = Number.MAX_VALUE; + } else { + opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000; + } + } + var deadline = opt_deadline; + + // Check for null inputs. + if (text1 == null || text2 == null) { + throw new Error('Null input. (diff_main)'); + } + + // Check for equality (speedup). + if (text1 == text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + if (typeof opt_checklines == 'undefined') { + opt_checklines = true; + } + var checklines = opt_checklines; + + // Trim off common prefix (speedup). + var commonlength = this.diff_commonPrefix(text1, text2); + var commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = this.diff_commonSuffix(text1, text2); + var commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + var diffs = this.diff_compute_(text1, text2, checklines, deadline); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + this.diff_cleanupMerge(diffs); + return diffs; +}; + + +/** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines, + deadline) { + var diffs; + + if (!text1) { + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + var i = longtext.indexOf(shorttext); + if (i != -1) { + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length == 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + var hm = this.diff_halfMatch_(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + var text1_a = hm[0]; + var text1_b = hm[1]; + var text2_a = hm[2]; + var text2_b = hm[3]; + var mid_common = hm[4]; + // Send both pairs off for separate processing. + var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline); + var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline); + // Merge the results. + return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diff_lineMode_(text1, text2, deadline); + } + + return this.diff_bisect_(text1, text2, deadline); +}; + + +/** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) { + // Scan the text on a line-by-line basis first. + var a = this.diff_linesToChars_(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + var linearray = a.lineArray; + + var diffs = this.diff_main(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diff_charsToLines_(diffs, linearray); + // Eliminate freak matches (e.g. blank lines) + this.diff_cleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push([DIFF_EQUAL, '']); + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete >= 1 && count_insert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert); + pointer = pointer - count_delete - count_insert; + var a = this.diff_main(text_delete, text_insert, false, deadline); + for (var j = a.length - 1; j >= 0; j--) { + diffs.splice(pointer, 0, a[j]); + } + pointer = pointer + a.length; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; +}; + + +/** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + var max_d = Math.ceil((text1_length + text2_length) / 2); + var v_offset = max_d; + var v_length = 2 * max_d; + var v1 = new Array(v_length); + var v2 = new Array(v_length); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (var x = 0; x < v_length; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[v_offset + 1] = 0; + v2[v_offset + 1] = 0; + var delta = text1_length - text2_length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + var front = (delta % 2 != 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + var k1start = 0; + var k1end = 0; + var k2start = 0; + var k2end = 0; + for (var d = 0; d < max_d; d++) { + // Bail out if deadline is reached. + if ((new Date()).getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + var k1_offset = v_offset + k1; + var x1; + if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + x1 = v1[k1_offset + 1]; + } else { + x1 = v1[k1_offset - 1] + 1; + } + var y1 = x1 - k1; + while (x1 < text1_length && y1 < text2_length && + text1.charAt(x1) == text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1_offset] = x1; + if (x1 > text1_length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2_length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + var k2_offset = v_offset + delta - k1; + if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { + // Mirror x2 onto top-left coordinate system. + var x2 = text1_length - v2[k2_offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + var k2_offset = v_offset + k2; + var x2; + if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + x2 = v2[k2_offset + 1]; + } else { + x2 = v2[k2_offset - 1] + 1; + } + var y2 = x2 - k2; + while (x2 < text1_length && y2 < text2_length && + text1.charAt(text1_length - x2 - 1) == + text2.charAt(text2_length - y2 - 1)) { + x2++; + y2++; + } + v2[k2_offset] = x2; + if (x2 > text1_length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2_length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + var k1_offset = v_offset + delta - k2; + if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { + var x1 = v1[k1_offset]; + var y1 = v_offset + x1 - k1_offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1_length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; +}; + + +/** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_bisectSplit_ = function(text1, text2, x, y, + deadline) { + var text1a = text1.substring(0, x); + var text2a = text2.substring(0, y); + var text1b = text1.substring(x); + var text2b = text2.substring(y); + + // Compute both diffs serially. + var diffs = this.diff_main(text1a, text2a, false, deadline); + var diffsb = this.diff_main(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); +}; + + +/** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ +diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) { + var lineArray = []; // e.g. lineArray[4] == 'Hello\n' + var lineHash = {}; // e.g. lineHash['Hello\n'] == 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ''; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diff_linesToCharsMunge_(text) { + var chars = ''; + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + var lineStart = 0; + var lineEnd = -1; + // Keeping our own length variable is faster than looking it up. + var lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf('\n', lineStart); + if (lineEnd == -1) { + lineEnd = text.length - 1; + } + var line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : + (lineHash[line] !== undefined)) { + chars += String.fromCharCode(lineHash[line]); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + var chars1 = diff_linesToCharsMunge_(text1); + var chars2 = diff_linesToCharsMunge_(text2); + return {chars1: chars1, chars2: chars2, lineArray: lineArray}; +}; + + +/** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ +diff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) { + for (var x = 0; x < diffs.length; x++) { + var chars = diffs[x][1]; + var text = []; + for (var y = 0; y < chars.length; y++) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(''); + } +}; + + +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ +diff_match_patch.prototype.diff_commonPrefix = function(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ +diff_match_patch.prototype.diff_commonSuffix = function(text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + var pointermin = 0; + var pointermax = Math.min(text1.length, text2.length); + var pointermid = pointermax; + var pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ +diff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) { + // Cache the text lengths to prevent multiple calls. + var text1_length = text1.length; + var text2_length = text2.length; + // Eliminate the null case. + if (text1_length == 0 || text2_length == 0) { + return 0; + } + // Truncate the longer string. + if (text1_length > text2_length) { + text1 = text1.substring(text1_length - text2_length); + } else if (text1_length < text2_length) { + text2 = text2.substring(0, text1_length); + } + var text_length = Math.min(text1_length, text2_length); + // Quick check for the worst case. + if (text1 == text2) { + return text_length; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: http://neil.fraser.name/news/2010/11/04/ + var best = 0; + var length = 1; + while (true) { + var pattern = text1.substring(text_length - length); + var found = text2.indexOf(pattern); + if (found == -1) { + return best; + } + length += found; + if (found == 0 || text1.substring(text_length - length) == + text2.substring(0, length)) { + best = length; + length++; + } + } +}; + + +/** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ +diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) { + if (this.Diff_Timeout <= 0) { + // Don't risk returning a non-optimal diff if we have unlimited time. + return null; + } + var longtext = text1.length > text2.length ? text1 : text2; + var shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + var dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diff_halfMatchI_(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + var j = -1; + var best_common = ''; + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j + 1)) != -1) { + var prefixLength = dmp.diff_commonPrefix(longtext.substring(i), + shorttext.substring(j)); + var suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (best_common.length < suffixLength + prefixLength) { + best_common = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + best_longtext_a = longtext.substring(0, i - suffixLength); + best_longtext_b = longtext.substring(i + prefixLength); + best_shorttext_a = shorttext.substring(0, j - suffixLength); + best_shorttext_b = shorttext.substring(j + prefixLength); + } + } + if (best_common.length * 2 >= longtext.length) { + return [best_longtext_a, best_longtext_b, + best_shorttext_a, best_shorttext_b, best_common]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + var hm1 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + var hm2 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 2)); + var hm; + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + var text1_a, text1_b, text2_a, text2_b; + if (text1.length > text2.length) { + text1_a = hm[0]; + text1_b = hm[1]; + text2_a = hm[2]; + text2_b = hm[3]; + } else { + text2_a = hm[0]; + text2_b = hm[1]; + text1_a = hm[2]; + text1_b = hm[3]; + } + var mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +}; + + +/** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) { + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + var equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + var lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + var pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + var length_insertions1 = 0; + var length_deletions1 = 0; + // Number of characters that changed after the equality. + var length_insertions2 = 0; + var length_deletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found. + equalities[equalitiesLength++] = pointer; + length_insertions1 = length_insertions2; + length_deletions1 = length_deletions2; + length_insertions2 = 0; + length_deletions2 = 0; + lastequality = diffs[pointer][1]; + } else { // An insertion or deletion. + if (diffs[pointer][0] == DIFF_INSERT) { + length_insertions2 += diffs[pointer][1].length; + } else { + length_deletions2 += diffs[pointer][1].length; + } + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && (lastequality.length <= + Math.max(length_insertions1, length_deletions1)) && + (lastequality.length <= Math.max(length_insertions2, + length_deletions2))) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, + [DIFF_DELETE, lastequality]); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + // Throw away the equality we just deleted. + equalitiesLength--; + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + length_insertions1 = 0; // Reset the counters. + length_deletions1 = 0; + length_insertions2 = 0; + length_deletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diff_cleanupMerge(diffs); + } + this.diff_cleanupSemanticLossless(diffs); + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] == DIFF_DELETE && + diffs[pointer][0] == DIFF_INSERT) { + var deletion = diffs[pointer - 1][1]; + var insertion = diffs[pointer][1]; + var overlap_length1 = this.diff_commonOverlap_(deletion, insertion); + var overlap_length2 = this.diff_commonOverlap_(insertion, deletion); + if (overlap_length1 >= overlap_length2) { + if (overlap_length1 >= deletion.length / 2 || + overlap_length1 >= insertion.length / 2) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice(pointer, 0, + [DIFF_EQUAL, insertion.substring(0, overlap_length1)]); + diffs[pointer - 1][1] = + deletion.substring(0, deletion.length - overlap_length1); + diffs[pointer + 1][1] = insertion.substring(overlap_length1); + pointer++; + } + } else { + if (overlap_length2 >= deletion.length / 2 || + overlap_length2 >= insertion.length / 2) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice(pointer, 0, + [DIFF_EQUAL, deletion.substring(0, overlap_length2)]); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = + insertion.substring(0, insertion.length - overlap_length2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = + deletion.substring(overlap_length2); + pointer++; + } + } + pointer++; + } + pointer++; + } +}; + + +/** + * Look for single edits surrounded on both sides by equalities + * which can be shifted sideways to align the edit to a word boundary. + * e.g: The cat came. -> The cat came. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) { + /** + * Given two strings, compute a score representing whether the internal + * boundary falls on logical boundaries. + * Scores range from 6 (best) to 0 (worst). + * Closure, but does not reference any external variables. + * @param {string} one First string. + * @param {string} two Second string. + * @return {number} The score. + * @private + */ + function diff_cleanupSemanticScore_(one, two) { + if (!one || !two) { + // Edges are the best. + return 6; + } + + // Each port of this function behaves slightly differently due to + // subtle differences in each language's definition of things like + // 'whitespace'. Since this function's purpose is largely cosmetic, + // the choice has been made to use each language's native features + // rather than force total conformity. + var char1 = one.charAt(one.length - 1); + var char2 = two.charAt(0); + var nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_); + var nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_); + var whitespace1 = nonAlphaNumeric1 && + char1.match(diff_match_patch.whitespaceRegex_); + var whitespace2 = nonAlphaNumeric2 && + char2.match(diff_match_patch.whitespaceRegex_); + var lineBreak1 = whitespace1 && + char1.match(diff_match_patch.linebreakRegex_); + var lineBreak2 = whitespace2 && + char2.match(diff_match_patch.linebreakRegex_); + var blankLine1 = lineBreak1 && + one.match(diff_match_patch.blanklineEndRegex_); + var blankLine2 = lineBreak2 && + two.match(diff_match_patch.blanklineStartRegex_); + + if (blankLine1 || blankLine2) { + // Five points for blank lines. + return 5; + } else if (lineBreak1 || lineBreak2) { + // Four points for line breaks. + return 4; + } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { + // Three points for end of sentences. + return 3; + } else if (whitespace1 || whitespace2) { + // Two points for whitespace. + return 2; + } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { + // One point for non-alphanumeric. + return 1; + } + return 0; + } + + var pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + var equality1 = diffs[pointer - 1][1]; + var edit = diffs[pointer][1]; + var equality2 = diffs[pointer + 1][1]; + + // First, shift the edit as far left as possible. + var commonOffset = this.diff_commonSuffix(equality1, edit); + if (commonOffset) { + var commonString = edit.substring(edit.length - commonOffset); + equality1 = equality1.substring(0, equality1.length - commonOffset); + edit = commonString + edit.substring(0, edit.length - commonOffset); + equality2 = commonString + equality2; + } + + // Second, step character by character right, looking for the best fit. + var bestEquality1 = equality1; + var bestEdit = edit; + var bestEquality2 = equality2; + var bestScore = diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + while (edit.charAt(0) === equality2.charAt(0)) { + equality1 += edit.charAt(0); + edit = edit.substring(1) + equality2.charAt(0); + equality2 = equality2.substring(1); + var score = diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + // The >= encourages trailing rather than leading whitespace on edits. + if (score >= bestScore) { + bestScore = score; + bestEquality1 = equality1; + bestEdit = edit; + bestEquality2 = equality2; + } + } + + if (diffs[pointer - 1][1] != bestEquality1) { + // We have an improvement, save it back to the diff. + if (bestEquality1) { + diffs[pointer - 1][1] = bestEquality1; + } else { + diffs.splice(pointer - 1, 1); + pointer--; + } + diffs[pointer][1] = bestEdit; + if (bestEquality2) { + diffs[pointer + 1][1] = bestEquality2; + } else { + diffs.splice(pointer + 1, 1); + pointer--; + } + } + } + pointer++; + } +}; + +// Define some regex patterns for matching boundaries. +diff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/; +diff_match_patch.whitespaceRegex_ = /\s/; +diff_match_patch.linebreakRegex_ = /[\r\n]/; +diff_match_patch.blanklineEndRegex_ = /\n\r?\n$/; +diff_match_patch.blanklineStartRegex_ = /^\r?\n\r?\n/; + +/** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) { + var changes = false; + var equalities = []; // Stack of indices where equalities are found. + var equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + var lastequality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + var pointer = 0; // Index of current position. + // Is there an insertion operation before the last equality. + var pre_ins = false; + // Is there a deletion operation before the last equality. + var pre_del = false; + // Is there an insertion operation after the last equality. + var post_ins = false; + // Is there a deletion operation after the last equality. + var post_del = false; + while (pointer < diffs.length) { + if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found. + if (diffs[pointer][1].length < this.Diff_EditCost && + (post_ins || post_del)) { + // Candidate found. + equalities[equalitiesLength++] = pointer; + pre_ins = post_ins; + pre_del = post_del; + lastequality = diffs[pointer][1]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + post_ins = post_del = false; + } else { // An insertion or deletion. + if (diffs[pointer][0] == DIFF_DELETE) { + post_del = true; + } else { + post_ins = true; + } + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if (lastequality && ((pre_ins && pre_del && post_ins && post_del) || + ((lastequality.length < this.Diff_EditCost / 2) && + (pre_ins + pre_del + post_ins + post_del) == 3))) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, + [DIFF_DELETE, lastequality]); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (pre_ins && pre_del) { + // No changes made which could affect previous entry, keep going. + post_ins = post_del = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? + equalities[equalitiesLength - 1] : -1; + post_ins = post_del = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) { + this.diff_cleanupMerge(diffs); + } +}; + + +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupMerge = function(diffs) { + diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. + var pointer = 0; + var count_delete = 0; + var count_insert = 0; + var text_delete = ''; + var text_insert = ''; + var commonlength; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = this.diff_commonPrefix(text_insert, text_delete); + if (commonlength !== 0) { + if ((pointer - count_delete - count_insert) > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL) { + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, + text_insert.substring(0, commonlength)]); + pointer++; + } + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = this.diff_commonSuffix(text_insert, text_delete); + if (commonlength !== 0) { + diffs[pointer][1] = text_insert.substring(text_insert.length - + commonlength) + diffs[pointer][1]; + text_insert = text_insert.substring(0, text_insert.length - + commonlength); + text_delete = text_delete.substring(0, text_delete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + if (count_delete === 0) { + diffs.splice(pointer - count_insert, + count_delete + count_insert, [DIFF_INSERT, text_insert]); + } else if (count_insert === 0) { + diffs.splice(pointer - count_delete, + count_delete + count_insert, [DIFF_DELETE, text_delete]); + } else { + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert, [DIFF_DELETE, text_delete], + [DIFF_INSERT, text_insert]); + } + pointer = pointer - count_delete - count_insert + + (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + var changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if (diffs[pointer][1].substring(diffs[pointer][1].length - + diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1]) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diff_cleanupMerge(diffs); + } +}; + + +/** + * loc is a location in text1, compute and return the equivalent location in + * text2. + * e.g. 'The cat' vs 'The big cat', 1->1, 5->8 + * @param {!Array.} diffs Array of diff tuples. + * @param {number} loc Location within text1. + * @return {number} Location within text2. + */ +diff_match_patch.prototype.diff_xIndex = function(diffs, loc) { + var chars1 = 0; + var chars2 = 0; + var last_chars1 = 0; + var last_chars2 = 0; + var x; + for (x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_INSERT) { // Equality or deletion. + chars1 += diffs[x][1].length; + } + if (diffs[x][0] !== DIFF_DELETE) { // Equality or insertion. + chars2 += diffs[x][1].length; + } + if (chars1 > loc) { // Overshot the location. + break; + } + last_chars1 = chars1; + last_chars2 = chars2; + } + // Was the location was deleted? + if (diffs.length != x && diffs[x][0] === DIFF_DELETE) { + return last_chars2; + } + // Add the remaining character length. + return last_chars2 + (loc - last_chars1); +}; + + +/** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @return {string} HTML representation. + */ +diff_match_patch.prototype.diff_prettyHtml = function(diffs) { + var html = []; + var pattern_amp = /&/g; + var pattern_lt = //g; + var pattern_para = /\n/g; + for (var x = 0; x < diffs.length; x++) { + var op = diffs[x][0]; // Operation (insert, delete, equal) + var data = diffs[x][1]; // Text of change. + var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') + .replace(pattern_gt, '>').replace(pattern_para, '¶
'); + switch (op) { + case DIFF_INSERT: + html[x] = '' + text + ''; + break; + case DIFF_DELETE: + html[x] = '' + text + ''; + break; + case DIFF_EQUAL: + html[x] = '' + text + ''; + break; + } + } + return html.join(''); +}; + + +/** + * Compute and return the source text (all equalities and deletions). + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Source text. + */ +diff_match_patch.prototype.diff_text1 = function(diffs) { + var text = []; + for (var x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_INSERT) { + text[x] = diffs[x][1]; + } + } + return text.join(''); +}; + + +/** + * Compute and return the destination text (all equalities and insertions). + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Destination text. + */ +diff_match_patch.prototype.diff_text2 = function(diffs) { + var text = []; + for (var x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_DELETE) { + text[x] = diffs[x][1]; + } + } + return text.join(''); +}; + + +/** + * Compute the Levenshtein distance; the number of inserted, deleted or + * substituted characters. + * @param {!Array.} diffs Array of diff tuples. + * @return {number} Number of changes. + */ +diff_match_patch.prototype.diff_levenshtein = function(diffs) { + var levenshtein = 0; + var insertions = 0; + var deletions = 0; + for (var x = 0; x < diffs.length; x++) { + var op = diffs[x][0]; + var data = diffs[x][1]; + switch (op) { + case DIFF_INSERT: + insertions += data.length; + break; + case DIFF_DELETE: + deletions += data.length; + break; + case DIFF_EQUAL: + // A deletion and an insertion is one substitution. + levenshtein += Math.max(insertions, deletions); + insertions = 0; + deletions = 0; + break; + } + } + levenshtein += Math.max(insertions, deletions); + return levenshtein; +}; + + +/** + * Crush the diff into an encoded string which describes the operations + * required to transform text1 into text2. + * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. + * Operations are tab-separated. Inserted text is escaped using %xx notation. + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Delta text. + */ +diff_match_patch.prototype.diff_toDelta = function(diffs) { + var text = []; + for (var x = 0; x < diffs.length; x++) { + switch (diffs[x][0]) { + case DIFF_INSERT: + text[x] = '+' + encodeURI(diffs[x][1]); + break; + case DIFF_DELETE: + text[x] = '-' + diffs[x][1].length; + break; + case DIFF_EQUAL: + text[x] = '=' + diffs[x][1].length; + break; + } + } + return text.join('\t').replace(/%20/g, ' '); +}; + + +/** + * Given the original text1, and an encoded string which describes the + * operations required to transform text1 into text2, compute the full diff. + * @param {string} text1 Source string for the diff. + * @param {string} delta Delta text. + * @return {!Array.} Array of diff tuples. + * @throws {!Error} If invalid input. + */ +diff_match_patch.prototype.diff_fromDelta = function(text1, delta) { + var diffs = []; + var diffsLength = 0; // Keeping our own length var is faster in JS. + var pointer = 0; // Cursor in text1 + var tokens = delta.split(/\t/g); + for (var x = 0; x < tokens.length; x++) { + // Each token begins with a one character parameter which specifies the + // operation of this token (delete, insert, equality). + var param = tokens[x].substring(1); + switch (tokens[x].charAt(0)) { + case '+': + try { + diffs[diffsLength++] = [DIFF_INSERT, decodeURI(param)]; + } catch (ex) { + // Malformed URI sequence. + throw new Error('Illegal escape in diff_fromDelta: ' + param); + } + break; + case '-': + // Fall through. + case '=': + var n = parseInt(param, 10); + if (isNaN(n) || n < 0) { + throw new Error('Invalid number in diff_fromDelta: ' + param); + } + var text = text1.substring(pointer, pointer += n); + if (tokens[x].charAt(0) == '=') { + diffs[diffsLength++] = [DIFF_EQUAL, text]; + } else { + diffs[diffsLength++] = [DIFF_DELETE, text]; + } + break; + default: + // Blank tokens are ok (from a trailing \t). + // Anything else is an error. + if (tokens[x]) { + throw new Error('Invalid diff operation in diff_fromDelta: ' + + tokens[x]); + } + } + } + if (pointer != text1.length) { + throw new Error('Delta length (' + pointer + + ') does not equal source text length (' + text1.length + ').'); + } + return diffs; +}; + + +// MATCH FUNCTIONS + + +/** + * Locate the best instance of 'pattern' in 'text' near 'loc'. + * @param {string} text The text to search. + * @param {string} pattern The pattern to search for. + * @param {number} loc The location to search around. + * @return {number} Best match index or -1. + */ +diff_match_patch.prototype.match_main = function(text, pattern, loc) { + // Check for null inputs. + if (text == null || pattern == null || loc == null) { + throw new Error('Null input. (match_main)'); + } + + loc = Math.max(0, Math.min(loc, text.length)); + if (text == pattern) { + // Shortcut (potentially not guaranteed by the algorithm) + return 0; + } else if (!text.length) { + // Nothing to match. + return -1; + } else if (text.substring(loc, loc + pattern.length) == pattern) { + // Perfect match at the perfect spot! (Includes case of null pattern) + return loc; + } else { + // Do a fuzzy compare. + return this.match_bitap_(text, pattern, loc); + } +}; + + +/** + * Locate the best instance of 'pattern' in 'text' near 'loc' using the + * Bitap algorithm. + * @param {string} text The text to search. + * @param {string} pattern The pattern to search for. + * @param {number} loc The location to search around. + * @return {number} Best match index or -1. + * @private + */ +diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) { + if (pattern.length > this.Match_MaxBits) { + throw new Error('Pattern too long for this browser.'); + } + + // Initialise the alphabet. + var s = this.match_alphabet_(pattern); + + var dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Compute and return the score for a match with e errors and x location. + * Accesses loc and pattern through being a closure. + * @param {number} e Number of errors in match. + * @param {number} x Location of match. + * @return {number} Overall score for match (0.0 = good, 1.0 = bad). + * @private + */ + function match_bitapScore_(e, x) { + var accuracy = e / pattern.length; + var proximity = Math.abs(loc - x); + if (!dmp.Match_Distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + return accuracy + (proximity / dmp.Match_Distance); + } + + // Highest score beyond which we give up. + var score_threshold = this.Match_Threshold; + // Is there a nearby exact match? (speedup) + var best_loc = text.indexOf(pattern, loc); + if (best_loc != -1) { + score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold); + // What about in the other direction? (speedup) + best_loc = text.lastIndexOf(pattern, loc + pattern.length); + if (best_loc != -1) { + score_threshold = + Math.min(match_bitapScore_(0, best_loc), score_threshold); + } + } + + // Initialise the bit arrays. + var matchmask = 1 << (pattern.length - 1); + best_loc = -1; + + var bin_min, bin_mid; + var bin_max = pattern.length + text.length; + var last_rd; + for (var d = 0; d < pattern.length; d++) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from 'loc' we can stray at this + // error level. + bin_min = 0; + bin_mid = bin_max; + while (bin_min < bin_mid) { + if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) { + bin_min = bin_mid; + } else { + bin_max = bin_mid; + } + bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min); + } + // Use the result from this iteration as the maximum for the next. + bin_max = bin_mid; + var start = Math.max(1, loc - bin_mid + 1); + var finish = Math.min(loc + bin_mid, text.length) + pattern.length; + + var rd = Array(finish + 2); + rd[finish + 1] = (1 << d) - 1; + for (var j = finish; j >= start; j--) { + // The alphabet (s) is a sparse hash, so the following line generates + // warnings. + var charMatch = s[text.charAt(j - 1)]; + if (d === 0) { // First pass: exact match. + rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; + } else { // Subsequent passes: fuzzy match. + rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) | + (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | + last_rd[j + 1]; + } + if (rd[j] & matchmask) { + var score = match_bitapScore_(d, j - 1); + // This match will almost certainly be better than any existing match. + // But check anyway. + if (score <= score_threshold) { + // Told you so. + score_threshold = score; + best_loc = j - 1; + if (best_loc > loc) { + // When passing loc, don't exceed our current distance from loc. + start = Math.max(1, 2 * loc - best_loc); + } else { + // Already passed loc, downhill from here on in. + break; + } + } + } + } + // No hope for a (better) match at greater error levels. + if (match_bitapScore_(d + 1, loc) > score_threshold) { + break; + } + last_rd = rd; + } + return best_loc; +}; + + +/** + * Initialise the alphabet for the Bitap algorithm. + * @param {string} pattern The text to encode. + * @return {!Object} Hash of character locations. + * @private + */ +diff_match_patch.prototype.match_alphabet_ = function(pattern) { + var s = {}; + for (var i = 0; i < pattern.length; i++) { + s[pattern.charAt(i)] = 0; + } + for (var i = 0; i < pattern.length; i++) { + s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1); + } + return s; +}; + + +// PATCH FUNCTIONS + + +/** + * Increase the context until it is unique, + * but don't let the pattern expand beyond Match_MaxBits. + * @param {!diff_match_patch.patch_obj} patch The patch to grow. + * @param {string} text Source text. + * @private + */ +diff_match_patch.prototype.patch_addContext_ = function(patch, text) { + if (text.length == 0) { + return; + } + var pattern = text.substring(patch.start2, patch.start2 + patch.length1); + var padding = 0; + + // Look for the first and last matches of pattern in text. If two different + // matches are found, increase the pattern length. + while (text.indexOf(pattern) != text.lastIndexOf(pattern) && + pattern.length < this.Match_MaxBits - this.Patch_Margin - + this.Patch_Margin) { + padding += this.Patch_Margin; + pattern = text.substring(patch.start2 - padding, + patch.start2 + patch.length1 + padding); + } + // Add one chunk for good luck. + padding += this.Patch_Margin; + + // Add the prefix. + var prefix = text.substring(patch.start2 - padding, patch.start2); + if (prefix) { + patch.diffs.unshift([DIFF_EQUAL, prefix]); + } + // Add the suffix. + var suffix = text.substring(patch.start2 + patch.length1, + patch.start2 + patch.length1 + padding); + if (suffix) { + patch.diffs.push([DIFF_EQUAL, suffix]); + } + + // Roll back the start points. + patch.start1 -= prefix.length; + patch.start2 -= prefix.length; + // Extend the lengths. + patch.length1 += prefix.length + suffix.length; + patch.length2 += prefix.length + suffix.length; +}; + + +/** + * Compute a list of patches to turn text1 into text2. + * Use diffs if provided, otherwise compute it ourselves. + * There are four ways to call this function, depending on what data is + * available to the caller: + * Method 1: + * a = text1, b = text2 + * Method 2: + * a = diffs + * Method 3 (optimal): + * a = text1, b = diffs + * Method 4 (deprecated, use method 3): + * a = text1, b = text2, c = diffs + * + * @param {string|!Array.} a text1 (methods 1,3,4) or + * Array of diff tuples for text1 to text2 (method 2). + * @param {string|!Array.} opt_b text2 (methods 1,4) or + * Array of diff tuples for text1 to text2 (method 3) or undefined (method 2). + * @param {string|!Array.} opt_c Array of diff tuples + * for text1 to text2 (method 4) or undefined (methods 1,2,3). + * @return {!Array.} Array of Patch objects. + */ +diff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) { + var text1, diffs; + if (typeof a == 'string' && typeof opt_b == 'string' && + typeof opt_c == 'undefined') { + // Method 1: text1, text2 + // Compute diffs from text1 and text2. + text1 = /** @type {string} */(a); + diffs = this.diff_main(text1, /** @type {string} */(opt_b), true); + if (diffs.length > 2) { + this.diff_cleanupSemantic(diffs); + this.diff_cleanupEfficiency(diffs); + } + } else if (a && typeof a == 'object' && typeof opt_b == 'undefined' && + typeof opt_c == 'undefined') { + // Method 2: diffs + // Compute text1 from diffs. + diffs = /** @type {!Array.} */(a); + text1 = this.diff_text1(diffs); + } else if (typeof a == 'string' && opt_b && typeof opt_b == 'object' && + typeof opt_c == 'undefined') { + // Method 3: text1, diffs + text1 = /** @type {string} */(a); + diffs = /** @type {!Array.} */(opt_b); + } else if (typeof a == 'string' && typeof opt_b == 'string' && + opt_c && typeof opt_c == 'object') { + // Method 4: text1, text2, diffs + // text2 is not used. + text1 = /** @type {string} */(a); + diffs = /** @type {!Array.} */(opt_c); + } else { + throw new Error('Unknown call format to patch_make.'); + } + + if (diffs.length === 0) { + return []; // Get rid of the null case. + } + var patches = []; + var patch = new diff_match_patch.patch_obj(); + var patchDiffLength = 0; // Keeping our own length var is faster in JS. + var char_count1 = 0; // Number of characters into the text1 string. + var char_count2 = 0; // Number of characters into the text2 string. + // Start with text1 (prepatch_text) and apply the diffs until we arrive at + // text2 (postpatch_text). We recreate the patches one by one to determine + // context info. + var prepatch_text = text1; + var postpatch_text = text1; + for (var x = 0; x < diffs.length; x++) { + var diff_type = diffs[x][0]; + var diff_text = diffs[x][1]; + + if (!patchDiffLength && diff_type !== DIFF_EQUAL) { + // A new patch starts here. + patch.start1 = char_count1; + patch.start2 = char_count2; + } + + switch (diff_type) { + case DIFF_INSERT: + patch.diffs[patchDiffLength++] = diffs[x]; + patch.length2 += diff_text.length; + postpatch_text = postpatch_text.substring(0, char_count2) + diff_text + + postpatch_text.substring(char_count2); + break; + case DIFF_DELETE: + patch.length1 += diff_text.length; + patch.diffs[patchDiffLength++] = diffs[x]; + postpatch_text = postpatch_text.substring(0, char_count2) + + postpatch_text.substring(char_count2 + + diff_text.length); + break; + case DIFF_EQUAL: + if (diff_text.length <= 2 * this.Patch_Margin && + patchDiffLength && diffs.length != x + 1) { + // Small equality inside a patch. + patch.diffs[patchDiffLength++] = diffs[x]; + patch.length1 += diff_text.length; + patch.length2 += diff_text.length; + } else if (diff_text.length >= 2 * this.Patch_Margin) { + // Time for a new patch. + if (patchDiffLength) { + this.patch_addContext_(patch, prepatch_text); + patches.push(patch); + patch = new diff_match_patch.patch_obj(); + patchDiffLength = 0; + // Unlike Unidiff, our patch lists have a rolling context. + // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff + // Update prepatch text & pos to reflect the application of the + // just completed patch. + prepatch_text = postpatch_text; + char_count1 = char_count2; + } + } + break; + } + + // Update the current character count. + if (diff_type !== DIFF_INSERT) { + char_count1 += diff_text.length; + } + if (diff_type !== DIFF_DELETE) { + char_count2 += diff_text.length; + } + } + // Pick up the leftover patch if not empty. + if (patchDiffLength) { + this.patch_addContext_(patch, prepatch_text); + patches.push(patch); + } + + return patches; +}; + + +/** + * Given an array of patches, return another array that is identical. + * @param {!Array.} patches Array of Patch objects. + * @return {!Array.} Array of Patch objects. + */ +diff_match_patch.prototype.patch_deepCopy = function(patches) { + // Making deep copies is hard in JavaScript. + var patchesCopy = []; + for (var x = 0; x < patches.length; x++) { + var patch = patches[x]; + var patchCopy = new diff_match_patch.patch_obj(); + patchCopy.diffs = []; + for (var y = 0; y < patch.diffs.length; y++) { + patchCopy.diffs[y] = patch.diffs[y].slice(); + } + patchCopy.start1 = patch.start1; + patchCopy.start2 = patch.start2; + patchCopy.length1 = patch.length1; + patchCopy.length2 = patch.length2; + patchesCopy[x] = patchCopy; + } + return patchesCopy; +}; + + +/** + * Merge a set of patches onto the text. Return a patched text, as well + * as a list of true/false values indicating which patches were applied. + * @param {!Array.} patches Array of Patch objects. + * @param {string} text Old text. + * @return {!Array.>} Two element Array, containing the + * new text and an array of boolean values. + */ +diff_match_patch.prototype.patch_apply = function(patches, text) { + if (patches.length == 0) { + return [text, []]; + } + + // Deep copy the patches so that no changes are made to originals. + patches = this.patch_deepCopy(patches); + + var nullPadding = this.patch_addPadding(patches); + text = nullPadding + text + nullPadding; + + this.patch_splitMax(patches); + // delta keeps track of the offset between the expected and actual location + // of the previous patch. If there are patches expected at positions 10 and + // 20, but the first patch was found at 12, delta is 2 and the second patch + // has an effective expected position of 22. + var delta = 0; + var results = []; + for (var x = 0; x < patches.length; x++) { + var expected_loc = patches[x].start2 + delta; + var text1 = this.diff_text1(patches[x].diffs); + var start_loc; + var end_loc = -1; + if (text1.length > this.Match_MaxBits) { + // patch_splitMax will only provide an oversized pattern in the case of + // a monster delete. + start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits), + expected_loc); + if (start_loc != -1) { + end_loc = this.match_main(text, + text1.substring(text1.length - this.Match_MaxBits), + expected_loc + text1.length - this.Match_MaxBits); + if (end_loc == -1 || start_loc >= end_loc) { + // Can't find valid trailing context. Drop this patch. + start_loc = -1; + } + } + } else { + start_loc = this.match_main(text, text1, expected_loc); + } + if (start_loc == -1) { + // No match found. :( + results[x] = false; + // Subtract the delta for this failed patch from subsequent patches. + delta -= patches[x].length2 - patches[x].length1; + } else { + // Found a match. :) + results[x] = true; + delta = start_loc - expected_loc; + var text2; + if (end_loc == -1) { + text2 = text.substring(start_loc, start_loc + text1.length); + } else { + text2 = text.substring(start_loc, end_loc + this.Match_MaxBits); + } + if (text1 == text2) { + // Perfect match, just shove the replacement text in. + text = text.substring(0, start_loc) + + this.diff_text2(patches[x].diffs) + + text.substring(start_loc + text1.length); + } else { + // Imperfect match. Run a diff to get a framework of equivalent + // indices. + var diffs = this.diff_main(text1, text2, false); + if (text1.length > this.Match_MaxBits && + this.diff_levenshtein(diffs) / text1.length > + this.Patch_DeleteThreshold) { + // The end points match, but the content is unacceptably bad. + results[x] = false; + } else { + this.diff_cleanupSemanticLossless(diffs); + var index1 = 0; + var index2; + for (var y = 0; y < patches[x].diffs.length; y++) { + var mod = patches[x].diffs[y]; + if (mod[0] !== DIFF_EQUAL) { + index2 = this.diff_xIndex(diffs, index1); + } + if (mod[0] === DIFF_INSERT) { // Insertion + text = text.substring(0, start_loc + index2) + mod[1] + + text.substring(start_loc + index2); + } else if (mod[0] === DIFF_DELETE) { // Deletion + text = text.substring(0, start_loc + index2) + + text.substring(start_loc + this.diff_xIndex(diffs, + index1 + mod[1].length)); + } + if (mod[0] !== DIFF_DELETE) { + index1 += mod[1].length; + } + } + } + } + } + } + // Strip the padding off. + text = text.substring(nullPadding.length, text.length - nullPadding.length); + return [text, results]; +}; + + +/** + * Add some padding on text start and end so that edges can match something. + * Intended to be called only from within patch_apply. + * @param {!Array.} patches Array of Patch objects. + * @return {string} The padding string added to each side. + */ +diff_match_patch.prototype.patch_addPadding = function(patches) { + var paddingLength = this.Patch_Margin; + var nullPadding = ''; + for (var x = 1; x <= paddingLength; x++) { + nullPadding += String.fromCharCode(x); + } + + // Bump all the patches forward. + for (var x = 0; x < patches.length; x++) { + patches[x].start1 += paddingLength; + patches[x].start2 += paddingLength; + } + + // Add some padding on start of first diff. + var patch = patches[0]; + var diffs = patch.diffs; + if (diffs.length == 0 || diffs[0][0] != DIFF_EQUAL) { + // Add nullPadding equality. + diffs.unshift([DIFF_EQUAL, nullPadding]); + patch.start1 -= paddingLength; // Should be 0. + patch.start2 -= paddingLength; // Should be 0. + patch.length1 += paddingLength; + patch.length2 += paddingLength; + } else if (paddingLength > diffs[0][1].length) { + // Grow first equality. + var extraLength = paddingLength - diffs[0][1].length; + diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1]; + patch.start1 -= extraLength; + patch.start2 -= extraLength; + patch.length1 += extraLength; + patch.length2 += extraLength; + } + + // Add some padding on end of last diff. + patch = patches[patches.length - 1]; + diffs = patch.diffs; + if (diffs.length == 0 || diffs[diffs.length - 1][0] != DIFF_EQUAL) { + // Add nullPadding equality. + diffs.push([DIFF_EQUAL, nullPadding]); + patch.length1 += paddingLength; + patch.length2 += paddingLength; + } else if (paddingLength > diffs[diffs.length - 1][1].length) { + // Grow last equality. + var extraLength = paddingLength - diffs[diffs.length - 1][1].length; + diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength); + patch.length1 += extraLength; + patch.length2 += extraLength; + } + + return nullPadding; +}; + + +/** + * Look through the patches and break up any which are longer than the maximum + * limit of the match algorithm. + * Intended to be called only from within patch_apply. + * @param {!Array.} patches Array of Patch objects. + */ +diff_match_patch.prototype.patch_splitMax = function(patches) { + var patch_size = this.Match_MaxBits; + for (var x = 0; x < patches.length; x++) { + if (patches[x].length1 <= patch_size) { + continue; + } + var bigpatch = patches[x]; + // Remove the big old patch. + patches.splice(x--, 1); + var start1 = bigpatch.start1; + var start2 = bigpatch.start2; + var precontext = ''; + while (bigpatch.diffs.length !== 0) { + // Create one of several smaller patches. + var patch = new diff_match_patch.patch_obj(); + var empty = true; + patch.start1 = start1 - precontext.length; + patch.start2 = start2 - precontext.length; + if (precontext !== '') { + patch.length1 = patch.length2 = precontext.length; + patch.diffs.push([DIFF_EQUAL, precontext]); + } + while (bigpatch.diffs.length !== 0 && + patch.length1 < patch_size - this.Patch_Margin) { + var diff_type = bigpatch.diffs[0][0]; + var diff_text = bigpatch.diffs[0][1]; + if (diff_type === DIFF_INSERT) { + // Insertions are harmless. + patch.length2 += diff_text.length; + start2 += diff_text.length; + patch.diffs.push(bigpatch.diffs.shift()); + empty = false; + } else if (diff_type === DIFF_DELETE && patch.diffs.length == 1 && + patch.diffs[0][0] == DIFF_EQUAL && + diff_text.length > 2 * patch_size) { + // This is a large deletion. Let it pass in one chunk. + patch.length1 += diff_text.length; + start1 += diff_text.length; + empty = false; + patch.diffs.push([diff_type, diff_text]); + bigpatch.diffs.shift(); + } else { + // Deletion or equality. Only take as much as we can stomach. + diff_text = diff_text.substring(0, + patch_size - patch.length1 - this.Patch_Margin); + patch.length1 += diff_text.length; + start1 += diff_text.length; + if (diff_type === DIFF_EQUAL) { + patch.length2 += diff_text.length; + start2 += diff_text.length; + } else { + empty = false; + } + patch.diffs.push([diff_type, diff_text]); + if (diff_text == bigpatch.diffs[0][1]) { + bigpatch.diffs.shift(); + } else { + bigpatch.diffs[0][1] = + bigpatch.diffs[0][1].substring(diff_text.length); + } + } + } + // Compute the head context for the next patch. + precontext = this.diff_text2(patch.diffs); + precontext = + precontext.substring(precontext.length - this.Patch_Margin); + // Append the end context for this patch. + var postcontext = this.diff_text1(bigpatch.diffs) + .substring(0, this.Patch_Margin); + if (postcontext !== '') { + patch.length1 += postcontext.length; + patch.length2 += postcontext.length; + if (patch.diffs.length !== 0 && + patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) { + patch.diffs[patch.diffs.length - 1][1] += postcontext; + } else { + patch.diffs.push([DIFF_EQUAL, postcontext]); + } + } + if (!empty) { + patches.splice(++x, 0, patch); + } + } + } +}; + + +/** + * Take a list of patches and return a textual representation. + * @param {!Array.} patches Array of Patch objects. + * @return {string} Text representation of patches. + */ +diff_match_patch.prototype.patch_toText = function(patches) { + var text = []; + for (var x = 0; x < patches.length; x++) { + text[x] = patches[x]; + } + return text.join(''); +}; + + +/** + * Parse a textual representation of patches and return a list of Patch objects. + * @param {string} textline Text representation of patches. + * @return {!Array.} Array of Patch objects. + * @throws {!Error} If invalid input. + */ +diff_match_patch.prototype.patch_fromText = function(textline) { + var patches = []; + if (!textline) { + return patches; + } + var text = textline.split('\n'); + var textPointer = 0; + var patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/; + while (textPointer < text.length) { + var m = text[textPointer].match(patchHeader); + if (!m) { + throw new Error('Invalid patch string: ' + text[textPointer]); + } + var patch = new diff_match_patch.patch_obj(); + patches.push(patch); + patch.start1 = parseInt(m[1], 10); + if (m[2] === '') { + patch.start1--; + patch.length1 = 1; + } else if (m[2] == '0') { + patch.length1 = 0; + } else { + patch.start1--; + patch.length1 = parseInt(m[2], 10); + } + + patch.start2 = parseInt(m[3], 10); + if (m[4] === '') { + patch.start2--; + patch.length2 = 1; + } else if (m[4] == '0') { + patch.length2 = 0; + } else { + patch.start2--; + patch.length2 = parseInt(m[4], 10); + } + textPointer++; + + while (textPointer < text.length) { + var sign = text[textPointer].charAt(0); + try { + var line = decodeURI(text[textPointer].substring(1)); + } catch (ex) { + // Malformed URI sequence. + throw new Error('Illegal escape in patch_fromText: ' + line); + } + if (sign == '-') { + // Deletion. + patch.diffs.push([DIFF_DELETE, line]); + } else if (sign == '+') { + // Insertion. + patch.diffs.push([DIFF_INSERT, line]); + } else if (sign == ' ') { + // Minor equality. + patch.diffs.push([DIFF_EQUAL, line]); + } else if (sign == '@') { + // Start of next patch. + break; + } else if (sign === '') { + // Blank line? Whatever. + } else { + // WTF? + throw new Error('Invalid patch mode "' + sign + '" in: ' + line); + } + textPointer++; + } + } + return patches; +}; + + +/** + * Class representing one patch operation. + * @constructor + */ +diff_match_patch.patch_obj = function() { + /** @type {!Array.} */ + this.diffs = []; + /** @type {?number} */ + this.start1 = null; + /** @type {?number} */ + this.start2 = null; + /** @type {number} */ + this.length1 = 0; + /** @type {number} */ + this.length2 = 0; +}; + + +/** + * Emmulate GNU diff's format. + * Header: @@ -382,8 +481,9 @@ + * Indicies are printed as 1-based, not 0-based. + * @return {string} The GNU diff string. + */ +diff_match_patch.patch_obj.prototype.toString = function() { + var coords1, coords2; + if (this.length1 === 0) { + coords1 = this.start1 + ',0'; + } else if (this.length1 == 1) { + coords1 = this.start1 + 1; + } else { + coords1 = (this.start1 + 1) + ',' + this.length1; + } + if (this.length2 === 0) { + coords2 = this.start2 + ',0'; + } else if (this.length2 == 1) { + coords2 = this.start2 + 1; + } else { + coords2 = (this.start2 + 1) + ',' + this.length2; + } + var text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\n']; + var op; + // Escape the body of the patch with %xx notation. + for (var x = 0; x < this.diffs.length; x++) { + switch (this.diffs[x][0]) { + case DIFF_INSERT: + op = '+'; + break; + case DIFF_DELETE: + op = '-'; + break; + case DIFF_EQUAL: + op = ' '; + break; + } + text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\n'; + } + return text.join('').replace(/%20/g, ' '); +}; + + +// Export these global variables so that they survive Google's JS compiler. +// In a browser, 'this' will be 'window'. +// Users of node.js should 'require' the uncompressed version since Google's +// JS compiler may break the following exports for non-browser environments. +this['diff_match_patch'] = diff_match_patch; +this['DIFF_DELETE'] = DIFF_DELETE; +this['DIFF_INSERT'] = DIFF_INSERT; +this['DIFF_EQUAL'] = DIFF_EQUAL; diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index eab7422291..d2e449b646 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -345,6 +345,47 @@ describe "UpdateCompressor", -> meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id v: 43 }] + + describe "delete - insert", -> + it "should do a diff of the content", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, d: "one two three four five six seven eight" } + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 3, i: "one 2 three four five six seven eight" } + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: { p: 7, d: "two" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 + }, { + op: { p: 7, i: "2" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 + }] + + it "should do a diff of the content", -> + expect(@UpdateCompressor.compressUpdates [{ + op: { p: 3, d: "one two three four five six seven eight" } + meta: ts: @ts1, user_id: @user_id + v: 42 + }, { + op: { p: 3, i: "one 2 three four five six seven eight" } + meta: ts: @ts2, user_id: @user_id + v: 43 + }]) + .to.deep.equal [{ + op: { p: 7, d: "two" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 + }, { + op: { p: 7, i: "2" } + meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + v: 43 + }] describe "noop - insert", -> it "should leave them untouched", -> From dfe26262ec9197a722aaf1359efd8034e91c030f Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 Sep 2016 11:50:44 +0100 Subject: [PATCH 324/549] Return a No-op if diff returns nothing --- .../app/coffee/UpdateCompressor.coffee | 22 ++++++++++++++----- .../UpdateCompressorTests.coffee | 15 +++++-------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index f55d7462ea..3ae5adf8fb 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -162,16 +162,28 @@ module.exports = UpdateCompressor = else if firstOp.d? and secondOp.i? and firstOp.p == secondOp.p offset = firstOp.p diff_ops = @diffAsShareJsOps(firstOp.d, secondOp.i) - return diff_ops.map (op) -> - op.p += offset - return { + if diff_ops.length == 0 + return [{ # Noop meta: start_ts: firstUpdate.meta.start_ts end_ts: secondUpdate.meta.end_ts user_id: firstUpdate.meta.user_id - op: op + op: + p: firstOp.p + i: "" v: secondUpdate.v - } + }] + else + return diff_ops.map (op) -> + op.p += offset + return { + meta: + start_ts: firstUpdate.meta.start_ts + end_ts: secondUpdate.meta.end_ts + user_id: firstUpdate.meta.user_id + op: op + v: secondUpdate.v + } else return [firstUpdate, secondUpdate] diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index d2e449b646..b4858b79b0 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -7,7 +7,8 @@ SandboxedModule = require('sandboxed-module') describe "UpdateCompressor", -> beforeEach -> - @UpdateCompressor = SandboxedModule.require modulePath + @UpdateCompressor = SandboxedModule.require modulePath, requires: + "../lib/diff_match_patch": require("../../../../app/lib/diff_match_patch") @user_id = "user-id-1" @other_user_id = "user-id-2" @bigstring = ("a" for [0 .. 2*1024*1024]).join("") @@ -366,23 +367,19 @@ describe "UpdateCompressor", -> meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id v: 43 }] - - it "should do a diff of the content", -> + + it "should return a no-op if the delete and insert are the same", -> expect(@UpdateCompressor.compressUpdates [{ op: { p: 3, d: "one two three four five six seven eight" } meta: ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 3, i: "one 2 three four five six seven eight" } + op: { p: 3, i: "one two three four five six seven eight" } meta: ts: @ts2, user_id: @user_id v: 43 }]) .to.deep.equal [{ - op: { p: 7, d: "two" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id - v: 43 - }, { - op: { p: 7, i: "2" } + op: { p: 3, i: "" } meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id v: 43 }] From 8dda44f880124a5dd9ae87aebe5ce0dca09e8f75 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 16 Sep 2016 11:51:41 +0100 Subject: [PATCH 325/549] Speed up unit tests by only generating big fixtures once up front --- .../UpdateCompressorTests.coffee | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index b4858b79b0..05fe923ab3 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -5,14 +5,15 @@ expect = chai.expect modulePath = "../../../../app/js/UpdateCompressor.js" SandboxedModule = require('sandboxed-module') +bigstring = ("a" for [0 .. 2*1024*1024]).join("") +mediumstring = ("a" for [0 .. 1024*1024]).join("") + describe "UpdateCompressor", -> beforeEach -> @UpdateCompressor = SandboxedModule.require modulePath, requires: "../lib/diff_match_patch": require("../../../../app/lib/diff_match_patch") @user_id = "user-id-1" @other_user_id = "user-id-2" - @bigstring = ("a" for [0 .. 2*1024*1024]).join("") - @mediumstring = ("a" for [0 .. 1024*1024]).join("") @ts1 = Date.now() @ts2 = Date.now() + 1000 @@ -150,7 +151,7 @@ describe "UpdateCompressor", -> meta: ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 6, i: @bigstring } + op: { p: 6, i: bigstring } meta: ts: @ts2, user_id: @user_id v: 43 }]) @@ -159,47 +160,47 @@ describe "UpdateCompressor", -> meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 6, i: @bigstring } + op: { p: 6, i: bigstring } meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id v: 43 }] it "should not append inserts that are too big (first op)", -> expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: @bigstring } + op: { p: 3, i: bigstring } meta: ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 3 + @bigstring.length, i: "bar" } + op: { p: 3 + bigstring.length, i: "bar" } meta: ts: @ts2, user_id: @user_id v: 43 }]) .to.deep.equal [{ - op: { p: 3, i: @bigstring } + op: { p: 3, i: bigstring } meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 3 + @bigstring.length, i: "bar" } + op: { p: 3 + bigstring.length, i: "bar" } meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id v: 43 }] it "should not append inserts that are too big (first and second op)", -> expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: @mediumstring } + op: { p: 3, i: mediumstring } meta: ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 3 + @mediumstring.length, i: @mediumstring } + op: { p: 3 + mediumstring.length, i: mediumstring } meta: ts: @ts2, user_id: @user_id v: 43 }]) .to.deep.equal [{ - op: { p: 3, i: @mediumstring } + op: { p: 3, i: mediumstring } meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id v: 42 }, { - op: { p: 3 + @mediumstring.length, i: @mediumstring } + op: { p: 3 + mediumstring.length, i: mediumstring } meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id v: 43 }] From 05a048db9ab6bfb2beed184e8708cd2b4eff1d0e Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 22 Sep 2016 11:13:26 +0100 Subject: [PATCH 326/549] Add in a little more logging --- services/track-changes/app/coffee/DiffGenerator.coffee | 2 +- services/track-changes/app/coffee/DiffManager.coffee | 2 ++ services/track-changes/app/coffee/DocumentUpdaterManager.coffee | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index b4e0156f02..c9c8e1d4bd 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -18,7 +18,7 @@ module.exports = DiffGenerator = if e instanceof ConsistencyError and i = update.op.length - 1 # catch known case where the last op in an array has been # merged into a later op - logger.error {update, op: JSON.stringify(op)}, "marking op as broken" + logger.error {err: e, update, op: JSON.stringify(op)}, "marking op as broken" op.broken = true else throw e # rethrow the execption diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index dfdb1a80a2..71692b9d83 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -49,6 +49,8 @@ module.exports = DiffManager = lastUpdate = updates[0] if lastUpdate? and lastUpdate.v != version - 1 return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") + + logger.log {docVersion: version, lastUpdateVersion: lastUpdate?.v, updateCount: updates.length}, "rewinding updates" tryUpdates = updates.slice().reverse() diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee index edc2f11c68..625622206e 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee @@ -14,6 +14,7 @@ module.exports = DocumentUpdaterManager = body = JSON.parse(body) catch error return callback(error) + logger.log {project_id, doc_id, version: body.version}, "got doc from document updater" callback null, body.lines.join("\n"), body.version else error = new Error("doc updater returned a non-success status code: #{res.statusCode}") From cce93b95a544d45fe6f98f1eb2233fe6c8b7a701 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 22 Sep 2016 11:18:10 +0100 Subject: [PATCH 327/549] Fetch updates before doc content when building a diff --- services/track-changes/app/coffee/DiffManager.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 71692b9d83..94e2f0adf6 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -5,11 +5,12 @@ logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> - # retrieve the document before retreiving the updates, - # because updates are written to mongo after the document - DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> + # Whichever order these are in, there is potential for updates to come in between + # them so that they do not return the same 'latest' versions. + # TODO: If these don't return consistent content, try again. + UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? - UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> + DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? callback(null, content, version, updates) From bddd1fda7d4fe7338c08a399912f8eeae7b07354 Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 30 Sep 2016 11:36:47 +0100 Subject: [PATCH 328/549] Retry rewind if doc and update versions don't match --- .../app/coffee/DiffManager.coffee | 36 +++++++-- .../DiffManager/DiffManagerTests.coffee | 78 ++++++++++++++++++- 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 94e2f0adf6..382b366b49 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -5,12 +5,11 @@ logger = require "logger-sharelatex" module.exports = DiffManager = getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> - # Whichever order these are in, there is potential for updates to come in between - # them so that they do not return the same 'latest' versions. - # TODO: If these don't return consistent content, try again. - UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> + # Get updates last, since then they must be ahead and it + # might be possible to rewind to the same version as the doc. + DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? - DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> + UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> return callback(error) if error? callback(null, content, version, updates) @@ -34,7 +33,28 @@ module.exports = DiffManager = callback(null, diff) - getDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) -> + getDocumentBeforeVersion: (project_id, doc_id, version, _callback = (error, document, rewoundUpdates) ->) -> + # Whichever order we get the latest document and the latest updates, + # there is potential for updates to be applied between them so that + # they do not return the same 'latest' versions. + # If this happens, we just retry and hopefully get them at the compatible + # versions. + retries = 3 + callback = (error, args...) -> + if error? + if error.retry and retries > 0 + logger.warn {error, project_id, doc_id, version}, "retrying getDocumentBeforeVersion" + retry() + else + _callback(error) + else + _callback(null, args...) + + do retry = () -> + retries-- + DiffManager._tryGetDocumentBeforeVersion(project_id, doc_id, version, callback) + + _tryGetDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) -> logger.log project_id: project_id, doc_id: doc_id, version: version, "getting document before version" DiffManager.getLatestDocAndUpdates project_id, doc_id, version, null, (error, content, version, updates) -> return callback(error) if error? @@ -49,7 +69,9 @@ module.exports = DiffManager = lastUpdate = updates[0] if lastUpdate? and lastUpdate.v != version - 1 - return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") + error = new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") + error.retry = true + callback error logger.log {docVersion: version, lastUpdateVersion: lastUpdate?.v, updateCount: updates.length}, "rewinding updates" diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 388520293e..5e906959d5 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -90,6 +90,76 @@ describe "DiffManager", -> .should.equal true describe "getDocumentBeforeVersion", -> + beforeEach -> + @DiffManager._tryGetDocumentBeforeVersion = sinon.stub() + @document = "mock-documents" + @rewound_updates = "mock-rewound-updates" + + describe "succesfully", -> + beforeEach -> + @DiffManager._tryGetDocumentBeforeVersion.yields(null, @document, @rewound_updates) + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + + it "should call _tryGetDocumentBeforeVersion", -> + @DiffManager._tryGetDocumentBeforeVersion + .calledWith(@project_id, @doc_id, @version) + .should.equal true + + it "should call the callback with the response", -> + @callback.calledWith(null, @document, @rewound_updates).should.equal true + + describe "with a retry needed", -> + beforeEach -> + retried = false + @DiffManager._tryGetDocumentBeforeVersion = (project_id, doc_id, version, callback) => + if !retried + retried = true + error = new Error() + error.retry = true + callback error + else + callback(null, @document, @rewound_updates) + sinon.spy @DiffManager, "_tryGetDocumentBeforeVersion" + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + + it "should call _tryGetDocumentBeforeVersion twice", -> + @DiffManager._tryGetDocumentBeforeVersion + .calledTwice + .should.equal true + + it "should call the callback with the response", -> + @callback.calledWith(null, @document, @rewound_updates).should.equal true + + describe "with a non-retriable error", -> + beforeEach -> + @error = new Error("oops") + @DiffManager._tryGetDocumentBeforeVersion.yields(@error) + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + + it "should call _tryGetDocumentBeforeVersion once", -> + @DiffManager._tryGetDocumentBeforeVersion + .calledOnce + .should.equal true + + it "should call the callback with the error", -> + @callback.calledWith(@error).should.equal true + + describe "when retry limit is matched", -> + beforeEach -> + @error = new Error("oops") + @error.retry = true + @DiffManager._tryGetDocumentBeforeVersion.yields(@error) + @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + + it "should call _tryGetDocumentBeforeVersion three times (max retries)", -> + @DiffManager._tryGetDocumentBeforeVersion + .calledThrice + .should.equal true + + it "should call the callback with the error", -> + @callback.calledWith(@error).should.equal true + + describe "_tryGetDocumentBeforeVersion", -> beforeEach -> @content = "hello world" # Op versions are the version they were applied to, so doc is always one version @@ -113,7 +183,7 @@ describe "DiffManager", -> updates.reverse() return @rewound_content @rewindUpdatesWithArgs = @DiffGenerator.rewindUpdates.withArgs(@content, @updates.slice().reverse()) - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback + @DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback it "should get the latest doc and version with all recent updates", -> @DiffManager.getLatestDocAndUpdates @@ -131,12 +201,14 @@ describe "DiffManager", -> @version = 50 @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback + @DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback - it "should call the callback with an error", -> + it "should call the callback with an error with retry = true set", -> @callback .calledWith(new Error("latest update version, 40, does not match doc version, 42")) .should.equal true + error = @callback.args[0][0] + expect(error.retry).to.equal true describe "when the updates are inconsistent", -> beforeEach -> From deced1aa15a87f23a229491651039ebaf31b50af Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 30 Sep 2016 11:41:49 +0100 Subject: [PATCH 329/549] Fix unit tests with logger.warn stub --- .../test/unit/coffee/DiffManager/DiffManagerTests.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 5e906959d5..d19f3938cf 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -8,7 +8,7 @@ SandboxedModule = require('sandboxed-module') describe "DiffManager", -> beforeEach -> @DiffManager = SandboxedModule.require modulePath, requires: - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() } "./UpdatesManager": @UpdatesManager = {} "./DocumentUpdaterManager": @DocumentUpdaterManager = {} "./DiffGenerator": @DiffGenerator = {} From a7f44bcd01afcb1ece16da9bd5f72d0f79c30e6c Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 30 Sep 2016 13:36:31 +0100 Subject: [PATCH 330/549] Add missing return on callback --- services/track-changes/app/coffee/DiffManager.coffee | 2 +- .../test/unit/coffee/DiffManager/DiffManagerTests.coffee | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index 382b366b49..f9adc56ea9 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -71,7 +71,7 @@ module.exports = DiffManager = if lastUpdate? and lastUpdate.v != version - 1 error = new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") error.retry = true - callback error + return callback error logger.log {docVersion: version, lastUpdateVersion: lastUpdate?.v, updateCount: updates.length}, "rewinding updates" diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index d19f3938cf..723193403c 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -204,9 +204,7 @@ describe "DiffManager", -> @DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback it "should call the callback with an error with retry = true set", -> - @callback - .calledWith(new Error("latest update version, 40, does not match doc version, 42")) - .should.equal true + @callback.calledOnce.should.equal true error = @callback.args[0][0] expect(error.retry).to.equal true From 3c8aeb1262349257343fffd7caf7e013903565da Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 30 Sep 2016 13:38:47 +0100 Subject: [PATCH 331/549] Log number of retries --- services/track-changes/app/coffee/DiffManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index f9adc56ea9..c01c73a533 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -43,7 +43,7 @@ module.exports = DiffManager = callback = (error, args...) -> if error? if error.retry and retries > 0 - logger.warn {error, project_id, doc_id, version}, "retrying getDocumentBeforeVersion" + logger.warn {error, project_id, doc_id, version, retries}, "retrying getDocumentBeforeVersion" retry() else _callback(error) From f8865e616d86864ee8d46644fc48f20fff6c069c Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 30 Sep 2016 14:34:13 +0100 Subject: [PATCH 332/549] Update ensureIndices to reflect reality --- services/track-changes/app/coffee/MongoManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 4376e3adb1..8390b25ef7 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -81,7 +81,7 @@ module.exports = MongoManager = # For finding all updates that go into a diff for a doc db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } # For finding all updates that affect a project - db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1, "meta.start_ts": -1 }, { background: true } + db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } # For finding updates that don't yet have a project_id and need it inserting db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } # For finding project meta-data From 40ed6fee468d332aa8c8458fc733e30f9625d2c6 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 4 Oct 2016 15:13:04 +0100 Subject: [PATCH 333/549] Split update summary on big deletes --- .../app/coffee/UpdatesManager.coffee | 24 +++++++++++- .../UpdatesManager/UpdatesManagerTests.coffee | 38 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 8506f6cdaf..bd53e81a0a 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -219,11 +219,33 @@ module.exports = UpdatesManager = return !!user_id.match(/^[a-f0-9]{24}$/) TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 + SPLIT_ON_DELETE_SIZE: 16 # characters _summarizeUpdates: (updates, existingSummarizedUpdates = []) -> summarizedUpdates = existingSummarizedUpdates.slice() + previousUpdateWasBigDelete = false for update in updates earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] - if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + shouldConcat = false + + # If a user inserts some text, then deletes a big chunk including that text, + # the update we show might concat the insert and delete, and there will be no sign + # of that insert having happened, or be able to restore to it (restoring after a big delete is common). + # So, we split the summary on 'big' deletes. However, we've stepping backwards in time with + # most recent changes considered first, so if this update is a big delete, we want to start + # a new summarized update next timge, hence we monitor the previous update. + if previousUpdateWasBigDelete + shouldConcat = false + else if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + shouldConcat = true + + isBigDelete = false + for op in update.op or [] + if op.d? and op.d.length > @SPLIT_ON_DELETE_SIZE + isBigDelete = true + + previousUpdateWasBigDelete = isBigDelete + + if shouldConcat # check if the user in this update is already present in the earliest update, # if not, add them to the users list of the earliest update earliestUpdate.meta.user_ids = _.union earliestUpdate.meta.user_ids, [update.meta.user_id] diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index cff44f07eb..161a5deb55 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -761,3 +761,41 @@ describe "UpdatesManager", -> start_ts: @now end_ts: @now + 30 }] + + it "should split updates before a big delete", -> + result = @UpdatesManager._summarizeUpdates [{ + doc_id: "doc-id-1" + op: [{ d: "this is a long long long long long delete", p: 34 }] + meta: + user_id: @user_1.id + start_ts: @now + 20 + end_ts: @now + 30 + v: 5 + }, { + doc_id: "doc-id-1" + meta: + user_id: @user_2.id + start_ts: @now + end_ts: @now + 10 + v: 4 + }] + + expect(result).to.deep.equal [{ + docs: + "doc-id-1": + fromV: 5 + toV: 5 + meta: + user_ids: [@user_1.id] + start_ts: @now + 20 + end_ts: @now + 30 + }, { + docs: + "doc-id-1": + fromV: 4 + toV: 4 + meta: + user_ids: [@user_2.id] + start_ts: @now + end_ts: @now + 10 + }] \ No newline at end of file From 694be95e66e7c40db124de15c213966416e1e3a9 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Oct 2016 11:01:50 +0100 Subject: [PATCH 334/549] Swap start_ts and end_ts comparison to correctly break chunks after 5 minutes --- services/track-changes/app/coffee/UpdatesManager.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index bd53e81a0a..ad6a583979 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -235,7 +235,9 @@ module.exports = UpdatesManager = # a new summarized update next timge, hence we monitor the previous update. if previousUpdateWasBigDelete shouldConcat = false - else if earliestUpdate and earliestUpdate.meta.start_ts - update.meta.end_ts < @TIME_BETWEEN_DISTINCT_UPDATES + else if earliestUpdate and earliestUpdate.meta.end_ts - update.meta.start_ts < @TIME_BETWEEN_DISTINCT_UPDATES + # We're going backwards in time through the updates, so only combine if this update starts less than 5 minutes before + # the end of current summarized block, so no block spans more than 5 minutes. shouldConcat = true isBigDelete = false From dadc548f81150df5f995850cbd908d87d6f40c57 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 12 Jan 2017 10:04:50 +0100 Subject: [PATCH 335/549] Ignore comment updates --- .../app/coffee/DiffGenerator.coffee | 3 +++ .../app/coffee/UpdateCompressor.coffee | 6 +++-- .../coffee/AppendingUpdatesTests.coffee | 22 +++++++++++++++++++ .../UpdateCompressorTests.coffee | 16 ++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.coffee index c9c8e1d4bd..8738621f67 100644 --- a/services/track-changes/app/coffee/DiffGenerator.coffee +++ b/services/track-changes/app/coffee/DiffGenerator.coffee @@ -47,6 +47,9 @@ module.exports = DiffGenerator = else if op.d? return content.slice(0, op.p) + op.d + content.slice(op.p) + + else + return content rewindUpdates: (content, updates) -> for update in updates.reverse() diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.coffee index 3ae5adf8fb..9e11b281e6 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.coffee +++ b/services/track-changes/app/coffee/UpdateCompressor.coffee @@ -24,7 +24,9 @@ module.exports = UpdateCompressor = convertToSingleOpUpdates: (updates) -> splitUpdates = [] for update in updates - if update.op.length == 0 + # Reject any non-insert or delete ops, i.e. comments + ops = update.op.filter (o) -> o.i? or o.d? + if ops.length == 0 splitUpdates.push op: UpdateCompressor.NOOP meta: @@ -33,7 +35,7 @@ module.exports = UpdateCompressor = user_id: update.meta.user_id v: update.v else - for op in update.op + for op in ops splitUpdates.push op: op meta: diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 3d4bf14465..69d378fd9d 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -233,6 +233,28 @@ describe "Appending doc ops to the history", -> expect(@updates[0].pack[0].v).to.equal 3 expect(@updates[0].pack[1].v).to.equal 4 + describe "when there is a comment update", -> + before (done) -> + @project_id = ObjectId().toString() + @doc_id = ObjectId().toString() + @user_id = ObjectId().toString() + MockWebApi.projects[@project_id] = features: versioning: false + TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ + op: [{ c: "foo", p: 3 }, {d: "bar", p: 6}] + meta: { ts: Date.now(), user_id: @user_id } + v: 3 + }], (error) => + throw error if error? + TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => + throw error if error? + done() + + it "should ignore the comment op", -> + expect(@updates[0].pack[0].op).to.deep.equal [{d: "bar", p: 6}] + + it "should insert the correct version numbers into mongo", -> + expect(@updates[0].pack[0].v).to.equal 3 + describe "when the project has versioning enabled", -> before (done) -> @project_id = ObjectId().toString() diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee index 05fe923ab3..b4488c17bc 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee @@ -54,6 +54,22 @@ describe "UpdateCompressor", -> v: 42 }] + it "should ignore comment ops", -> + expect(@UpdateCompressor.convertToSingleOpUpdates [{ + op: [ @op1 = { p: 0, i: "Foo" }, @op2 = { p: 9, c: "baz"}, @op3 = { p: 6, i: "bar"} ] + meta: { ts: @ts1, user_id: @user_id } + v: 42 + }]) + .to.deep.equal [{ + op: @op1, + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + v: 42 + }, { + op: @op3, + meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + v: 42 + }] + describe "concatUpdatesWithSameVersion", -> it "should concat updates with the same version", -> expect(@UpdateCompressor.concatUpdatesWithSameVersion [{ From 1d356f93ec4f1582b1a7537fbff83bd35587f335 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 26 Jan 2017 13:07:54 +0000 Subject: [PATCH 336/549] added random up to 30 mins delay to the ttl of mongo objects --- .../app/coffee/PackManager.coffee | 8 ++++- .../PackManager/PackManagerTests.coffee | 31 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index f9707931cf..15e7e8902a 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -526,7 +526,13 @@ module.exports = PackManager = setTTLOnArchivedPack: (project_id, doc_id, pack_id, callback) -> db.docHistory.findAndModify { query: {_id: pack_id} - update: {$set: {expiresAt: new Date(Date.now() + 1*DAYS)}} + update: {$set: {expiresAt: PackManager._getOneDayInFutureWithRandomDelay()}} }, (err) -> logger.log {project_id, doc_id, pack_id}, "set expiry on pack" callback() + + + _getOneDayInFutureWithRandomDelay: -> + thirtyMins = 1000 * 60 * 30 + randomThirtyMinMax = Math.round(Math.random() * thirtyMins) + return new Date(Date.now() + randomThirtyMinMax + 1*DAYS) diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index bd2244517f..c792381c6a 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -25,7 +25,6 @@ describe "PackManager", -> @project_id = ObjectId().toString() @PackManager.MAX_COUNT = 512 - afterEach -> tk.reset() @@ -334,3 +333,33 @@ describe "PackManager", -> @callback.called.should.equal true it "should return with no error", -> @callback.calledWith(undefined).should.equal true + + describe "setTTLOnArchivedPack", -> + beforeEach -> + @pack_id = "somepackid" + @onedayinms = 86400000 + @db.docHistory = + findAndModify : sinon.stub().callsArgWith(1) + + it "should set expires to 1 day", (done)-> + @PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) + @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => + args = @db.docHistory.findAndModify.args[0][0] + args.query._id.should.equal @pack_id + args.update['$set'].expiresAt.should.equal @onedayinms + done() + + + describe "_getOneDayInFutureWithRandomDelay", -> + beforeEach -> + @onedayinms = 86400000 + @thirtyMins = 1000 * 60 * 30 + + it "should give 1 day + 30 mins random time", (done)-> + loops = 10000 + while --loops > 0 + randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) + randomDelay.should.be.above(0) + randomDelay.should.be.below(@thirtyMins + 1) + done() + From ad5af5f4ddf3d1bbdc159896128267b7ec041fdc Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 26 Jan 2017 15:14:20 +0000 Subject: [PATCH 337/549] ceil not round --- services/track-changes/app/coffee/PackManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 15e7e8902a..98d4f6f274 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -534,5 +534,5 @@ module.exports = PackManager = _getOneDayInFutureWithRandomDelay: -> thirtyMins = 1000 * 60 * 30 - randomThirtyMinMax = Math.round(Math.random() * thirtyMins) + randomThirtyMinMax = Math.ceil(Math.random() * thirtyMins) return new Date(Date.now() + randomThirtyMinMax + 1*DAYS) From ac5d59211d3c1139619586f81e8a62395a2ad710 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 31 Jan 2017 15:07:58 +0000 Subject: [PATCH 338/549] revert random TTL in favour of delay in archiving there could be some issues with newer packs expiring before older ones --- .../app/coffee/PackManager.coffee | 10 ++-- .../PackManager/PackManagerTests.coffee | 48 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index 98d4f6f274..d60500ce56 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -526,13 +526,13 @@ module.exports = PackManager = setTTLOnArchivedPack: (project_id, doc_id, pack_id, callback) -> db.docHistory.findAndModify { query: {_id: pack_id} - update: {$set: {expiresAt: PackManager._getOneDayInFutureWithRandomDelay()}} + update: {$set: {expiresAt: new Date(Date.now() + 1*DAYS)}} }, (err) -> logger.log {project_id, doc_id, pack_id}, "set expiry on pack" callback() - _getOneDayInFutureWithRandomDelay: -> - thirtyMins = 1000 * 60 * 30 - randomThirtyMinMax = Math.ceil(Math.random() * thirtyMins) - return new Date(Date.now() + randomThirtyMinMax + 1*DAYS) +# _getOneDayInFutureWithRandomDelay: -> +# thirtyMins = 1000 * 60 * 30 +# randomThirtyMinMax = Math.ceil(Math.random() * thirtyMins) +# return new Date(Date.now() + randomThirtyMinMax + 1*DAYS) diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index c792381c6a..d1d7d898d6 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -334,32 +334,32 @@ describe "PackManager", -> it "should return with no error", -> @callback.calledWith(undefined).should.equal true - describe "setTTLOnArchivedPack", -> - beforeEach -> - @pack_id = "somepackid" - @onedayinms = 86400000 - @db.docHistory = - findAndModify : sinon.stub().callsArgWith(1) + # describe "setTTLOnArchivedPack", -> + # beforeEach -> + # @pack_id = "somepackid" + # @onedayinms = 86400000 + # @db.docHistory = + # findAndModify : sinon.stub().callsArgWith(1) - it "should set expires to 1 day", (done)-> - @PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) - @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => - args = @db.docHistory.findAndModify.args[0][0] - args.query._id.should.equal @pack_id - args.update['$set'].expiresAt.should.equal @onedayinms - done() + # it "should set expires to 1 day", (done)-> + # #@PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) + # @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => + # args = @db.docHistory.findAndModify.args[0][0] + # args.query._id.should.equal @pack_id + # args.update['$set'].expiresAt.should.equal @onedayinms + # done() - describe "_getOneDayInFutureWithRandomDelay", -> - beforeEach -> - @onedayinms = 86400000 - @thirtyMins = 1000 * 60 * 30 + # describe "_getOneDayInFutureWithRandomDelay", -> + # beforeEach -> + # @onedayinms = 86400000 + # @thirtyMins = 1000 * 60 * 30 - it "should give 1 day + 30 mins random time", (done)-> - loops = 10000 - while --loops > 0 - randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) - randomDelay.should.be.above(0) - randomDelay.should.be.below(@thirtyMins + 1) - done() + # it "should give 1 day + 30 mins random time", (done)-> + # loops = 10000 + # while --loops > 0 + # randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) + # randomDelay.should.be.above(0) + # randomDelay.should.be.below(@thirtyMins + 1) + # done() From c303a1a386bb9073c71193ac72be074af9dfede4 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 16 Mar 2017 15:17:38 +0000 Subject: [PATCH 339/549] wip: upgrade metrics --- services/track-changes/app.coffee | 2 +- .../track-changes/app/coffee/MongoManager.coffee | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 1e5c4e1635..da55d899e3 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -25,7 +25,7 @@ TrackChangesLogger.addSerializers { Path = require "path" Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") -Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) +# Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) Metrics.memory.monitor(logger) child_process = require "child_process" diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index 4376e3adb1..ff4865a11e 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -2,6 +2,7 @@ PackManager = require "./PackManager" async = require "async" _ = require "underscore" +metrics = require 'metrics-sharelatex' module.exports = MongoManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> @@ -92,3 +93,17 @@ module.exports = MongoManager = db.docHistory.ensureIndex { last_checked: 1 }, { background: true } # For finding archived packs db.docHistoryIndex.ensureIndex { project_id: 1 }, { background: true } + + +metrics.timeAsyncMethod( + MongoManager, 'getLastCompressedUpdate', + 'MongoManger.getLastCompressedUpdate' +) +metrics.timeAsyncMethod( + MongoManager, 'getProjectMetaData', + 'MongoManger.getProjectMetaData' +) +metrics.timeAsyncMethod( + MongoManager, 'setProjectMetaData', + 'MongoManger.setProjectMetaData' +) From fa7ee739bb9ed74b7723c3c4d5358b4bdd7620d6 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 17 Mar 2017 14:58:35 +0000 Subject: [PATCH 340/549] Update to new metrics api --- .../app/coffee/MongoManager.coffee | 19 +++++++------------ .../MongoManager/MongoManagerTests.coffee | 1 + 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.coffee index e11e66990e..201c2a95c6 100644 --- a/services/track-changes/app/coffee/MongoManager.coffee +++ b/services/track-changes/app/coffee/MongoManager.coffee @@ -3,6 +3,7 @@ PackManager = require "./PackManager" async = require "async" _ = require "underscore" metrics = require 'metrics-sharelatex' +logger = require 'logger-sharelatex' module.exports = MongoManager = getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> @@ -95,15 +96,9 @@ module.exports = MongoManager = db.docHistoryIndex.ensureIndex { project_id: 1 }, { background: true } -metrics.timeAsyncMethod( - MongoManager, 'getLastCompressedUpdate', - 'MongoManger.getLastCompressedUpdate' -) -metrics.timeAsyncMethod( - MongoManager, 'getProjectMetaData', - 'MongoManger.getProjectMetaData' -) -metrics.timeAsyncMethod( - MongoManager, 'setProjectMetaData', - 'MongoManger.setProjectMetaData' -) +[ + 'getLastCompressedUpdate', + 'getProjectMetaData', + 'setProjectMetaData' +].map (method) -> + metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger) diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 4088c46c0e..29ed86e92c 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -14,6 +14,7 @@ describe "MongoManager", -> @MongoManager = SandboxedModule.require modulePath, requires: "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "./PackManager" : @PackManager = {} + 'metrics-sharelatex': {timeAsyncMethod: ()->} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From 118511ae4a3b9aeb5c22390a685ef53c333dff28 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 20 Mar 2017 09:39:51 +0000 Subject: [PATCH 341/549] Remove commented-out code --- services/track-changes/app.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index da55d899e3..cb9db8e9b6 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -25,7 +25,6 @@ TrackChangesLogger.addSerializers { Path = require "path" Metrics = require "metrics-sharelatex" Metrics.initialize("track-changes") -# Metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) Metrics.memory.monitor(logger) child_process = require "child_process" From 7fbc08d68af91543b23933c544007951cbe70640 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 21 Mar 2017 09:27:47 +0000 Subject: [PATCH 342/549] Add logger stub to unit test --- .../test/unit/coffee/MongoManager/MongoManagerTests.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee index 29ed86e92c..85f4357a58 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee @@ -15,6 +15,7 @@ describe "MongoManager", -> "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "./PackManager" : @PackManager = {} 'metrics-sharelatex': {timeAsyncMethod: ()->} + 'logger-sharelatex': {log: ()->} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From d60445adfc63f8ae2243089fa37a6401d4e96f8e Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 21 Mar 2017 11:32:53 +0000 Subject: [PATCH 343/549] Pass undo flag to doc updater when restoring from history --- .../track-changes/app/coffee/DocumentUpdaterManager.coffee | 1 + .../test/acceptance/coffee/RestoringVersions.coffee | 2 +- .../test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee | 4 ++-- .../DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee index 625622206e..a9fc4981c5 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.coffee @@ -30,6 +30,7 @@ module.exports = DocumentUpdaterManager = lines: content.split("\n") source: "restore" user_id: user_id + undoing: true }, (error, res, body)-> if error? return callback(error) diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index 5dbe094cd8..b09328d2ea 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -65,5 +65,5 @@ describe "Restoring a version", -> it "should set the doc in the doc updater", -> MockDocUpdaterApi.setDoc - .calledWith(@project_id, @doc_id, @restored_lines, @user_id) + .calledWith(@project_id, @doc_id, @restored_lines, @user_id, true) .should.equal true diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee index 97b4d93772..5ef3506b9c 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -7,7 +7,7 @@ module.exports = MockDocUpdaterApi = getDoc: (project_id, doc_id, callback = (error) ->) -> callback null, @docs[doc_id] - setDoc: (project_id, doc_id, lines, user_id, callback = (error) ->) -> + setDoc: (project_id, doc_id, lines, user_id, undoing, callback = (error) ->) -> @docs[doc_id] ||= {} @docs[doc_id].lines = lines callback() @@ -23,7 +23,7 @@ module.exports = MockDocUpdaterApi = res.send JSON.stringify doc app.post "/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => - @setDoc req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, (errr, doc) -> + @setDoc req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, req.body.undoing, (errr, doc) -> if error? res.send 500 else diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee index 8c460f4757..2329cf79f5 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee @@ -70,6 +70,7 @@ describe "DocumentUpdaterManager", -> lines: @content.split("\n") source: "restore" user_id: @user_id + undoing: true }).should.equal true it "should call the callback", -> From d520c78ae325110a1ee83d11fd6f22d73171a662 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 21 Mar 2017 13:45:15 +0000 Subject: [PATCH 344/549] Update metrics version to 1.7.0 --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index a376684d45..9f18cbcc67 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -16,7 +16,7 @@ "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.0", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", "redis": "~0.10.1", From 679582093307483d985d4ba1c7abb00f7e54fd54 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 21 Mar 2017 16:49:23 +0000 Subject: [PATCH 345/549] move lock inside web http calls --- .../app/coffee/UpdatesManager.coffee | 96 ++++++++++++------- .../UpdatesManager/UpdatesManagerTests.coffee | 12 ++- 2 files changed, 70 insertions(+), 38 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index ad6a583979..0060126e71 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -70,51 +70,79 @@ module.exports = UpdatesManager = REDIS_READ_BATCH_SIZE: 100 processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> + UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> + return callback(error) if error? + UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> + return callback(error) if error? + UpdatesManager._processUncompressedUpdates project_id, doc_id, temporary, callback + + # Check whether the updates are temporary (per-project property) + _prepareProjectForUpdates: (project_id, callback = (error, temporary) ->) -> UpdateTrimmer.shouldTrimUpdates project_id, (error, temporary) -> return callback(error) if error? - MongoManager.backportProjectId project_id, doc_id, (error) -> + callback(null, temporary) + + # Check for project id on document history (per-document property) + _prepareDocForUpdates: (project_id, doc_id, callback = (error) ->) -> + MongoManager.backportProjectId project_id, doc_id, (error) -> + return callback(error) if error? + callback(null) + + # Apply updates for specific project/doc after preparing at project and doc level + _processUncompressedUpdates: (project_id, doc_id, temporary, callback = (error) ->) -> + # get the updates as strings from redis (so we can delete them after they are applied) + RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> + return callback(error) if error? + length = docUpdates.length + # parse the redis strings into ShareJs updates + RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> return callback(error) if error? - # get the updates as strings from redis (so we can delete them after they are applied) - RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> + logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? - length = docUpdates.length - # parse the redis strings into ShareJs updates - RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + # delete the applied updates from redis + RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> - return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - # delete the applied updates from redis - RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> - return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + if length == UpdatesManager.REDIS_READ_BATCH_SIZE + # There might be more updates + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" + setTimeout () -> + UpdatesManager._processUncompressedUpdates project_id, doc_id, temporary, callback + , 0 + else + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" + callback() + # Process updates for a doc when we flush it individually processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> - LockManager.runWithLock( - "HistoryLock:#{doc_id}", - (releaseLock) -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, releaseLock - callback - ) + UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> + return callback(error) if error? + UpdatesManager._processUncompressedUpdatesForDoc project_id, doc_id, temporary, callback + + # Process updates for a doc when the whole project is flushed (internal method) + _processUncompressedUpdatesForDoc: (project_id, doc_id, temporary, callback = (error) ->) -> + UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> + return callback(error) if error? + LockManager.runWithLock( + "HistoryLock:#{doc_id}", + (releaseLock) -> + UpdatesManager._processUncompressedUpdates project_id, doc_id, temporary, releaseLock + callback + ) + + # Process all updates for a project, only check project-level information once processUncompressedUpdatesForProject: (project_id, callback = (error) ->) -> RedisManager.getDocIdsWithHistoryOps project_id, (error, doc_ids) -> return callback(error) if error? - jobs = [] - for doc_id in doc_ids - do (doc_id) -> - jobs.push (callback) -> - UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, callback - async.parallelLimit jobs, 5, callback + UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> + jobs = [] + for doc_id in doc_ids + do (doc_id) -> + jobs.push (cb) -> + UpdatesManager._processUncompressedUpdatesForDoc project_id, doc_id, temporary, cb + async.parallelLimit jobs, 5, callback getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 161a5deb55..3746cec999 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -251,7 +251,9 @@ describe "UpdatesManager", -> describe "processCompressedUpdatesWithLock", -> beforeEach -> - @UpdatesManager.processUncompressedUpdates = sinon.stub().callsArg(2) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") + @MongoManager.backportProjectId = sinon.stub().callsArg(2) + @UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3) @LockManager.runWithLock = sinon.stub().callsArg(2) @UpdatesManager.processUncompressedUpdatesWithLock @project_id, @doc_id, @callback @@ -313,7 +315,9 @@ describe "UpdatesManager", -> describe "processUncompressedUpdatesForProject", -> beforeEach (done) -> @doc_ids = ["mock-id-1", "mock-id-2"] - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") + @MongoManager.backportProjectId = sinon.stub().callsArg(2) + @UpdatesManager._processUncompressedUpdatesForDoc = sinon.stub().callsArg(3) @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => @callback() @@ -326,8 +330,8 @@ describe "UpdatesManager", -> it "should process the doc ops for the each doc_id", -> for doc_id in @doc_ids - @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@project_id, doc_id) + @UpdatesManager._processUncompressedUpdatesForDoc + .calledWith(@project_id, doc_id, @temporary) .should.equal true it "should call the callback", -> From 04b82a49ef41c5d2777105a17e5b5636e200575a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 22 Mar 2017 15:38:12 +0000 Subject: [PATCH 346/549] add /profile endpoint --- services/track-changes/app.coffee | 9 +++++++++ services/track-changes/package.json | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 1e5c4e1635..d022406497 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -75,6 +75,15 @@ app.get "/check_lock", HttpController.checkLock app.get "/health_check", HttpController.healthCheck +profiler = require "v8-profiler" +app.get "/profile", (req, res) -> + time = parseInt(req.query.time || "1000") + profiler.startProfiling("test") + setTimeout () -> + profile = profiler.stopProfiling("test") + res.json(profile) + , time + app.use (error, req, res, next) -> logger.error err: error, req: req, "an internal error occured" res.send 500 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index a376684d45..e385de49e6 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -24,7 +24,8 @@ "mongo-uri": "^0.1.2", "s3-streams": "^0.3.0", "JSONStream": "^1.0.4", - "heap": "^0.2.6" + "heap": "^0.2.6", + "v8-profiler": "^5.6.5" }, "devDependencies": { "chai": "~1.9.0", From 0dd668416dccebe3170eea94092bc0b50e14103a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 22 Mar 2017 16:02:50 +0000 Subject: [PATCH 347/549] increase request timeout now it is outside lock --- services/track-changes/app/coffee/WebApiManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 50409ca43c..0067c87d03 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -3,7 +3,7 @@ logger = require "logger-sharelatex" Settings = require "settings-sharelatex" # Don't let HTTP calls hang for a long time -MAX_HTTP_REQUEST_LENGTH = 15000 # 15 seconds +MAX_HTTP_REQUEST_LENGTH = 30000 # 30 seconds # DEPRECATED! This method of getting user details via track-changes is deprecated # in the way we lay out our services. From b1c0ebbaaef9dadd757e51a6061a897631693f85 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 22 Mar 2017 16:16:04 +0000 Subject: [PATCH 348/549] add withLock to processUncompressedUpdatesForDoc --- services/track-changes/app/coffee/UpdatesManager.coffee | 6 +++--- .../unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 0060126e71..6b581bcd53 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -118,11 +118,11 @@ module.exports = UpdatesManager = processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> return callback(error) if error? - UpdatesManager._processUncompressedUpdatesForDoc project_id, doc_id, temporary, callback + UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, callback # Process updates for a doc when the whole project is flushed (internal method) - _processUncompressedUpdatesForDoc: (project_id, doc_id, temporary, callback = (error) ->) -> + _processUncompressedUpdatesForDocWithLock: (project_id, doc_id, temporary, callback = (error) ->) -> UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> return callback(error) if error? LockManager.runWithLock( @@ -141,7 +141,7 @@ module.exports = UpdatesManager = for doc_id in doc_ids do (doc_id) -> jobs.push (cb) -> - UpdatesManager._processUncompressedUpdatesForDoc project_id, doc_id, temporary, cb + UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb async.parallelLimit jobs, 5, callback getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 3746cec999..2c3e06832f 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -317,7 +317,7 @@ describe "UpdatesManager", -> @doc_ids = ["mock-id-1", "mock-id-2"] @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") @MongoManager.backportProjectId = sinon.stub().callsArg(2) - @UpdatesManager._processUncompressedUpdatesForDoc = sinon.stub().callsArg(3) + @UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon.stub().callsArg(3) @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => @callback() @@ -330,7 +330,7 @@ describe "UpdatesManager", -> it "should process the doc ops for the each doc_id", -> for doc_id in @doc_ids - @UpdatesManager._processUncompressedUpdatesForDoc + @UpdatesManager._processUncompressedUpdatesForDocWithLock .calledWith(@project_id, doc_id, @temporary) .should.equal true @@ -802,4 +802,4 @@ describe "UpdatesManager", -> user_ids: [@user_2.id] start_ts: @now end_ts: @now + 10 - }] \ No newline at end of file + }] From bc7815f7fce2fb1601f7e8892c87ca5420f7e404 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 22 Mar 2017 16:59:52 +0000 Subject: [PATCH 349/549] remove old processUncompressedUpdates method replace with new per doc method --- .../app/coffee/UpdatesManager.coffee | 15 ++++-------- .../UpdatesManager/UpdatesManagerTests.coffee | 24 +++++++++---------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 6b581bcd53..fa4016f632 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -68,14 +68,6 @@ module.exports = UpdatesManager = logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? callback() - REDIS_READ_BATCH_SIZE: 100 - processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> - UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> - return callback(error) if error? - UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> - return callback(error) if error? - UpdatesManager._processUncompressedUpdates project_id, doc_id, temporary, callback - # Check whether the updates are temporary (per-project property) _prepareProjectForUpdates: (project_id, callback = (error, temporary) ->) -> UpdateTrimmer.shouldTrimUpdates project_id, (error, temporary) -> @@ -89,7 +81,8 @@ module.exports = UpdatesManager = callback(null) # Apply updates for specific project/doc after preparing at project and doc level - _processUncompressedUpdates: (project_id, doc_id, temporary, callback = (error) ->) -> + REDIS_READ_BATCH_SIZE: 100 + processUncompressedUpdates: (project_id, doc_id, temporary, callback = (error) ->) -> # get the updates as strings from redis (so we can delete them after they are applied) RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> return callback(error) if error? @@ -108,7 +101,7 @@ module.exports = UpdatesManager = # There might be more updates logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" setTimeout () -> - UpdatesManager._processUncompressedUpdates project_id, doc_id, temporary, callback + UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, callback , 0 else logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" @@ -128,7 +121,7 @@ module.exports = UpdatesManager = LockManager.runWithLock( "HistoryLock:#{doc_id}", (releaseLock) -> - UpdatesManager._processUncompressedUpdates project_id, doc_id, temporary, releaseLock + UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, releaseLock callback ) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 2c3e06832f..4a7d463b37 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -182,17 +182,7 @@ describe "UpdatesManager", -> @updates = ["mock-update"] @RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, @updates) @RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, @updates) - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback - - it "should check if the updates are temporary", -> - @UpdateTrimmer.shouldTrimUpdates - .calledWith(@project_id) - .should.equal true - - it "should backport the project id", -> - @MongoManager.backportProjectId - .calledWith(@project_id, @doc_id) - .should.equal true + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, @callback it "should get the oldest updates", -> @RedisManager.getOldestDocUpdates @@ -225,7 +215,7 @@ describe "UpdatesManager", -> @RedisManager.expandDocUpdates = (jsonUpdates, callback) => callback null, jsonUpdates sinon.spy @RedisManager, "expandDocUpdates" - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, (args...) => @callback(args...) done() @@ -257,6 +247,16 @@ describe "UpdatesManager", -> @LockManager.runWithLock = sinon.stub().callsArg(2) @UpdatesManager.processUncompressedUpdatesWithLock @project_id, @doc_id, @callback + it "should check if the updates are temporary", -> + @UpdateTrimmer.shouldTrimUpdates + .calledWith(@project_id) + .should.equal true + + it "should backport the project id", -> + @MongoManager.backportProjectId + .calledWith(@project_id, @doc_id) + .should.equal true + it "should run processUncompressedUpdates with the lock", -> @LockManager.runWithLock .calledWith( From a4cca1f9483ec722192df423d1c1bcc2af1c1d34 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 23 Mar 2017 14:21:39 +0000 Subject: [PATCH 350/549] update shrinkwrap file for new npm package --- services/track-changes/npm-shrinkwrap.json | 1204 ++++++++++++++++---- 1 file changed, 978 insertions(+), 226 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 36b6e13937..e7be0b1799 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -4,49 +4,49 @@ "dependencies": { "JSONStream": { "version": "1.1.1", - "from": "JSONStream@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", "dependencies": { "jsonparse": { "version": "1.2.0", - "from": "jsonparse@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz" }, "through": { "version": "2.3.8", - "from": "through@>=2.2.7 <3.0.0", + "from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "async": { "version": "0.2.10", - "from": "async@>=0.2.10 <0.3.0", + "from": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" }, "aws-sdk": { "version": "2.2.43", - "from": "aws-sdk@>=2.1.34 <3.0.0", + "from": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", "dependencies": { "sax": { "version": "1.1.5", - "from": "sax@1.1.5", + "from": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" }, "xml2js": { "version": "0.4.15", - "from": "xml2js@0.4.15", + "from": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" }, "xmlbuilder": { "version": "2.6.2", - "from": "xmlbuilder@2.6.2", + "from": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", "dependencies": { "lodash": { "version": "3.5.0", - "from": "lodash@>=3.5.0 <3.6.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" } } @@ -55,42 +55,42 @@ }, "bson": { "version": "0.4.22", - "from": "bson@>=0.4.20 <0.5.0", + "from": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz", "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" }, "byline": { "version": "4.2.1", - "from": "byline@>=4.2.1 <5.0.0", + "from": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz", "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz" }, "cli": { "version": "0.6.6", - "from": "cli@>=0.6.6 <0.7.0", + "from": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@>=3.2.1 <3.3.0", + "from": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } @@ -99,115 +99,115 @@ }, "exit": { "version": "0.1.2", - "from": "exit@0.1.2", + "from": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" } } }, "express": { "version": "3.3.5", - "from": "express@3.3.5", + "from": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "dependencies": { "connect": { "version": "2.8.5", - "from": "connect@2.8.5", + "from": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "dependencies": { "qs": { "version": "0.6.5", - "from": "qs@0.6.5", + "from": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" }, "formidable": { "version": "1.0.14", - "from": "formidable@1.0.14", + "from": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" }, "bytes": { "version": "0.2.0", - "from": "bytes@0.2.0", + "from": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" }, "pause": { "version": "0.0.1", - "from": "pause@0.0.1", + "from": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" }, "uid2": { "version": "0.0.2", - "from": "uid2@0.0.2", + "from": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" } } }, "commander": { "version": "1.2.0", - "from": "commander@1.2.0", + "from": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "dependencies": { "keypress": { "version": "0.1.0", - "from": "keypress@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" } } }, "range-parser": { "version": "0.0.4", - "from": "range-parser@0.0.4", + "from": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0", + "from": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" }, "buffer-crc32": { "version": "0.2.1", - "from": "buffer-crc32@0.2.1", + "from": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" }, "fresh": { "version": "0.2.0", - "from": "fresh@0.2.0", + "from": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" }, "methods": { "version": "0.0.1", - "from": "methods@0.0.1", + "from": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" }, "send": { "version": "0.1.4", - "from": "send@0.1.4", + "from": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "dependencies": { "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", + "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "cookie-signature": { "version": "1.0.1", - "from": "cookie-signature@1.0.1", + "from": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" }, "debug": { "version": "2.2.0", - "from": "debug@*", + "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "ms@0.7.1", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } @@ -216,103 +216,103 @@ }, "heap": { "version": "0.2.6", - "from": "heap@>=0.2.6 <0.3.0", + "from": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" }, "line-reader": { "version": "0.2.4", - "from": "line-reader@>=0.2.4 <0.3.0", + "from": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, "logger-sharelatex": { - "version": "1.3.0", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#fd5e037e64178fb3ef5138d39a048cadbacd2ade", + "version": "1.5.0", + "from": "git+https://github.com/sharelatex/logger-sharelatex.git#35ac891175f58f87f3950b5808a83ab01579a097", + "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#35ac891175f58f87f3950b5808a83ab01579a097", "dependencies": { "bunyan": { "version": "1.5.1", - "from": "bunyan@1.5.1", + "from": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", "dependencies": { "dtrace-provider": { "version": "0.6.0", - "from": "dtrace-provider@>=0.6.0 <0.7.0", + "from": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "dependencies": { "nan": { - "version": "2.2.0", - "from": "nan@>=2.0.8 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.2.0.tgz" + "version": "2.5.1", + "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" } } }, "mv": { "version": "2.1.1", - "from": "mv@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.1 <0.6.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "ncp": { "version": "2.0.0", - "from": "ncp@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" }, "rimraf": { "version": "2.4.5", - "from": "rimraf@>=2.4.0 <2.5.0", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "dependencies": { "glob": { "version": "6.0.4", - "from": "glob@>=6.0.1 <7.0.0", + "from": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "dependencies": { "inflight": { - "version": "1.0.4", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "version": "1.0.6", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { - "version": "3.0.0", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.3", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { - "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -320,21 +320,21 @@ } }, "once": { - "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { - "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + "version": "1.0.1", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -343,40 +343,40 @@ } }, "safe-json-stringify": { - "version": "1.0.3", - "from": "safe-json-stringify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz" + "version": "1.0.4", + "from": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz" } } }, "coffee-script": { "version": "1.4.0", - "from": "coffee-script@1.4.0", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz" }, "raven": { "version": "0.8.1", - "from": "raven@>=0.8.0 <0.9.0", + "from": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", "dependencies": { "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0", + "from": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" }, "lsmod": { "version": "0.0.3", - "from": "lsmod@>=0.0.3 <0.1.0", + "from": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz", "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz" }, "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.1 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + "version": "1.4.8", + "from": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" }, "stack-trace": { "version": "0.0.7", - "from": "stack-trace@0.0.7", + "from": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz" } } @@ -384,72 +384,72 @@ } }, "metrics-sharelatex": { - "version": "1.3.0", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#a2d156e97b7ab51fca5f36c0838a9a808416dfe8", + "version": "1.5.0", + "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", + "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", "dependencies": { "lynx": { "version": "0.1.1", - "from": "lynx@>=0.1.1 <0.2.0", + "from": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", "dependencies": { "mersenne": { "version": "0.0.3", - "from": "mersenne@>=0.0.3 <0.1.0", + "from": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz", "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" }, "statsd-parser": { "version": "0.0.4", - "from": "statsd-parser@>=0.0.4 <0.1.0", + "from": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" } } }, "coffee-script": { "version": "1.6.0", - "from": "coffee-script@1.6.0", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" }, "underscore": { "version": "1.6.0", - "from": "underscore@>=1.6.0 <1.7.0", + "from": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" } } }, "mongo-uri": { "version": "0.1.2", - "from": "mongo-uri@>=0.1.2 <0.2.0", + "from": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz", "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" }, "mongojs": { "version": "1.4.1", - "from": "mongojs@>=1.4.1 <2.0.0", + "from": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", "dependencies": { "each-series": { "version": "1.0.0", - "from": "each-series@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" }, "mongodb-core": { "version": "1.3.10", - "from": "mongodb-core@>=1.2.8 <2.0.0", + "from": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", "dependencies": { "require_optional": { "version": "1.0.0", - "from": "require_optional@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", "dependencies": { "semver": { "version": "5.1.0", - "from": "semver@>=5.1.0 <6.0.0", + "from": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" }, "resolve-from": { "version": "2.0.0", - "from": "resolve-from@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" } } @@ -458,115 +458,115 @@ }, "once": { "version": "1.3.3", - "from": "once@>=1.3.2 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } }, "parse-mongo-url": { "version": "1.1.1", - "from": "parse-mongo-url@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" }, "pump": { "version": "1.0.1", - "from": "pump@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", "dependencies": { "end-of-stream": { "version": "1.1.0", - "from": "end-of-stream@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" } } }, "readable-stream": { "version": "2.0.6", - "from": "readable-stream@>=2.0.2 <3.0.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "process-nextick-args": { "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } }, "thunky": { "version": "0.1.0", - "from": "thunky@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" }, "to-mongodb-core": { "version": "2.0.0", - "from": "to-mongodb-core@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", + "from": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" } } }, "redis": { "version": "0.10.3", - "from": "redis@>=0.10.1 <0.11.0", + "from": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" }, "redis-sharelatex": { "version": "0.0.9", - "from": "redis-sharelatex@>=0.0.9 <0.1.0", + "from": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", "dependencies": { "chai": { "version": "1.9.1", - "from": "chai@1.9.1", + "from": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", "dependencies": { "assertion-error": { "version": "1.0.0", - "from": "assertion-error@1.0.0", + "from": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" }, "deep-eql": { "version": "0.1.3", - "from": "deep-eql@0.1.3", + "from": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "dependencies": { "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1", + "from": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" } } @@ -575,168 +575,168 @@ }, "coffee-script": { "version": "1.8.0", - "from": "coffee-script@1.8.0", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@0.11.1", + "from": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } }, "grunt-mocha-test": { "version": "0.12.0", - "from": "grunt-mocha-test@0.12.0", + "from": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", "dependencies": { "hooker": { "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", + "from": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "fs-extra": { "version": "0.11.1", - "from": "fs-extra@>=0.11.1 <0.12.0", + "from": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", "dependencies": { "ncp": { "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", + "from": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz", "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "jsonfile": { "version": "2.2.3", - "from": "jsonfile@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" }, "rimraf": { "version": "2.5.2", - "from": "rimraf@>=2.2.8 <3.0.0", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", "dependencies": { "glob": { "version": "7.0.3", - "from": "glob@>=7.0.0 <8.0.0", + "from": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", "dependencies": { "inflight": { "version": "1.0.4", - "from": "inflight@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "3.0.0", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", "dependencies": { "brace-expansion": { "version": "1.1.3", - "from": "brace-expansion@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", "dependencies": { "balanced-match": { "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -745,19 +745,19 @@ }, "once": { "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } @@ -770,88 +770,88 @@ }, "mocha": { "version": "1.21.4", - "from": "mocha@1.21.4", + "from": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0", + "from": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "growl": { "version": "1.8.1", - "from": "growl@>=1.8.0 <1.9.0", + "from": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" }, "jade": { "version": "0.26.3", - "from": "jade@0.26.3", + "from": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1", + "from": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7", + "from": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { "version": "2.2.0", - "from": "debug@*", + "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "ms@0.7.1", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "glob": { "version": "3.2.3", - "from": "glob@3.2.3", + "from": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -860,68 +860,68 @@ }, "redis": { "version": "0.12.1", - "from": "redis@0.12.1", + "from": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" }, "redis-sentinel": { "version": "0.1.1", - "from": "redis-sentinel@0.1.1", + "from": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "dependencies": { "redis": { "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", + "from": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" }, "q": { "version": "0.9.2", - "from": "q@0.9.2", + "from": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" } } }, "sandboxed-module": { "version": "1.0.1", - "from": "sandboxed-module@1.0.1", + "from": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", - "from": "require-like@0.1.2", + "from": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", + "from": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } }, "sinon": { "version": "1.10.3", - "from": "sinon@1.10.3", + "from": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "formatio@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "dependencies": { "samsam": { "version": "1.1.3", - "from": "samsam@>=1.1.0 <1.2.0", + "from": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1.0.0", + "from": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -932,199 +932,951 @@ }, "request": { "version": "2.33.0", - "from": "request@>=2.33.0 <2.34.0", + "from": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "qs@>=0.6.0 <0.7.0", + "from": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.0 <5.1.0", + "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, "node-uuid": { "version": "1.4.7", - "from": "node-uuid@>=1.4.0 <1.5.0", + "from": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", + "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "tough-cookie": { "version": "2.2.2", - "from": "tough-cookie@>=0.12.0", + "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" }, "form-data": { "version": "0.1.4", - "from": "form-data@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "dependencies": { "combined-stream": { "version": "0.0.7", - "from": "combined-stream@>=0.0.4 <0.1.0", + "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", - "from": "delayed-stream@0.0.5", + "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" } } }, "async": { "version": "0.9.2", - "from": "async@>=0.9.0 <0.10.0", + "from": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" } } }, "tunnel-agent": { "version": "0.3.0", - "from": "tunnel-agent@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.1", - "from": "http-signature@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", "dependencies": { "assert-plus": { "version": "0.1.5", - "from": "assert-plus@>=0.1.5 <0.2.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" }, "asn1": { "version": "0.1.11", - "from": "asn1@0.1.11", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" }, "ctype": { "version": "0.5.3", - "from": "ctype@0.5.3", + "from": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" } } }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" }, "hawk": { "version": "1.0.0", - "from": "hawk@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "hoek@>=0.9.0 <0.10.0", + "from": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, "boom": { "version": "0.4.2", - "from": "boom@>=0.4.0 <0.5.0", + "from": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" }, "sntp": { "version": "0.2.4", - "from": "sntp@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" } } }, "aws-sign2": { "version": "0.5.0", - "from": "aws-sign2@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" } } }, "s3-streams": { "version": "0.3.0", - "from": "s3-streams@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "dependencies": { "lodash": { "version": "3.10.1", - "from": "lodash@>=3.9.3 <4.0.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" }, "readable-stream": { "version": "2.0.5", - "from": "readable-stream@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1", + "from": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "process-nextick-args": { "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } }, "bluebird": { "version": "2.10.2", - "from": "bluebird@>=2.9.27 <3.0.0", + "from": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" } } }, "settings-sharelatex": { "version": "1.0.0", - "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "from": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", "dependencies": { "coffee-script": { "version": "1.6.0", - "from": "coffee-script@1.6.0", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" } } }, "underscore": { "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", + "from": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "v8-profiler": { + "version": "5.7.0", + "from": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "dependencies": { + "nan": { + "version": "2.5.1", + "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" + }, + "node-pre-gyp": { + "version": "0.6.34", + "from": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "nopt": { + "version": "4.0.1", + "from": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" + }, + "osenv": { + "version": "0.1.4", + "from": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + } + } + } + }, + "npmlog": { + "version": "4.0.2", + "from": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "dependencies": { + "are-we-there-yet": { + "version": "1.1.2", + "from": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "dependencies": { + "delegates": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + }, + "readable-stream": { + "version": "2.2.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + }, + "gauge": { + "version": "2.7.3", + "from": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "dependencies": { + "aproba": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" + }, + "has-unicode": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + }, + "object-assign": { + "version": "4.1.1", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "signal-exit": { + "version": "3.0.2", + "from": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + }, + "string-width": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + } + }, + "wide-align": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" + } + } + }, + "set-blocking": { + "version": "2.0.0", + "from": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + } + } + }, + "rc": { + "version": "1.1.7", + "from": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "dependencies": { + "deep-extend": { + "version": "0.4.1", + "from": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" + }, + "ini": { + "version": "1.3.4", + "from": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "strip-json-comments": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + } + } + }, + "request": { + "version": "2.81.0", + "from": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + } + }, + "extend": { + "version": "3.0.0", + "from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.2", + "from": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "dependencies": { + "asynckit": { + "version": "0.4.0", + "from": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + } + } + }, + "har-validator": { + "version": "4.2.1", + "from": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "dependencies": { + "ajv": { + "version": "4.11.5", + "from": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "dependencies": { + "co": { + "version": "4.6.0", + "from": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "dependencies": { + "jsonify": { + "version": "0.0.0", + "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "jsprim": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "extsprintf": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "verror": { + "version": "1.3.6", + "from": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + } + } + }, + "sshpk": { + "version": "1.11.0", + "from": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" + }, + "getpass": { + "version": "0.1.6", + "from": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "jodid25519": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "mime-types": { + "version": "2.1.14", + "from": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", + "dependencies": { + "mime-db": { + "version": "1.26.0", + "from": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "safe-buffer": { + "version": "5.0.1", + "from": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" + }, + "stringstream": { + "version": "0.0.5", + "from": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.0.1", + "from": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + } + } + }, + "rimraf": { + "version": "2.6.1", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "dependencies": { + "glob": { + "version": "7.1.1", + "from": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + } + } + } + }, + "semver": { + "version": "5.3.0", + "from": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + }, + "tar": { + "version": "2.2.1", + "from": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.9", + "from": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" + }, + "fstream": { + "version": "1.0.11", + "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } + } + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + }, + "tar-pack": { + "version": "3.4.0", + "from": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "dependencies": { + "debug": { + "version": "2.6.3", + "from": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "dependencies": { + "ms": { + "version": "0.7.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + } + }, + "fstream": { + "version": "1.0.11", + "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "dependencies": { + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "readable-stream": { + "version": "2.2.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "uid-number": { + "version": "0.0.6", + "from": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + } + } + } + } + } + } } } } From 7802bcc911e6ccb7d8d4c9590d79a734ef5956fb Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 23 Mar 2017 16:57:48 +0000 Subject: [PATCH 351/549] Revert "update shrinkwrap file for new npm package" This reverts commit 68283e2650420c1cedc957b039b52766a8d4ce3c. --- services/track-changes/npm-shrinkwrap.json | 1856 ++++++-------------- 1 file changed, 552 insertions(+), 1304 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index e7be0b1799..36b6e13937 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -4,49 +4,49 @@ "dependencies": { "JSONStream": { "version": "1.1.1", - "from": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", + "from": "JSONStream@>=1.0.4 <2.0.0", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", "dependencies": { "jsonparse": { "version": "1.2.0", - "from": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", + "from": "jsonparse@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz" }, "through": { "version": "2.3.8", - "from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "from": "through@>=2.2.7 <3.0.0", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "async": { "version": "0.2.10", - "from": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "from": "async@>=0.2.10 <0.3.0", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" }, "aws-sdk": { "version": "2.2.43", - "from": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", + "from": "aws-sdk@>=2.1.34 <3.0.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", "dependencies": { "sax": { "version": "1.1.5", - "from": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz", + "from": "sax@1.1.5", "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" }, "xml2js": { "version": "0.4.15", - "from": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz", + "from": "xml2js@0.4.15", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" }, "xmlbuilder": { "version": "2.6.2", - "from": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", + "from": "xmlbuilder@2.6.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", "dependencies": { "lodash": { "version": "3.5.0", - "from": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz", + "from": "lodash@>=3.5.0 <3.6.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" } } @@ -55,42 +55,42 @@ }, "bson": { "version": "0.4.22", - "from": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz", + "from": "bson@>=0.4.20 <0.5.0", "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" }, "byline": { "version": "4.2.1", - "from": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz", + "from": "byline@>=4.2.1 <5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz" }, "cli": { "version": "0.6.6", - "from": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "from": "cli@>=0.6.6 <0.7.0", "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "from": "glob@>=3.2.1 <3.3.0", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "from": "minimatch@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "from": "lru-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } @@ -99,115 +99,115 @@ }, "exit": { "version": "0.1.2", - "from": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "from": "exit@0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" } } }, "express": { "version": "3.3.5", - "from": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", + "from": "express@3.3.5", "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "dependencies": { "connect": { "version": "2.8.5", - "from": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", + "from": "connect@2.8.5", "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "dependencies": { "qs": { "version": "0.6.5", - "from": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", + "from": "qs@0.6.5", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" }, "formidable": { "version": "1.0.14", - "from": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", + "from": "formidable@1.0.14", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" }, "bytes": { "version": "0.2.0", - "from": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", + "from": "bytes@0.2.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" }, "pause": { "version": "0.0.1", - "from": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "from": "pause@0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" }, "uid2": { "version": "0.0.2", - "from": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz", + "from": "uid2@0.0.2", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" } } }, "commander": { "version": "1.2.0", - "from": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", + "from": "commander@1.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "dependencies": { "keypress": { "version": "0.1.0", - "from": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", + "from": "keypress@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" } } }, "range-parser": { "version": "0.0.4", - "from": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", + "from": "range-parser@0.0.4", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" }, "mkdirp": { "version": "0.3.5", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "from": "mkdirp@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "cookie": { "version": "0.1.0", - "from": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", + "from": "cookie@0.1.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" }, "buffer-crc32": { "version": "0.2.1", - "from": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", + "from": "buffer-crc32@0.2.1", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" }, "fresh": { "version": "0.2.0", - "from": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", + "from": "fresh@0.2.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" }, "methods": { "version": "0.0.1", - "from": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", + "from": "methods@0.0.1", "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" }, "send": { "version": "0.1.4", - "from": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", + "from": "send@0.1.4", "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "dependencies": { "mime": { "version": "1.2.11", - "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "from": "mime@>=1.2.9 <1.3.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "cookie-signature": { "version": "1.0.1", - "from": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz", + "from": "cookie-signature@1.0.1", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" }, "debug": { "version": "2.2.0", - "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "from": "debug@*", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } @@ -216,527 +216,103 @@ }, "heap": { "version": "0.2.6", - "from": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", + "from": "heap@>=0.2.6 <0.3.0", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" }, "line-reader": { "version": "0.2.4", - "from": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz", + "from": "line-reader@>=0.2.4 <0.3.0", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, "logger-sharelatex": { - "version": "1.5.0", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#35ac891175f58f87f3950b5808a83ab01579a097", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#35ac891175f58f87f3950b5808a83ab01579a097", + "version": "1.3.0", + "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", + "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#fd5e037e64178fb3ef5138d39a048cadbacd2ade", "dependencies": { "bunyan": { "version": "1.5.1", - "from": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", + "from": "bunyan@1.5.1", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", "dependencies": { "dtrace-provider": { "version": "0.6.0", - "from": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "from": "dtrace-provider@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "dependencies": { "nan": { - "version": "2.5.1", - "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" + "version": "2.2.0", + "from": "nan@>=2.0.8 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.2.0.tgz" } } }, "mv": { "version": "2.1.1", - "from": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "from": "mv@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@>=0.5.1 <0.6.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "ncp": { "version": "2.0.0", - "from": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "from": "ncp@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" }, "rimraf": { "version": "2.4.5", - "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "from": "rimraf@>=2.4.0 <2.5.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "dependencies": { "glob": { "version": "6.0.4", - "from": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "from": "glob@>=6.0.1 <7.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "dependencies": { - "inflight": { - "version": "1.0.6", - "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - } - } - } - } - } - } - }, - "safe-json-stringify": { - "version": "1.0.4", - "from": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz" - } - } - }, - "coffee-script": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz" - }, - "raven": { - "version": "0.8.1", - "from": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", - "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", - "dependencies": { - "cookie": { - "version": "0.1.0", - "from": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" - }, - "lsmod": { - "version": "0.0.3", - "from": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz" - }, - "node-uuid": { - "version": "1.4.8", - "from": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" - }, - "stack-trace": { - "version": "0.0.7", - "from": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz" - } - } - } - } - }, - "metrics-sharelatex": { - "version": "1.5.0", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", - "dependencies": { - "lynx": { - "version": "0.1.1", - "from": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", - "dependencies": { - "mersenne": { - "version": "0.0.3", - "from": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" - }, - "statsd-parser": { - "version": "0.0.4", - "from": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" - } - } - }, - "coffee-script": { - "version": "1.6.0", - "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" - }, - "underscore": { - "version": "1.6.0", - "from": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" - } - } - }, - "mongo-uri": { - "version": "0.1.2", - "from": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz", - "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" - }, - "mongojs": { - "version": "1.4.1", - "from": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", - "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", - "dependencies": { - "each-series": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" - }, - "mongodb-core": { - "version": "1.3.10", - "from": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", - "dependencies": { - "require_optional": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", - "dependencies": { - "semver": { - "version": "5.1.0", - "from": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" - }, - "resolve-from": { - "version": "2.0.0", - "from": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" - } - } - } - } - }, - "once": { - "version": "1.3.3", - "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" - } - } - }, - "parse-mongo-url": { - "version": "1.1.1", - "from": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", - "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" - }, - "pump": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", - "dependencies": { - "end-of-stream": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" - } - } - }, - "readable-stream": { - "version": "2.0.6", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "process-nextick-args": { - "version": "1.0.6", - "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - }, - "thunky": { - "version": "0.1.0", - "from": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" - }, - "to-mongodb-core": { - "version": "2.0.0", - "from": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", - "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - } - } - }, - "redis": { - "version": "0.10.3", - "from": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" - }, - "redis-sharelatex": { - "version": "0.0.9", - "from": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", - "dependencies": { - "chai": { - "version": "1.9.1", - "from": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", - "dependencies": { - "assertion-error": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" - }, - "deep-eql": { - "version": "0.1.3", - "from": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "dependencies": { - "type-detect": { - "version": "0.1.1", - "from": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" - } - } - } - } - }, - "coffee-script": { - "version": "1.8.0", - "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", - "dependencies": { - "mkdirp": { - "version": "0.3.5", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" - } - } - }, - "grunt-contrib-coffee": { - "version": "0.11.1", - "from": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", - "dependencies": { - "coffee-script": { - "version": "1.7.1", - "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", - "dependencies": { - "mkdirp": { - "version": "0.3.5", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" - } - } - }, - "chalk": { - "version": "0.5.1", - "from": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "dependencies": { - "ansi-styles": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "has-ansi": { - "version": "0.1.0", - "from": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" - } - } - }, - "strip-ansi": { - "version": "0.3.0", - "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" - } - } - }, - "supports-color": { - "version": "0.2.0", - "from": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" - } - } - }, - "lodash": { - "version": "2.4.2", - "from": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - } - } - }, - "grunt-mocha-test": { - "version": "0.12.0", - "from": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", - "dependencies": { - "hooker": { - "version": "0.2.3", - "from": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" - }, - "fs-extra": { - "version": "0.11.1", - "from": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", - "dependencies": { - "ncp": { - "version": "0.6.0", - "from": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - } - } - }, - "jsonfile": { - "version": "2.2.3", - "from": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" - }, - "rimraf": { - "version": "2.5.2", - "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", - "dependencies": { - "glob": { - "version": "7.0.3", - "from": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", "dependencies": { "inflight": { "version": "1.0.4", - "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "from": "inflight@>=1.0.4 <2.0.0", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", + "from": "wrappy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } }, "inherits": { "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "3.0.0", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", "dependencies": { "brace-expansion": { "version": "1.1.3", - "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "from": "brace-expansion@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", "dependencies": { "balanced-match": { "version": "0.3.0", - "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz", + "from": "balanced-match@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" }, "concat-map": { "version": "0.0.1", - "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "from": "concat-map@0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } @@ -745,19 +321,443 @@ }, "once": { "version": "1.3.3", - "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", + "from": "wrappy@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + } + } + } + } + }, + "safe-json-stringify": { + "version": "1.0.3", + "from": "safe-json-stringify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz" + } + } + }, + "coffee-script": { + "version": "1.4.0", + "from": "coffee-script@1.4.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz" + }, + "raven": { + "version": "0.8.1", + "from": "raven@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", + "dependencies": { + "cookie": { + "version": "0.1.0", + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + }, + "lsmod": { + "version": "0.0.3", + "from": "lsmod@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz" + }, + "node-uuid": { + "version": "1.4.7", + "from": "node-uuid@>=1.4.1 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + }, + "stack-trace": { + "version": "0.0.7", + "from": "stack-trace@0.0.7", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz" + } + } + } + } + }, + "metrics-sharelatex": { + "version": "1.3.0", + "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", + "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#a2d156e97b7ab51fca5f36c0838a9a808416dfe8", + "dependencies": { + "lynx": { + "version": "0.1.1", + "from": "lynx@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "dependencies": { + "mersenne": { + "version": "0.0.3", + "from": "mersenne@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" + }, + "statsd-parser": { + "version": "0.0.4", + "from": "statsd-parser@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + } + } + }, + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + }, + "underscore": { + "version": "1.6.0", + "from": "underscore@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + } + } + }, + "mongo-uri": { + "version": "0.1.2", + "from": "mongo-uri@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" + }, + "mongojs": { + "version": "1.4.1", + "from": "mongojs@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", + "dependencies": { + "each-series": { + "version": "1.0.0", + "from": "each-series@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + }, + "mongodb-core": { + "version": "1.3.10", + "from": "mongodb-core@>=1.2.8 <2.0.0", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", + "dependencies": { + "require_optional": { + "version": "1.0.0", + "from": "require_optional@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", + "dependencies": { + "semver": { + "version": "5.1.0", + "from": "semver@>=5.1.0 <6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" + }, + "resolve-from": { + "version": "2.0.0", + "from": "resolve-from@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "once@>=1.3.2 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "parse-mongo-url": { + "version": "1.1.1", + "from": "parse-mongo-url@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + }, + "pump": { + "version": "1.0.1", + "from": "pump@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.6", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "process-nextick-args": { + "version": "1.0.6", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "thunky": { + "version": "0.1.0", + "from": "thunky@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + }, + "to-mongodb-core": { + "version": "2.0.0", + "from": "to-mongodb-core@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + } + }, + "redis": { + "version": "0.10.3", + "from": "redis@>=0.10.1 <0.11.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" + }, + "redis-sharelatex": { + "version": "0.0.9", + "from": "redis-sharelatex@>=0.0.9 <0.1.0", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", + "dependencies": { + "chai": { + "version": "1.9.1", + "from": "chai@1.9.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", + "dependencies": { + "assertion-error": { + "version": "1.0.0", + "from": "assertion-error@1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" + }, + "deep-eql": { + "version": "0.1.3", + "from": "deep-eql@0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "dependencies": { + "type-detect": { + "version": "0.1.1", + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + } + } + } + } + }, + "coffee-script": { + "version": "1.8.0", + "from": "coffee-script@1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + }, + "grunt-contrib-coffee": { + "version": "0.11.1", + "from": "grunt-contrib-coffee@0.11.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "dependencies": { + "coffee-script": { + "version": "1.7.1", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + }, + "chalk": { + "version": "0.5.1", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "dependencies": { + "ansi-styles": { + "version": "1.1.0", + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "has-ansi": { + "version": "0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "strip-ansi": { + "version": "0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "supports-color": { + "version": "0.2.0", + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + } + } + }, + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "grunt-mocha-test": { + "version": "0.12.0", + "from": "grunt-mocha-test@0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "from": "hooker@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "fs-extra": { + "version": "0.11.1", + "from": "fs-extra@>=0.11.1 <0.12.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "dependencies": { + "ncp": { + "version": "0.6.0", + "from": "ncp@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "jsonfile": { + "version": "2.2.3", + "from": "jsonfile@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" + }, + "rimraf": { + "version": "2.5.2", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "dependencies": { + "glob": { + "version": "7.0.3", + "from": "glob@>=7.0.0 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "3.0.0", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.3", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "dependencies": { + "balanced-match": { + "version": "0.3.0", + "from": "balanced-match@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } @@ -770,88 +770,88 @@ }, "mocha": { "version": "1.21.4", - "from": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", + "from": "mocha@1.21.4", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "from": "commander@2.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "growl": { "version": "1.8.1", - "from": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", + "from": "growl@>=1.8.0 <1.9.0", "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" }, "jade": { "version": "0.26.3", - "from": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "from": "jade@0.26.3", "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "from": "commander@0.6.1", "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "from": "mkdirp@0.3.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "diff": { "version": "1.0.7", - "from": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "from": "diff@1.0.7", "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { "version": "2.2.0", - "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "from": "debug@*", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "from": "mkdirp@0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "glob": { "version": "3.2.3", - "from": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "from": "glob@3.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "from": "minimatch@>=0.2.11 <0.3.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "from": "lru-cache@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "from": "graceful-fs@>=2.0.0 <2.1.0", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -860,68 +860,68 @@ }, "redis": { "version": "0.12.1", - "from": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz", + "from": "redis@0.12.1", "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" }, "redis-sentinel": { "version": "0.1.1", - "from": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", + "from": "redis-sentinel@0.1.1", "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "dependencies": { "redis": { "version": "0.11.0", - "from": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", + "from": "redis@>=0.11.0 <0.12.0", "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" }, "q": { "version": "0.9.2", - "from": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", + "from": "q@0.9.2", "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" } } }, "sandboxed-module": { "version": "1.0.1", - "from": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", + "from": "sandboxed-module@1.0.1", "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", - "from": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "from": "require-like@0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "from": "stack-trace@0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } }, "sinon": { "version": "1.10.3", - "from": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", + "from": "sinon@1.10.3", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", + "from": "formatio@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "dependencies": { "samsam": { "version": "1.1.3", - "from": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz", + "from": "samsam@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" } } }, "util": { "version": "0.10.3", - "from": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "from": "util@>=0.10.3 <1.0.0", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -932,951 +932,199 @@ }, "request": { "version": "2.33.0", - "from": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", + "from": "request@>=2.33.0 <2.34.0", "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", + "from": "qs@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.1", - "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "from": "json-stringify-safe@>=5.0.0 <5.1.0", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, "forever-agent": { "version": "0.5.2", - "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "from": "forever-agent@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, "node-uuid": { "version": "1.4.7", - "from": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz", + "from": "node-uuid@>=1.4.0 <1.5.0", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" }, "mime": { "version": "1.2.11", - "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "from": "mime@>=1.2.9 <1.3.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "tough-cookie": { "version": "2.2.2", - "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", + "from": "tough-cookie@>=0.12.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" }, "form-data": { "version": "0.1.4", - "from": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "from": "form-data@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "dependencies": { "combined-stream": { "version": "0.0.7", - "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "from": "combined-stream@>=0.0.4 <0.1.0", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", - "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "from": "delayed-stream@0.0.5", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" } } }, "async": { "version": "0.9.2", - "from": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "from": "async@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" } } }, "tunnel-agent": { "version": "0.3.0", - "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", + "from": "tunnel-agent@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.1", - "from": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "from": "http-signature@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", "dependencies": { "assert-plus": { "version": "0.1.5", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "from": "assert-plus@>=0.1.5 <0.2.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" }, "asn1": { "version": "0.1.11", - "from": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "from": "asn1@0.1.11", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" }, "ctype": { "version": "0.5.3", - "from": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "from": "ctype@0.5.3", "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" } } }, "oauth-sign": { "version": "0.3.0", - "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "from": "oauth-sign@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" }, "hawk": { "version": "1.0.0", - "from": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "from": "hawk@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "from": "hoek@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, "boom": { "version": "0.4.2", - "from": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "from": "boom@>=0.4.0 <0.5.0", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" }, "cryptiles": { "version": "0.2.2", - "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "from": "cryptiles@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" }, "sntp": { "version": "0.2.4", - "from": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "from": "sntp@>=0.2.0 <0.3.0", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" } } }, "aws-sign2": { "version": "0.5.0", - "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "from": "aws-sign2@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" } } }, "s3-streams": { "version": "0.3.0", - "from": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", + "from": "s3-streams@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "dependencies": { "lodash": { "version": "3.10.1", - "from": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "from": "lodash@>=3.9.3 <4.0.0", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" }, "readable-stream": { "version": "2.0.5", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "from": "readable-stream@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "from": "core-util-is@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "inherits": { "version": "2.0.1", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "from": "inherits@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "from": "isarray@0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "process-nextick-args": { "version": "1.0.6", - "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz", + "from": "process-nextick-args@>=1.0.6 <1.1.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } }, "bluebird": { "version": "2.10.2", - "from": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz", + "from": "bluebird@>=2.9.27 <3.0.0", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" } } }, "settings-sharelatex": { "version": "1.0.0", - "from": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", "dependencies": { "coffee-script": { "version": "1.6.0", - "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "from": "coffee-script@1.6.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" } } }, "underscore": { "version": "1.7.0", - "from": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "from": "underscore@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" - }, - "v8-profiler": { - "version": "5.7.0", - "from": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", - "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", - "dependencies": { - "nan": { - "version": "2.5.1", - "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" - }, - "node-pre-gyp": { - "version": "0.6.34", - "from": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - } - } - }, - "nopt": { - "version": "4.0.1", - "from": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "dependencies": { - "abbrev": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" - }, - "osenv": { - "version": "0.1.4", - "from": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "dependencies": { - "os-homedir": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - }, - "os-tmpdir": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - } - } - } - } - }, - "npmlog": { - "version": "4.0.2", - "from": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", - "dependencies": { - "are-we-there-yet": { - "version": "1.1.2", - "from": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", - "dependencies": { - "delegates": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - }, - "readable-stream": { - "version": "2.2.6", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - } - } - }, - "console-control-strings": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - }, - "gauge": { - "version": "2.7.3", - "from": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", - "dependencies": { - "aproba": { - "version": "1.1.1", - "from": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" - }, - "has-unicode": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - }, - "object-assign": { - "version": "4.1.1", - "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - }, - "signal-exit": { - "version": "3.0.2", - "from": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" - }, - "string-width": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - } - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - } - } - }, - "wide-align": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" - } - } - }, - "set-blocking": { - "version": "2.0.0", - "from": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - } - } - }, - "rc": { - "version": "1.1.7", - "from": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", - "dependencies": { - "deep-extend": { - "version": "0.4.1", - "from": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" - }, - "ini": { - "version": "1.3.4", - "from": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" - }, - "minimist": { - "version": "1.2.0", - "from": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "strip-json-comments": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - } - } - }, - "request": { - "version": "2.81.0", - "from": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.6.0", - "from": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" - }, - "caseless": { - "version": "0.12.0", - "from": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - } - } - }, - "extend": { - "version": "3.0.0", - "from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "2.1.2", - "from": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", - "dependencies": { - "asynckit": { - "version": "0.4.0", - "from": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - } - } - }, - "har-validator": { - "version": "4.2.1", - "from": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "dependencies": { - "ajv": { - "version": "4.11.5", - "from": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", - "dependencies": { - "co": { - "version": "4.6.0", - "from": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" - }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "dependencies": { - "jsonify": { - "version": "0.0.0", - "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "jsprim": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "extsprintf": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "json-schema": { - "version": "0.2.3", - "from": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - }, - "verror": { - "version": "1.3.6", - "from": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - } - } - }, - "sshpk": { - "version": "1.11.0", - "from": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "dashdash": { - "version": "1.14.1", - "from": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - }, - "getpass": { - "version": "0.1.6", - "from": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" - }, - "jsbn": { - "version": "0.1.1", - "from": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "mime-types": { - "version": "2.1.14", - "from": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", - "dependencies": { - "mime-db": { - "version": "1.26.0", - "from": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" - }, - "performance-now": { - "version": "0.2.0", - "from": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" - }, - "qs": { - "version": "6.4.0", - "from": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" - }, - "safe-buffer": { - "version": "5.0.1", - "from": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" - }, - "stringstream": { - "version": "0.0.5", - "from": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - }, - "uuid": { - "version": "3.0.1", - "from": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" - } - } - }, - "rimraf": { - "version": "2.6.1", - "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "dependencies": { - "glob": { - "version": "7.1.1", - "from": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "inflight": { - "version": "1.0.6", - "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - } - } - } - } - }, - "semver": { - "version": "5.3.0", - "from": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" - }, - "tar": { - "version": "2.2.1", - "from": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "dependencies": { - "block-stream": { - "version": "0.0.9", - "from": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" - }, - "fstream": { - "version": "1.0.11", - "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - } - }, - "tar-pack": { - "version": "3.4.0", - "from": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "dependencies": { - "debug": { - "version": "2.6.3", - "from": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", - "dependencies": { - "ms": { - "version": "0.7.2", - "from": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" - } - } - }, - "fstream": { - "version": "1.0.11", - "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - } - }, - "fstream-ignore": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "dependencies": { - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - } - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "readable-stream": { - "version": "2.2.6", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - }, - "uid-number": { - "version": "0.0.6", - "from": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" - } - } - } - } - } - } } } } From f1a66df4ad1431afb5480eba0a330d4ea721636d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 23 Mar 2017 17:01:01 +0000 Subject: [PATCH 352/549] added v8-profiler to shrinkwrap --- services/track-changes/npm-shrinkwrap.json | 752 +++++++++++++++++++++ 1 file changed, 752 insertions(+) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 36b6e13937..9c29e01fe9 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1125,6 +1125,758 @@ "version": "1.7.0", "from": "underscore@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "v8-profiler": { + "version": "5.7.0", + "from": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "dependencies": { + "nan": { + "version": "2.5.1", + "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" + }, + "node-pre-gyp": { + "version": "0.6.34", + "from": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "nopt": { + "version": "4.0.1", + "from": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" + }, + "osenv": { + "version": "0.1.4", + "from": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + } + } + } + }, + "npmlog": { + "version": "4.0.2", + "from": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "dependencies": { + "are-we-there-yet": { + "version": "1.1.2", + "from": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "dependencies": { + "delegates": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + }, + "readable-stream": { + "version": "2.2.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + }, + "gauge": { + "version": "2.7.3", + "from": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "dependencies": { + "aproba": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" + }, + "has-unicode": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + }, + "object-assign": { + "version": "4.1.1", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "signal-exit": { + "version": "3.0.2", + "from": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + }, + "string-width": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + } + }, + "wide-align": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" + } + } + }, + "set-blocking": { + "version": "2.0.0", + "from": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + } + } + }, + "rc": { + "version": "1.1.7", + "from": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "dependencies": { + "deep-extend": { + "version": "0.4.1", + "from": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" + }, + "ini": { + "version": "1.3.4", + "from": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "strip-json-comments": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + } + } + }, + "request": { + "version": "2.81.0", + "from": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + } + }, + "extend": { + "version": "3.0.0", + "from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.2", + "from": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "dependencies": { + "asynckit": { + "version": "0.4.0", + "from": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + } + } + }, + "har-validator": { + "version": "4.2.1", + "from": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "dependencies": { + "ajv": { + "version": "4.11.5", + "from": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "dependencies": { + "co": { + "version": "4.6.0", + "from": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "dependencies": { + "jsonify": { + "version": "0.0.0", + "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "jsprim": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "extsprintf": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "verror": { + "version": "1.3.6", + "from": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + } + } + }, + "sshpk": { + "version": "1.11.0", + "from": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" + }, + "getpass": { + "version": "0.1.6", + "from": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "jodid25519": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "mime-types": { + "version": "2.1.14", + "from": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", + "dependencies": { + "mime-db": { + "version": "1.26.0", + "from": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "safe-buffer": { + "version": "5.0.1", + "from": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" + }, + "stringstream": { + "version": "0.0.5", + "from": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.0.1", + "from": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + } + } + }, + "rimraf": { + "version": "2.6.1", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "dependencies": { + "glob": { + "version": "7.1.1", + "from": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + } + } + } + }, + "semver": { + "version": "5.3.0", + "from": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + }, + "tar": { + "version": "2.2.1", + "from": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.9", + "from": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" + }, + "fstream": { + "version": "1.0.11", + "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } + } + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + }, + "tar-pack": { + "version": "3.4.0", + "from": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "dependencies": { + "debug": { + "version": "2.6.3", + "from": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "dependencies": { + "ms": { + "version": "0.7.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + } + }, + "fstream": { + "version": "1.0.11", + "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "dependencies": { + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "readable-stream": { + "version": "2.2.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "uid-number": { + "version": "0.0.6", + "from": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + } + } + } + } + } + } } } } From 6ada7842a97c24f337a6680257197c4fab0b4d92 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 24 Mar 2017 09:12:28 +0000 Subject: [PATCH 353/549] Revert "added v8-profiler to shrinkwrap" This reverts commit 62d481b31ebed2a1714cdfcb037a9c1a9c053756. --- services/track-changes/npm-shrinkwrap.json | 752 --------------------- 1 file changed, 752 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 9c29e01fe9..36b6e13937 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1125,758 +1125,6 @@ "version": "1.7.0", "from": "underscore@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" - }, - "v8-profiler": { - "version": "5.7.0", - "from": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", - "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", - "dependencies": { - "nan": { - "version": "2.5.1", - "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" - }, - "node-pre-gyp": { - "version": "0.6.34", - "from": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - } - } - }, - "nopt": { - "version": "4.0.1", - "from": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "dependencies": { - "abbrev": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" - }, - "osenv": { - "version": "0.1.4", - "from": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "dependencies": { - "os-homedir": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - }, - "os-tmpdir": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - } - } - } - } - }, - "npmlog": { - "version": "4.0.2", - "from": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", - "dependencies": { - "are-we-there-yet": { - "version": "1.1.2", - "from": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", - "dependencies": { - "delegates": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - }, - "readable-stream": { - "version": "2.2.6", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - } - } - }, - "console-control-strings": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - }, - "gauge": { - "version": "2.7.3", - "from": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", - "dependencies": { - "aproba": { - "version": "1.1.1", - "from": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" - }, - "has-unicode": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - }, - "object-assign": { - "version": "4.1.1", - "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - }, - "signal-exit": { - "version": "3.0.2", - "from": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" - }, - "string-width": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - } - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - } - } - }, - "wide-align": { - "version": "1.1.0", - "from": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" - } - } - }, - "set-blocking": { - "version": "2.0.0", - "from": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - } - } - }, - "rc": { - "version": "1.1.7", - "from": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", - "dependencies": { - "deep-extend": { - "version": "0.4.1", - "from": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" - }, - "ini": { - "version": "1.3.4", - "from": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" - }, - "minimist": { - "version": "1.2.0", - "from": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - }, - "strip-json-comments": { - "version": "2.0.1", - "from": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - } - } - }, - "request": { - "version": "2.81.0", - "from": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.6.0", - "from": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" - }, - "caseless": { - "version": "0.12.0", - "from": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - } - } - }, - "extend": { - "version": "3.0.0", - "from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "2.1.2", - "from": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", - "dependencies": { - "asynckit": { - "version": "0.4.0", - "from": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - } - } - }, - "har-validator": { - "version": "4.2.1", - "from": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "dependencies": { - "ajv": { - "version": "4.11.5", - "from": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", - "dependencies": { - "co": { - "version": "4.6.0", - "from": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" - }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "dependencies": { - "jsonify": { - "version": "0.0.0", - "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "jsprim": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "extsprintf": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "json-schema": { - "version": "0.2.3", - "from": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - }, - "verror": { - "version": "1.3.6", - "from": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - } - } - }, - "sshpk": { - "version": "1.11.0", - "from": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "dashdash": { - "version": "1.14.1", - "from": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - }, - "getpass": { - "version": "0.1.6", - "from": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" - }, - "jsbn": { - "version": "0.1.1", - "from": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "mime-types": { - "version": "2.1.14", - "from": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", - "dependencies": { - "mime-db": { - "version": "1.26.0", - "from": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" - }, - "performance-now": { - "version": "0.2.0", - "from": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" - }, - "qs": { - "version": "6.4.0", - "from": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" - }, - "safe-buffer": { - "version": "5.0.1", - "from": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" - }, - "stringstream": { - "version": "0.0.5", - "from": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - }, - "uuid": { - "version": "3.0.1", - "from": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" - } - } - }, - "rimraf": { - "version": "2.6.1", - "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "dependencies": { - "glob": { - "version": "7.1.1", - "from": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "inflight": { - "version": "1.0.6", - "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - } - } - } - } - }, - "semver": { - "version": "5.3.0", - "from": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" - }, - "tar": { - "version": "2.2.1", - "from": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "dependencies": { - "block-stream": { - "version": "0.0.9", - "from": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" - }, - "fstream": { - "version": "1.0.11", - "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - } - }, - "tar-pack": { - "version": "3.4.0", - "from": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "dependencies": { - "debug": { - "version": "2.6.3", - "from": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", - "dependencies": { - "ms": { - "version": "0.7.2", - "from": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" - } - } - }, - "fstream": { - "version": "1.0.11", - "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - } - }, - "fstream-ignore": { - "version": "1.0.5", - "from": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "dependencies": { - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - } - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "readable-stream": { - "version": "2.2.6", - "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - }, - "uid-number": { - "version": "0.0.6", - "from": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" - } - } - } - } - } - } } } } From a73c8ba7b8fc0468069764398fa0466cdf5b1fad Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 24 Mar 2017 09:17:11 +0000 Subject: [PATCH 354/549] Revert "Merge pull request #23 from sharelatex/bg-move-lock" This reverts commit 72e6756415e390a29fa784529453f926e5144358, reversing changes made to 8f507dcc9fbcd51072047c82a443137aaac0f377. --- .../app/coffee/UpdatesManager.coffee | 93 +++++++------------ .../app/coffee/WebApiManager.coffee | 2 +- .../UpdatesManager/UpdatesManagerTests.coffee | 38 ++++---- 3 files changed, 54 insertions(+), 79 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index fa4016f632..ad6a583979 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -68,74 +68,53 @@ module.exports = UpdatesManager = logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? callback() - # Check whether the updates are temporary (per-project property) - _prepareProjectForUpdates: (project_id, callback = (error, temporary) ->) -> + REDIS_READ_BATCH_SIZE: 100 + processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> UpdateTrimmer.shouldTrimUpdates project_id, (error, temporary) -> return callback(error) if error? - callback(null, temporary) - - # Check for project id on document history (per-document property) - _prepareDocForUpdates: (project_id, doc_id, callback = (error) ->) -> - MongoManager.backportProjectId project_id, doc_id, (error) -> - return callback(error) if error? - callback(null) - - # Apply updates for specific project/doc after preparing at project and doc level - REDIS_READ_BATCH_SIZE: 100 - processUncompressedUpdates: (project_id, doc_id, temporary, callback = (error) ->) -> - # get the updates as strings from redis (so we can delete them after they are applied) - RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> - return callback(error) if error? - length = docUpdates.length - # parse the redis strings into ShareJs updates - RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> + MongoManager.backportProjectId project_id, doc_id, (error) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> + # get the updates as strings from redis (so we can delete them after they are applied) + RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - # delete the applied updates from redis - RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> + length = docUpdates.length + # parse the redis strings into ShareJs updates + RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> + return callback(error) if error? + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + # delete the applied updates from redis + RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> + return callback(error) if error? + if length == UpdatesManager.REDIS_READ_BATCH_SIZE + # There might be more updates + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" + setTimeout () -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, callback + , 0 + else + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" + callback() - # Process updates for a doc when we flush it individually processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> - UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> - return callback(error) if error? - UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, callback + LockManager.runWithLock( + "HistoryLock:#{doc_id}", + (releaseLock) -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, releaseLock + callback + ) - - # Process updates for a doc when the whole project is flushed (internal method) - _processUncompressedUpdatesForDocWithLock: (project_id, doc_id, temporary, callback = (error) ->) -> - UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> - return callback(error) if error? - LockManager.runWithLock( - "HistoryLock:#{doc_id}", - (releaseLock) -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, releaseLock - callback - ) - - # Process all updates for a project, only check project-level information once processUncompressedUpdatesForProject: (project_id, callback = (error) ->) -> RedisManager.getDocIdsWithHistoryOps project_id, (error, doc_ids) -> return callback(error) if error? - UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> - jobs = [] - for doc_id in doc_ids - do (doc_id) -> - jobs.push (cb) -> - UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb - async.parallelLimit jobs, 5, callback + jobs = [] + for doc_id in doc_ids + do (doc_id) -> + jobs.push (callback) -> + UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, callback + async.parallelLimit jobs, 5, callback getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 0067c87d03..50409ca43c 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -3,7 +3,7 @@ logger = require "logger-sharelatex" Settings = require "settings-sharelatex" # Don't let HTTP calls hang for a long time -MAX_HTTP_REQUEST_LENGTH = 30000 # 30 seconds +MAX_HTTP_REQUEST_LENGTH = 15000 # 15 seconds # DEPRECATED! This method of getting user details via track-changes is deprecated # in the way we lay out our services. diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 4a7d463b37..161a5deb55 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -182,7 +182,17 @@ describe "UpdatesManager", -> @updates = ["mock-update"] @RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, @updates) @RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, @updates) - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, @callback + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback + + it "should check if the updates are temporary", -> + @UpdateTrimmer.shouldTrimUpdates + .calledWith(@project_id) + .should.equal true + + it "should backport the project id", -> + @MongoManager.backportProjectId + .calledWith(@project_id, @doc_id) + .should.equal true it "should get the oldest updates", -> @RedisManager.getOldestDocUpdates @@ -215,7 +225,7 @@ describe "UpdatesManager", -> @RedisManager.expandDocUpdates = (jsonUpdates, callback) => callback null, jsonUpdates sinon.spy @RedisManager, "expandDocUpdates" - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, (args...) => + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => @callback(args...) done() @@ -241,22 +251,10 @@ describe "UpdatesManager", -> describe "processCompressedUpdatesWithLock", -> beforeEach -> - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") - @MongoManager.backportProjectId = sinon.stub().callsArg(2) - @UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3) + @UpdatesManager.processUncompressedUpdates = sinon.stub().callsArg(2) @LockManager.runWithLock = sinon.stub().callsArg(2) @UpdatesManager.processUncompressedUpdatesWithLock @project_id, @doc_id, @callback - it "should check if the updates are temporary", -> - @UpdateTrimmer.shouldTrimUpdates - .calledWith(@project_id) - .should.equal true - - it "should backport the project id", -> - @MongoManager.backportProjectId - .calledWith(@project_id, @doc_id) - .should.equal true - it "should run processUncompressedUpdates with the lock", -> @LockManager.runWithLock .calledWith( @@ -315,9 +313,7 @@ describe "UpdatesManager", -> describe "processUncompressedUpdatesForProject", -> beforeEach (done) -> @doc_ids = ["mock-id-1", "mock-id-2"] - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") - @MongoManager.backportProjectId = sinon.stub().callsArg(2) - @UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon.stub().callsArg(3) + @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => @callback() @@ -330,8 +326,8 @@ describe "UpdatesManager", -> it "should process the doc ops for the each doc_id", -> for doc_id in @doc_ids - @UpdatesManager._processUncompressedUpdatesForDocWithLock - .calledWith(@project_id, doc_id, @temporary) + @UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(@project_id, doc_id) .should.equal true it "should call the callback", -> @@ -802,4 +798,4 @@ describe "UpdatesManager", -> user_ids: [@user_2.id] start_ts: @now end_ts: @now + 10 - }] + }] \ No newline at end of file From 8bd3dcca75d8745ed35a781970244c4d5158e5ee Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 24 Mar 2017 09:17:38 +0000 Subject: [PATCH 355/549] Revert "Merge pull request #24 from sharelatex/bg-add-profile" This reverts commit 8f507dcc9fbcd51072047c82a443137aaac0f377, reversing changes made to 70abd7a155b396e3b44ce21ff8d36be6ad03a060. --- services/track-changes/app.coffee | 9 --------- services/track-changes/package.json | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index d022406497..1e5c4e1635 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -75,15 +75,6 @@ app.get "/check_lock", HttpController.checkLock app.get "/health_check", HttpController.healthCheck -profiler = require "v8-profiler" -app.get "/profile", (req, res) -> - time = parseInt(req.query.time || "1000") - profiler.startProfiling("test") - setTimeout () -> - profile = profiler.stopProfiling("test") - res.json(profile) - , time - app.use (error, req, res, next) -> logger.error err: error, req: req, "an internal error occured" res.send 500 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e385de49e6..a376684d45 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -24,8 +24,7 @@ "mongo-uri": "^0.1.2", "s3-streams": "^0.3.0", "JSONStream": "^1.0.4", - "heap": "^0.2.6", - "v8-profiler": "^5.6.5" + "heap": "^0.2.6" }, "devDependencies": { "chai": "~1.9.0", From 0130d3513ddfa588a601c889fa5b59f13207b302 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 27 Mar 2017 13:25:59 +0100 Subject: [PATCH 356/549] Revert "Revert "Merge pull request #24 from sharelatex/bg-add-profile"" This reverts commit a58cd0d787b51634caf694f9d858e638d27178b2. --- services/track-changes/app.coffee | 9 +++++++++ services/track-changes/package.json | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 1e5c4e1635..d022406497 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -75,6 +75,15 @@ app.get "/check_lock", HttpController.checkLock app.get "/health_check", HttpController.healthCheck +profiler = require "v8-profiler" +app.get "/profile", (req, res) -> + time = parseInt(req.query.time || "1000") + profiler.startProfiling("test") + setTimeout () -> + profile = profiler.stopProfiling("test") + res.json(profile) + , time + app.use (error, req, res, next) -> logger.error err: error, req: req, "an internal error occured" res.send 500 diff --git a/services/track-changes/package.json b/services/track-changes/package.json index a376684d45..e385de49e6 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -24,7 +24,8 @@ "mongo-uri": "^0.1.2", "s3-streams": "^0.3.0", "JSONStream": "^1.0.4", - "heap": "^0.2.6" + "heap": "^0.2.6", + "v8-profiler": "^5.6.5" }, "devDependencies": { "chai": "~1.9.0", From e745cce9f55461781590fed2349209d47dddd77b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 27 Mar 2017 13:26:55 +0100 Subject: [PATCH 357/549] Revert "Revert "added v8-profiler to shrinkwrap"" This reverts commit 642641ff7b97034346d494101124bee616927ade. --- services/track-changes/npm-shrinkwrap.json | 752 +++++++++++++++++++++ 1 file changed, 752 insertions(+) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 36b6e13937..9c29e01fe9 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1125,6 +1125,758 @@ "version": "1.7.0", "from": "underscore@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "v8-profiler": { + "version": "5.7.0", + "from": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "dependencies": { + "nan": { + "version": "2.5.1", + "from": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" + }, + "node-pre-gyp": { + "version": "0.6.34", + "from": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "nopt": { + "version": "4.0.1", + "from": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" + }, + "osenv": { + "version": "0.1.4", + "from": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + } + } + } + }, + "npmlog": { + "version": "4.0.2", + "from": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "dependencies": { + "are-we-there-yet": { + "version": "1.1.2", + "from": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "dependencies": { + "delegates": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + }, + "readable-stream": { + "version": "2.2.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + }, + "gauge": { + "version": "2.7.3", + "from": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "dependencies": { + "aproba": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" + }, + "has-unicode": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + }, + "object-assign": { + "version": "4.1.1", + "from": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "signal-exit": { + "version": "3.0.2", + "from": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + }, + "string-width": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "dependencies": { + "code-point-at": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + } + }, + "wide-align": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" + } + } + }, + "set-blocking": { + "version": "2.0.0", + "from": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + } + } + }, + "rc": { + "version": "1.1.7", + "from": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.7.tgz", + "dependencies": { + "deep-extend": { + "version": "0.4.1", + "from": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" + }, + "ini": { + "version": "1.3.4", + "from": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "strip-json-comments": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + } + } + }, + "request": { + "version": "2.81.0", + "from": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + } + }, + "extend": { + "version": "3.0.0", + "from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.2", + "from": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "dependencies": { + "asynckit": { + "version": "0.4.0", + "from": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + } + } + }, + "har-validator": { + "version": "4.2.1", + "from": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "dependencies": { + "ajv": { + "version": "4.11.5", + "from": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "dependencies": { + "co": { + "version": "4.6.0", + "from": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "dependencies": { + "jsonify": { + "version": "0.0.0", + "from": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "jsprim": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "extsprintf": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "verror": { + "version": "1.3.6", + "from": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + } + } + }, + "sshpk": { + "version": "1.11.0", + "from": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" + }, + "getpass": { + "version": "0.1.6", + "from": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "jodid25519": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "mime-types": { + "version": "2.1.14", + "from": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz", + "dependencies": { + "mime-db": { + "version": "1.26.0", + "from": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "safe-buffer": { + "version": "5.0.1", + "from": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" + }, + "stringstream": { + "version": "0.0.5", + "from": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.0.1", + "from": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + } + } + }, + "rimraf": { + "version": "2.6.1", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "dependencies": { + "glob": { + "version": "7.1.1", + "from": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + } + } + } + }, + "semver": { + "version": "5.3.0", + "from": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + }, + "tar": { + "version": "2.2.1", + "from": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.9", + "from": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" + }, + "fstream": { + "version": "1.0.11", + "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + } + } + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + }, + "tar-pack": { + "version": "3.4.0", + "from": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "dependencies": { + "debug": { + "version": "2.6.3", + "from": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "dependencies": { + "ms": { + "version": "0.7.2", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + } + }, + "fstream": { + "version": "1.0.11", + "from": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "dependencies": { + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "minimatch": { + "version": "3.0.3", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.6", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "readable-stream": { + "version": "2.2.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "uid-number": { + "version": "0.0.6", + "from": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + } + } + } + } + } + } } } } From 715ccbe227ec93f9af699eefd6d87501ae054e81 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 27 Mar 2017 14:37:32 +0100 Subject: [PATCH 358/549] Add a .nvmrc file --- services/track-changes/.nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 services/track-changes/.nvmrc diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc new file mode 100644 index 0000000000..994fe99096 --- /dev/null +++ b/services/track-changes/.nvmrc @@ -0,0 +1 @@ +0.10.22 \ No newline at end of file From 28d2ec93b8e20886b4185e581eb154f1ffb18654 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 27 Mar 2017 14:23:34 +0100 Subject: [PATCH 359/549] Revert "Revert "Merge pull request #23 from sharelatex/bg-move-lock"" This reverts commit 85bc45099f047aa8dfa6d189f02f4b4327c9d602. --- .../app/coffee/UpdatesManager.coffee | 93 ++++++++++++------- .../app/coffee/WebApiManager.coffee | 2 +- .../UpdatesManager/UpdatesManagerTests.coffee | 38 ++++---- 3 files changed, 79 insertions(+), 54 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index ad6a583979..fa4016f632 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -68,53 +68,74 @@ module.exports = UpdatesManager = logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? callback() - REDIS_READ_BATCH_SIZE: 100 - processUncompressedUpdates: (project_id, doc_id, callback = (error) ->) -> + # Check whether the updates are temporary (per-project property) + _prepareProjectForUpdates: (project_id, callback = (error, temporary) ->) -> UpdateTrimmer.shouldTrimUpdates project_id, (error, temporary) -> return callback(error) if error? - MongoManager.backportProjectId project_id, doc_id, (error) -> + callback(null, temporary) + + # Check for project id on document history (per-document property) + _prepareDocForUpdates: (project_id, doc_id, callback = (error) ->) -> + MongoManager.backportProjectId project_id, doc_id, (error) -> + return callback(error) if error? + callback(null) + + # Apply updates for specific project/doc after preparing at project and doc level + REDIS_READ_BATCH_SIZE: 100 + processUncompressedUpdates: (project_id, doc_id, temporary, callback = (error) ->) -> + # get the updates as strings from redis (so we can delete them after they are applied) + RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> + return callback(error) if error? + length = docUpdates.length + # parse the redis strings into ShareJs updates + RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> return callback(error) if error? - # get the updates as strings from redis (so we can delete them after they are applied) - RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> + logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" + UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? - length = docUpdates.length - # parse the redis strings into ShareJs updates - RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> + logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" + # delete the applied updates from redis + RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> - return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - # delete the applied updates from redis - RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> - return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + if length == UpdatesManager.REDIS_READ_BATCH_SIZE + # There might be more updates + logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" + setTimeout () -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, callback + , 0 + else + logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" + callback() + # Process updates for a doc when we flush it individually processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> - LockManager.runWithLock( - "HistoryLock:#{doc_id}", - (releaseLock) -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, releaseLock - callback - ) + UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> + return callback(error) if error? + UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, callback + + # Process updates for a doc when the whole project is flushed (internal method) + _processUncompressedUpdatesForDocWithLock: (project_id, doc_id, temporary, callback = (error) ->) -> + UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> + return callback(error) if error? + LockManager.runWithLock( + "HistoryLock:#{doc_id}", + (releaseLock) -> + UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, releaseLock + callback + ) + + # Process all updates for a project, only check project-level information once processUncompressedUpdatesForProject: (project_id, callback = (error) ->) -> RedisManager.getDocIdsWithHistoryOps project_id, (error, doc_ids) -> return callback(error) if error? - jobs = [] - for doc_id in doc_ids - do (doc_id) -> - jobs.push (callback) -> - UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, callback - async.parallelLimit jobs, 5, callback + UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> + jobs = [] + for doc_id in doc_ids + do (doc_id) -> + jobs.push (cb) -> + UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb + async.parallelLimit jobs, 5, callback getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 50409ca43c..0067c87d03 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -3,7 +3,7 @@ logger = require "logger-sharelatex" Settings = require "settings-sharelatex" # Don't let HTTP calls hang for a long time -MAX_HTTP_REQUEST_LENGTH = 15000 # 15 seconds +MAX_HTTP_REQUEST_LENGTH = 30000 # 30 seconds # DEPRECATED! This method of getting user details via track-changes is deprecated # in the way we lay out our services. diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 161a5deb55..4a7d463b37 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -182,17 +182,7 @@ describe "UpdatesManager", -> @updates = ["mock-update"] @RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, @updates) @RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, @updates) - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @callback - - it "should check if the updates are temporary", -> - @UpdateTrimmer.shouldTrimUpdates - .calledWith(@project_id) - .should.equal true - - it "should backport the project id", -> - @MongoManager.backportProjectId - .calledWith(@project_id, @doc_id) - .should.equal true + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, @callback it "should get the oldest updates", -> @RedisManager.getOldestDocUpdates @@ -225,7 +215,7 @@ describe "UpdatesManager", -> @RedisManager.expandDocUpdates = (jsonUpdates, callback) => callback null, jsonUpdates sinon.spy @RedisManager, "expandDocUpdates" - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, (args...) => + @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, (args...) => @callback(args...) done() @@ -251,10 +241,22 @@ describe "UpdatesManager", -> describe "processCompressedUpdatesWithLock", -> beforeEach -> - @UpdatesManager.processUncompressedUpdates = sinon.stub().callsArg(2) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") + @MongoManager.backportProjectId = sinon.stub().callsArg(2) + @UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3) @LockManager.runWithLock = sinon.stub().callsArg(2) @UpdatesManager.processUncompressedUpdatesWithLock @project_id, @doc_id, @callback + it "should check if the updates are temporary", -> + @UpdateTrimmer.shouldTrimUpdates + .calledWith(@project_id) + .should.equal true + + it "should backport the project id", -> + @MongoManager.backportProjectId + .calledWith(@project_id, @doc_id) + .should.equal true + it "should run processUncompressedUpdates with the lock", -> @LockManager.runWithLock .calledWith( @@ -313,7 +315,9 @@ describe "UpdatesManager", -> describe "processUncompressedUpdatesForProject", -> beforeEach (done) -> @doc_ids = ["mock-id-1", "mock-id-2"] - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) + @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") + @MongoManager.backportProjectId = sinon.stub().callsArg(2) + @UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon.stub().callsArg(3) @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => @callback() @@ -326,8 +330,8 @@ describe "UpdatesManager", -> it "should process the doc ops for the each doc_id", -> for doc_id in @doc_ids - @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@project_id, doc_id) + @UpdatesManager._processUncompressedUpdatesForDocWithLock + .calledWith(@project_id, doc_id, @temporary) .should.equal true it "should call the callback", -> @@ -798,4 +802,4 @@ describe "UpdatesManager", -> user_ids: [@user_2.id] start_ts: @now end_ts: @now + 10 - }] \ No newline at end of file + }] From 70bb95fb363f73ffa26981cb6d7b49aa7bfbe616 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 30 Mar 2017 09:37:00 +0100 Subject: [PATCH 360/549] update logger-sharelatex to v1.5.6 --- services/track-changes/npm-shrinkwrap.json | 1089 +++++++++++++------- services/track-changes/package.json | 2 +- 2 files changed, 698 insertions(+), 393 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 9c29e01fe9..ad3ba27a7f 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -4,49 +4,49 @@ "dependencies": { "JSONStream": { "version": "1.1.1", - "from": "JSONStream@>=1.0.4 <2.0.0", + "from": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", "dependencies": { "jsonparse": { "version": "1.2.0", - "from": "jsonparse@>=1.1.0 <2.0.0", + "from": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz" }, "through": { "version": "2.3.8", - "from": "through@>=2.2.7 <3.0.0", + "from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "async": { "version": "0.2.10", - "from": "async@>=0.2.10 <0.3.0", + "from": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" }, "aws-sdk": { "version": "2.2.43", - "from": "aws-sdk@>=2.1.34 <3.0.0", + "from": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", "dependencies": { "sax": { "version": "1.1.5", - "from": "sax@1.1.5", + "from": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" }, "xml2js": { "version": "0.4.15", - "from": "xml2js@0.4.15", + "from": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" }, "xmlbuilder": { "version": "2.6.2", - "from": "xmlbuilder@2.6.2", + "from": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", "dependencies": { "lodash": { "version": "3.5.0", - "from": "lodash@>=3.5.0 <3.6.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" } } @@ -55,42 +55,42 @@ }, "bson": { "version": "0.4.22", - "from": "bson@>=0.4.20 <0.5.0", + "from": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz", "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" }, "byline": { "version": "4.2.1", - "from": "byline@>=4.2.1 <5.0.0", + "from": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz", "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz" }, "cli": { "version": "0.6.6", - "from": "cli@>=0.6.6 <0.7.0", + "from": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@>=3.2.1 <3.3.0", + "from": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } @@ -99,115 +99,115 @@ }, "exit": { "version": "0.1.2", - "from": "exit@0.1.2", + "from": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" } } }, "express": { "version": "3.3.5", - "from": "express@3.3.5", + "from": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "dependencies": { "connect": { "version": "2.8.5", - "from": "connect@2.8.5", + "from": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "dependencies": { "qs": { "version": "0.6.5", - "from": "qs@0.6.5", + "from": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" }, "formidable": { "version": "1.0.14", - "from": "formidable@1.0.14", + "from": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" }, "bytes": { "version": "0.2.0", - "from": "bytes@0.2.0", + "from": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" }, "pause": { "version": "0.0.1", - "from": "pause@0.0.1", + "from": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" }, "uid2": { "version": "0.0.2", - "from": "uid2@0.0.2", + "from": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" } } }, "commander": { "version": "1.2.0", - "from": "commander@1.2.0", + "from": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "dependencies": { "keypress": { "version": "0.1.0", - "from": "keypress@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" } } }, "range-parser": { "version": "0.0.4", - "from": "range-parser@0.0.4", + "from": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0", + "from": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" }, "buffer-crc32": { "version": "0.2.1", - "from": "buffer-crc32@0.2.1", + "from": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" }, "fresh": { "version": "0.2.0", - "from": "fresh@0.2.0", + "from": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" }, "methods": { "version": "0.0.1", - "from": "methods@0.0.1", + "from": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" }, "send": { "version": "0.1.4", - "from": "send@0.1.4", + "from": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "dependencies": { "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", + "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "cookie-signature": { "version": "1.0.1", - "from": "cookie-signature@1.0.1", + "from": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" }, "debug": { "version": "2.2.0", - "from": "debug@*", + "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "ms@0.7.1", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } @@ -216,18 +216,18 @@ }, "heap": { "version": "0.2.6", - "from": "heap@>=0.2.6 <0.3.0", + "from": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" }, "line-reader": { "version": "0.2.4", - "from": "line-reader@>=0.2.4 <0.3.0", + "from": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, "logger-sharelatex": { - "version": "1.3.0", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#fd5e037e64178fb3ef5138d39a048cadbacd2ade", + "version": "1.5.6", + "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", + "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#b2956ec56b582b9f4fc8fdda8dc00c06e77c5537", "dependencies": { "bunyan": { "version": "1.5.1", @@ -240,9 +240,9 @@ "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "dependencies": { "nan": { - "version": "2.2.0", + "version": "2.5.1", "from": "nan@>=2.0.8 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.2.0.tgz" + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" } } }, @@ -279,36 +279,36 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "dependencies": { "inflight": { - "version": "1.0.4", + "version": "1.0.6", "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", + "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { - "version": "2.0.1", + "version": "2.0.3", "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { - "version": "3.0.0", + "version": "3.0.3", "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.3", + "version": "1.1.6", "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { - "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + "version": "0.4.2", + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", @@ -320,21 +320,21 @@ } }, "once": { - "version": "1.3.3", + "version": "1.4.0", "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", + "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { - "version": "1.0.0", + "version": "1.0.1", "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -343,251 +343,32 @@ } }, "safe-json-stringify": { - "version": "1.0.3", + "version": "1.0.4", "from": "safe-json-stringify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz" } } }, "coffee-script": { - "version": "1.4.0", - "from": "coffee-script@1.4.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz" + "version": "1.12.4", + "from": "coffee-script@1.12.4", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz" }, - "raven": { - "version": "0.8.1", - "from": "raven@>=0.8.0 <0.9.0", - "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", + "grunt-contrib-clean": { + "version": "0.6.0", + "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", "dependencies": { - "cookie": { - "version": "0.1.0", - "from": "cookie@0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" - }, - "lsmod": { - "version": "0.0.3", - "from": "lsmod@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz" - }, - "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.1 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, - "stack-trace": { - "version": "0.0.7", - "from": "stack-trace@0.0.7", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz" - } - } - } - } - }, - "metrics-sharelatex": { - "version": "1.3.0", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#a2d156e97b7ab51fca5f36c0838a9a808416dfe8", - "dependencies": { - "lynx": { - "version": "0.1.1", - "from": "lynx@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", - "dependencies": { - "mersenne": { - "version": "0.0.3", - "from": "mersenne@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" - }, - "statsd-parser": { - "version": "0.0.4", - "from": "statsd-parser@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" - } - } - }, - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" - }, - "underscore": { - "version": "1.6.0", - "from": "underscore@>=1.6.0 <1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" - } - } - }, - "mongo-uri": { - "version": "0.1.2", - "from": "mongo-uri@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" - }, - "mongojs": { - "version": "1.4.1", - "from": "mongojs@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", - "dependencies": { - "each-series": { - "version": "1.0.0", - "from": "each-series@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" - }, - "mongodb-core": { - "version": "1.3.10", - "from": "mongodb-core@>=1.2.8 <2.0.0", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", - "dependencies": { - "require_optional": { - "version": "1.0.0", - "from": "require_optional@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", - "dependencies": { - "semver": { - "version": "5.1.0", - "from": "semver@>=5.1.0 <6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" - }, - "resolve-from": { - "version": "2.0.0", - "from": "resolve-from@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" - } - } - } - } - }, - "once": { - "version": "1.3.3", - "from": "once@>=1.3.2 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" - } - } - }, - "parse-mongo-url": { - "version": "1.1.1", - "from": "parse-mongo-url@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" - }, - "pump": { - "version": "1.0.1", - "from": "pump@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", - "dependencies": { - "end-of-stream": { - "version": "1.1.0", - "from": "end-of-stream@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" - } - } - }, - "readable-stream": { - "version": "2.0.6", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "dependencies": { - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "process-nextick-args": { - "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - }, - "thunky": { - "version": "0.1.0", - "from": "thunky@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" - }, - "to-mongodb-core": { - "version": "2.0.0", - "from": "to-mongodb-core@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - } - } - }, - "redis": { - "version": "0.10.3", - "from": "redis@>=0.10.1 <0.11.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" - }, - "redis-sharelatex": { - "version": "0.0.9", - "from": "redis-sharelatex@>=0.0.9 <0.1.0", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", - "dependencies": { - "chai": { - "version": "1.9.1", - "from": "chai@1.9.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", - "dependencies": { - "assertion-error": { - "version": "1.0.0", - "from": "assertion-error@1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" - }, - "deep-eql": { - "version": "0.1.3", - "from": "deep-eql@0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "dependencies": { - "type-detect": { - "version": "0.1.1", - "from": "type-detect@0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" - } - } - } - } - }, - "coffee-script": { - "version": "1.8.0", - "from": "coffee-script@1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", - "dependencies": { - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "rimraf": { + "version": "2.2.8", + "from": "rimraf@>=2.2.1 <2.3.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@0.11.1", + "from": "grunt-contrib-coffee@>=0.11.0 <0.12.0", "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { @@ -655,25 +436,120 @@ } } }, + "grunt-execute": { + "version": "0.2.2", + "from": "grunt-execute@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" + }, "grunt-mocha-test": { - "version": "0.12.0", - "from": "grunt-mocha-test@0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", + "version": "0.11.0", + "from": "grunt-mocha-test@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz", "dependencies": { + "mocha": { + "version": "1.20.1", + "from": "mocha@>=1.20.0 <1.21.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz", + "dependencies": { + "commander": { + "version": "2.0.0", + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + }, + "growl": { + "version": "1.7.0", + "from": "growl@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" + }, + "jade": { + "version": "0.26.3", + "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "dependencies": { + "commander": { + "version": "0.6.1", + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + }, + "mkdirp": { + "version": "0.3.0", + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + } + } + }, + "diff": { + "version": "1.0.7", + "from": "diff@1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" + }, + "debug": { + "version": "2.6.3", + "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "dependencies": { + "ms": { + "version": "0.7.2", + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + } + } + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + }, + "glob": { + "version": "3.2.3", + "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.7.3", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "graceful-fs": { + "version": "2.0.3", + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + } + } + } + }, "hooker": { "version": "0.2.3", "from": "hooker@>=0.2.3 <0.3.0", "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "fs-extra": { - "version": "0.11.1", - "from": "fs-extra@>=0.11.1 <0.12.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "version": "0.9.1", + "from": "fs-extra@>=0.9.1 <0.10.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", "dependencies": { "ncp": { - "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + "version": "0.5.1", + "from": "ncp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" }, "mkdirp": { "version": "0.5.1", @@ -688,51 +564,56 @@ } }, "jsonfile": { - "version": "2.2.3", - "from": "jsonfile@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" + "version": "1.1.1", + "from": "jsonfile@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" }, "rimraf": { - "version": "2.5.2", + "version": "2.6.1", "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "dependencies": { "glob": { - "version": "7.0.3", - "from": "glob@>=7.0.0 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "version": "7.1.1", + "from": "glob@>=7.0.5 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, "inflight": { - "version": "1.0.4", + "version": "1.0.6", "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", + "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { - "version": "2.0.1", + "version": "2.0.3", "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { - "version": "3.0.0", + "version": "3.0.3", "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.3", + "version": "1.1.6", "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { - "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + "version": "0.4.2", + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", @@ -744,20 +625,444 @@ } }, "once": { - "version": "1.3.3", + "version": "1.4.0", "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + } + } + } + } + } + } + } + }, + "raven": { + "version": "1.2.0", + "from": "raven@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.0.tgz", + "dependencies": { + "cookie": { + "version": "0.3.1", + "from": "cookie@0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "lsmod": { + "version": "1.0.0", + "from": "lsmod@1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz" + }, + "uuid": { + "version": "3.0.0", + "from": "uuid@3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + }, + "stack-trace": { + "version": "0.0.9", + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + } + } + }, + "timekeeper": { + "version": "1.0.0", + "from": "timekeeper@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" + } + } + }, + "metrics-sharelatex": { + "version": "1.5.0", + "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", + "dependencies": { + "lynx": { + "version": "0.1.1", + "from": "lynx@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "dependencies": { + "mersenne": { + "version": "0.0.3", + "from": "mersenne@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" + }, + "statsd-parser": { + "version": "0.0.4", + "from": "statsd-parser@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + } + } + }, + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + }, + "underscore": { + "version": "1.6.0", + "from": "underscore@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + } + } + }, + "mongo-uri": { + "version": "0.1.2", + "from": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz", + "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" + }, + "mongojs": { + "version": "1.4.1", + "from": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", + "dependencies": { + "each-series": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + }, + "mongodb-core": { + "version": "1.3.10", + "from": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", + "dependencies": { + "require_optional": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", + "dependencies": { + "semver": { + "version": "5.1.0", + "from": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" + }, + "resolve-from": { + "version": "2.0.0", + "from": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "parse-mongo-url": { + "version": "1.1.1", + "from": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + }, + "pump": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.6", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "process-nextick-args": { + "version": "1.0.6", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "thunky": { + "version": "0.1.0", + "from": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + }, + "to-mongodb-core": { + "version": "2.0.0", + "from": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + } + } + }, + "redis": { + "version": "0.10.3", + "from": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" + }, + "redis-sharelatex": { + "version": "0.0.9", + "from": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", + "dependencies": { + "chai": { + "version": "1.9.1", + "from": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", + "dependencies": { + "assertion-error": { + "version": "1.0.0", + "from": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" + }, + "deep-eql": { + "version": "0.1.3", + "from": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "dependencies": { + "type-detect": { + "version": "0.1.1", + "from": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + } + } + } + } + }, + "coffee-script": { + "version": "1.8.0", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + }, + "grunt-contrib-coffee": { + "version": "0.11.1", + "from": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "dependencies": { + "coffee-script": { + "version": "1.7.1", + "from": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + }, + "chalk": { + "version": "0.5.1", + "from": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "dependencies": { + "ansi-styles": { + "version": "1.1.0", + "from": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "has-ansi": { + "version": "0.1.0", + "from": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "strip-ansi": { + "version": "0.3.0", + "from": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "from": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + } + } + }, + "supports-color": { + "version": "0.2.0", + "from": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + } + } + }, + "lodash": { + "version": "2.4.2", + "from": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "grunt-mocha-test": { + "version": "0.12.0", + "from": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", + "dependencies": { + "hooker": { + "version": "0.2.3", + "from": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "fs-extra": { + "version": "0.11.1", + "from": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "dependencies": { + "ncp": { + "version": "0.6.0", + "from": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "jsonfile": { + "version": "2.2.3", + "from": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" + }, + "rimraf": { + "version": "2.5.2", + "from": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "dependencies": { + "glob": { + "version": "7.0.3", + "from": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "3.0.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.3", + "from": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "dependencies": { + "balanced-match": { + "version": "0.3.0", + "from": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.3", + "from": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", "dependencies": { "wrappy": { "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" } } }, "path-is-absolute": { "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", + "from": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" } } @@ -770,88 +1075,88 @@ }, "mocha": { "version": "1.21.4", - "from": "mocha@1.21.4", + "from": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0", + "from": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "growl": { "version": "1.8.1", - "from": "growl@>=1.8.0 <1.9.0", + "from": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" }, "jade": { "version": "0.26.3", - "from": "jade@0.26.3", + "from": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1", + "from": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7", + "from": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { "version": "2.2.0", - "from": "debug@*", + "from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", - "from": "ms@0.7.1", + "from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", + "from": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "glob": { "version": "3.2.3", - "from": "glob@3.2.3", + "from": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", + "from": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", + "from": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -860,68 +1165,68 @@ }, "redis": { "version": "0.12.1", - "from": "redis@0.12.1", + "from": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" }, "redis-sentinel": { "version": "0.1.1", - "from": "redis-sentinel@0.1.1", + "from": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "dependencies": { "redis": { "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", + "from": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" }, "q": { "version": "0.9.2", - "from": "q@0.9.2", + "from": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" } } }, "sandboxed-module": { "version": "1.0.1", - "from": "sandboxed-module@1.0.1", + "from": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", - "from": "require-like@0.1.2", + "from": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", + "from": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } }, "sinon": { "version": "1.10.3", - "from": "sinon@1.10.3", + "from": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "formatio@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "dependencies": { "samsam": { "version": "1.1.3", - "from": "samsam@>=1.1.0 <1.2.0", + "from": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1.0.0", + "from": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } @@ -932,179 +1237,179 @@ }, "request": { "version": "2.33.0", - "from": "request@>=2.33.0 <2.34.0", + "from": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "qs@>=0.6.0 <0.7.0", + "from": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.0 <5.1.0", + "from": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, "node-uuid": { "version": "1.4.7", - "from": "node-uuid@>=1.4.0 <1.5.0", + "from": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" }, "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", + "from": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" }, "tough-cookie": { "version": "2.2.2", - "from": "tough-cookie@>=0.12.0", + "from": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" }, "form-data": { "version": "0.1.4", - "from": "form-data@>=0.1.0 <0.2.0", + "from": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "dependencies": { "combined-stream": { "version": "0.0.7", - "from": "combined-stream@>=0.0.4 <0.1.0", + "from": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dependencies": { "delayed-stream": { "version": "0.0.5", - "from": "delayed-stream@0.0.5", + "from": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" } } }, "async": { "version": "0.9.2", - "from": "async@>=0.9.0 <0.10.0", + "from": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" } } }, "tunnel-agent": { "version": "0.3.0", - "from": "tunnel-agent@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.1", - "from": "http-signature@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", "dependencies": { "assert-plus": { "version": "0.1.5", - "from": "assert-plus@>=0.1.5 <0.2.0", + "from": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" }, "asn1": { "version": "0.1.11", - "from": "asn1@0.1.11", + "from": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" }, "ctype": { "version": "0.5.3", - "from": "ctype@0.5.3", + "from": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" } } }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" }, "hawk": { "version": "1.0.0", - "from": "hawk@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "hoek@>=0.9.0 <0.10.0", + "from": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, "boom": { "version": "0.4.2", - "from": "boom@>=0.4.0 <0.5.0", + "from": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" }, "sntp": { "version": "0.2.4", - "from": "sntp@>=0.2.0 <0.3.0", + "from": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" } } }, "aws-sign2": { "version": "0.5.0", - "from": "aws-sign2@>=0.5.0 <0.6.0", + "from": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" } } }, "s3-streams": { "version": "0.3.0", - "from": "s3-streams@>=0.3.0 <0.4.0", + "from": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "dependencies": { "lodash": { "version": "3.10.1", - "from": "lodash@>=3.9.3 <4.0.0", + "from": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" }, "readable-stream": { "version": "2.0.5", - "from": "readable-stream@>=2.0.0 <3.0.0", + "from": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", "dependencies": { "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", + "from": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "inherits": { "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", + "from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "isarray": { "version": "0.0.1", - "from": "isarray@0.0.1", + "from": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "process-nextick-args": { "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", + "from": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", + "from": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", + "from": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } }, "bluebird": { "version": "2.10.2", - "from": "bluebird@>=2.9.27 <3.0.0", + "from": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" } } @@ -1123,7 +1428,7 @@ }, "underscore": { "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", + "from": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" }, "v8-profiler": { diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e385de49e6..85fc9dff38 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -15,7 +15,7 @@ "line-reader": "^0.2.4", "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.0", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", From d29141d583a197aa7c00104de0d326a6ad18d118 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 30 Mar 2017 11:48:26 +0100 Subject: [PATCH 361/549] return error when out-of-order ops detected --- .../track-changes/app/coffee/UpdatesManager.coffee | 9 +++++++++ .../UpdatesManager/UpdatesManagerTests.coffee | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index fa4016f632..fa56f463cd 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -16,6 +16,15 @@ module.exports = UpdatesManager = if length == 0 return callback() + # check that ops are in the correct order + for op, i in rawUpdates when i > 0 + thisVersion = op?.v + prevVersion = rawUpdates[i-1]?.v + if not (prevVersion < thisVersion) + logger.error project_id: project_id, doc_id: doc_id, rawUpdates:rawUpdates, temporary: temporary, thisVersion:thisVersion, prevVersion:prevVersion, "op versions out of order" + # TODO try to recover by sorting the ops + return callback(new Error("incoming op versions out of order")) + # FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it # CORRECTION: we do use it to log the time in case of error MongoManager.peekLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 4a7d463b37..dd0ba835d3 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -137,6 +137,20 @@ describe "UpdatesManager", -> it "should not insert any update into mongo", -> @PackManager.insertCompressedUpdates.called.should.equal false + describe "when the raw ops are out of order", -> + beforeEach -> + @rawUpdates = [{ v: 13, op: "mock-op-13" }, { v: 12, op: "mock-op-12" }] + @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + + it "should call the callback with an error", -> + @callback + .calledWith(new Error("incoming op versions out of order")) + .should.equal true + + it "should not insert any update into mongo", -> + @PackManager.insertCompressedUpdates.called.should.equal false + + describe "when the raw ops need appending to existing history which is in S3", -> beforeEach -> @lastCompressedUpdate = null From 8bb3dd07a438276446b7616f94e6f208f63b1afe Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 30 Mar 2017 13:37:11 +0100 Subject: [PATCH 362/549] continue when ops are out of order --- services/track-changes/app/coffee/UpdatesManager.coffee | 2 -- .../test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index fa56f463cd..f01681e5f0 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -22,8 +22,6 @@ module.exports = UpdatesManager = prevVersion = rawUpdates[i-1]?.v if not (prevVersion < thisVersion) logger.error project_id: project_id, doc_id: doc_id, rawUpdates:rawUpdates, temporary: temporary, thisVersion:thisVersion, prevVersion:prevVersion, "op versions out of order" - # TODO try to recover by sorting the ops - return callback(new Error("incoming op versions out of order")) # FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it # CORRECTION: we do use it to log the time in case of error diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index dd0ba835d3..c422fffec0 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -144,7 +144,7 @@ describe "UpdatesManager", -> it "should call the callback with an error", -> @callback - .calledWith(new Error("incoming op versions out of order")) + .calledWith(new Error) .should.equal true it "should not insert any update into mongo", -> From 9a847982935fa30482c064dc83206e040a8ef66f Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 31 Mar 2017 11:32:59 +0100 Subject: [PATCH 363/549] Upgrade to latest metrics --- services/track-changes/npm-shrinkwrap.json | 678 ++++++++++----------- services/track-changes/package.json | 2 +- 2 files changed, 314 insertions(+), 366 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 36b6e13937..7ea52eedb8 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -3,95 +3,131 @@ "version": "0.1.4", "dependencies": { "JSONStream": { - "version": "1.1.1", - "from": "JSONStream@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.1.tgz", + "version": "1.3.1", + "from": "JSONStream@^1.0.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", "dependencies": { "jsonparse": { - "version": "1.2.0", - "from": "jsonparse@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz" + "version": "1.3.0", + "from": "jsonparse@^1.2.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.0.tgz" }, "through": { "version": "2.3.8", - "from": "through@>=2.2.7 <3.0.0", + "from": "through@>=2.2.7 <3", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" } } }, "async": { "version": "0.2.10", - "from": "async@>=0.2.10 <0.3.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + "from": "async@~0.2.10" }, "aws-sdk": { - "version": "2.2.43", - "from": "aws-sdk@>=2.1.34 <3.0.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.2.43.tgz", + "version": "2.35.0", + "from": "aws-sdk@^2.1.34", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.35.0.tgz", "dependencies": { + "buffer": { + "version": "4.9.1", + "from": "buffer@4.9.1", + "dependencies": { + "base64-js": { + "version": "1.2.0", + "from": "base64-js@^1.0.2" + }, + "ieee754": { + "version": "1.1.8", + "from": "ieee754@^1.1.4" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@^1.0.0" + } + } + }, + "crypto-browserify": { + "version": "1.0.9", + "from": "crypto-browserify@1.0.9" + }, + "jmespath": { + "version": "0.15.0", + "from": "jmespath@0.15.0" + }, + "querystring": { + "version": "0.2.0", + "from": "querystring@0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + }, "sax": { "version": "1.1.5", - "from": "sax@1.1.5", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" + "from": "sax@1.1.5" + }, + "url": { + "version": "0.10.3", + "from": "url@0.10.3", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@1.3.2" + } + } + }, + "uuid": { + "version": "3.0.0", + "from": "uuid@3.0.0" }, "xml2js": { "version": "0.4.15", - "from": "xml2js@0.4.15", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" + "from": "xml2js@0.4.15" }, "xmlbuilder": { "version": "2.6.2", "from": "xmlbuilder@2.6.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", "dependencies": { "lodash": { "version": "3.5.0", - "from": "lodash@>=3.5.0 <3.6.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" + "from": "lodash@~3.5.0" } } } } }, "bson": { - "version": "0.4.22", - "from": "bson@>=0.4.20 <0.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.22.tgz" + "version": "0.4.23", + "from": "bson@^0.4.20", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" }, "byline": { - "version": "4.2.1", - "from": "byline@>=4.2.1 <5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.1.tgz" + "version": "4.2.2", + "from": "byline@^4.2.1", + "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz" }, "cli": { "version": "0.6.6", - "from": "cli@>=0.6.6 <0.7.0", + "from": "cli@^0.6.6", "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@>=3.2.1 <3.3.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "from": "glob@~ 3.2.1", "dependencies": { "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.3", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "from": "minimatch@0.3", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + "from": "lru-cache@2" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + "from": "sigmund@~1.0.0" } } } @@ -99,8 +135,7 @@ }, "exit": { "version": "0.1.2", - "from": "exit@0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + "from": "exit@~0.1.1" } } }, @@ -116,8 +151,7 @@ "dependencies": { "qs": { "version": "0.6.5", - "from": "qs@0.6.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" + "from": "qs@0.6.5" }, "formidable": { "version": "1.0.14", @@ -131,13 +165,11 @@ }, "pause": { "version": "0.0.1", - "from": "pause@0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + "from": "pause@0.0.1" }, "uid2": { "version": "0.0.2", - "from": "uid2@0.0.2", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" + "from": "uid2@0.0.2" } } }, @@ -148,30 +180,26 @@ "dependencies": { "keypress": { "version": "0.1.0", - "from": "keypress@>=0.1.0 <0.2.0", + "from": "keypress@0.1.x", "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" } } }, "range-parser": { "version": "0.0.4", - "from": "range-parser@0.0.4", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" + "from": "range-parser@0.0.4" }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" }, "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + "from": "cookie@0.1.0" }, "buffer-crc32": { "version": "0.2.1", - "from": "buffer-crc32@0.2.1", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" + "from": "buffer-crc32@0.2.1" }, "fresh": { "version": "0.2.0", @@ -180,8 +208,7 @@ }, "methods": { "version": "0.0.1", - "from": "methods@0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" + "from": "methods@0.0.1" }, "send": { "version": "0.1.4", @@ -190,25 +217,22 @@ "dependencies": { "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + "from": "mime@~1.2.9" } } }, "cookie-signature": { "version": "1.0.1", - "from": "cookie-signature@1.0.1", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" + "from": "cookie-signature@1.0.1" }, "debug": { - "version": "2.2.0", + "version": "2.6.3", "from": "debug@*", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { - "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "version": "0.7.2", + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" } } } @@ -216,18 +240,18 @@ }, "heap": { "version": "0.2.6", - "from": "heap@>=0.2.6 <0.3.0", + "from": "heap@^0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" }, "line-reader": { "version": "0.2.4", - "from": "line-reader@>=0.2.4 <0.3.0", + "from": "line-reader@^0.2.4", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, "logger-sharelatex": { - "version": "1.3.0", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.3.0", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#fd5e037e64178fb3ef5138d39a048cadbacd2ade", + "version": "1.5.0", + "from": "logger-sharelatex@git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.0", + "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#35ac891175f58f87f3950b5808a83ab01579a097", "dependencies": { "bunyan": { "version": "1.5.1", @@ -236,25 +260,21 @@ "dependencies": { "dtrace-provider": { "version": "0.6.0", - "from": "dtrace-provider@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "from": "dtrace-provider@~0.6", "dependencies": { "nan": { - "version": "2.2.0", - "from": "nan@>=2.0.8 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.2.0.tgz" + "version": "2.5.1", + "from": "nan@^2.0.8" } } }, "mv": { "version": "2.1.1", - "from": "mv@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "from": "mv@~2", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@~0.5.1", "dependencies": { "minimist": { "version": "0.0.8", @@ -265,76 +285,67 @@ }, "ncp": { "version": "2.0.0", - "from": "ncp@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" + "from": "ncp@~2.0.0" }, "rimraf": { "version": "2.4.5", - "from": "rimraf@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "from": "rimraf@~2.4.0", "dependencies": { "glob": { "version": "6.0.4", - "from": "glob@>=6.0.1 <7.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "from": "glob@^6.0.1", "dependencies": { "inflight": { - "version": "1.0.4", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "version": "1.0.6", + "from": "inflight@^1.0.4", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "wrappy@1" } } }, "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.3", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { - "version": "3.0.0", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "version": "3.0.3", + "from": "minimatch@2 || 3", "dependencies": { "brace-expansion": { - "version": "1.1.3", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "version": "1.1.6", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { - "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + "version": "0.4.2", + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } } }, "once": { - "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "version": "1.4.0", + "from": "once@^1.3.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "wrappy@1" } } }, "path-is-absolute": { - "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + "version": "1.0.1", + "from": "path-is-absolute@^1.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -343,9 +354,8 @@ } }, "safe-json-stringify": { - "version": "1.0.3", - "from": "safe-json-stringify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.3.tgz" + "version": "1.0.4", + "from": "safe-json-stringify@~1" } } }, @@ -356,23 +366,21 @@ }, "raven": { "version": "0.8.1", - "from": "raven@>=0.8.0 <0.9.0", + "from": "raven@^0.8.0", "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", "dependencies": { "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + "from": "cookie@0.1.0" }, "lsmod": { "version": "0.0.3", - "from": "lsmod@>=0.0.3 <0.1.0", + "from": "lsmod@~0.0.3", "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz" }, "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.1 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + "version": "1.4.8", + "from": "node-uuid@~1.4.1" }, "stack-trace": { "version": "0.0.7", @@ -384,24 +392,21 @@ } }, "metrics-sharelatex": { - "version": "1.3.0", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.4.0", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#a2d156e97b7ab51fca5f36c0838a9a808416dfe8", + "version": "1.7.1", + "from": "metrics-sharelatex@git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", + "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#166961924c599b1f9468f2e17846fa2a9d12372d", "dependencies": { "lynx": { "version": "0.1.1", - "from": "lynx@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "from": "lynx@~0.1.1", "dependencies": { "mersenne": { "version": "0.0.3", - "from": "mersenne@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" + "from": "mersenne@~0.0.3" }, "statsd-parser": { "version": "0.0.4", - "from": "statsd-parser@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + "from": "statsd-parser@~0.0.4" } } }, @@ -412,44 +417,42 @@ }, "underscore": { "version": "1.6.0", - "from": "underscore@>=1.6.0 <1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + "from": "underscore@~1.6.0" } } }, "mongo-uri": { "version": "0.1.2", - "from": "mongo-uri@>=0.1.2 <0.2.0", + "from": "mongo-uri@^0.1.2", "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" }, "mongojs": { "version": "1.4.1", - "from": "mongojs@>=1.4.1 <2.0.0", + "from": "mongojs@^1.4.1", "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", "dependencies": { "each-series": { "version": "1.0.0", - "from": "each-series@>=1.0.0 <2.0.0", + "from": "each-series@^1.0.0", "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" }, "mongodb-core": { - "version": "1.3.10", - "from": "mongodb-core@>=1.2.8 <2.0.0", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.10.tgz", + "version": "1.3.22-alpha4", + "from": "mongodb-core@^1.2.8", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.22-alpha4.tgz", "dependencies": { "require_optional": { "version": "1.0.0", - "from": "require_optional@>=1.0.0 <1.1.0", + "from": "require_optional@~1.0.0", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", "dependencies": { "semver": { - "version": "5.1.0", - "from": "semver@>=5.1.0 <6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz" + "version": "5.3.0", + "from": "semver@^5.1.0" }, "resolve-from": { "version": "2.0.0", - "from": "resolve-from@>=2.0.0 <3.0.0", + "from": "resolve-from@^2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" } } @@ -457,117 +460,107 @@ } }, "once": { - "version": "1.3.3", - "from": "once@>=1.3.2 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "version": "1.4.0", + "from": "once@^1.3.2", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "wrappy@1" } } }, "parse-mongo-url": { "version": "1.1.1", - "from": "parse-mongo-url@>=1.1.0 <2.0.0", + "from": "parse-mongo-url@^1.1.0", "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" }, "pump": { - "version": "1.0.1", - "from": "pump@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.1.tgz", + "version": "1.0.2", + "from": "pump@^1.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", "dependencies": { "end-of-stream": { - "version": "1.1.0", - "from": "end-of-stream@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz" + "version": "1.4.0", + "from": "end-of-stream@^1.1.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz" } } }, "readable-stream": { - "version": "2.0.6", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "version": "2.2.6", + "from": "readable-stream@^2.0.2", "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@^1.0.0" + }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "from": "core-util-is@~1.0.0" }, "isarray": { "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "from": "isarray@~1.0.0" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "process-nextick-args": { - "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + "version": "1.0.7", + "from": "process-nextick-args@~1.0.6" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "from": "string_decoder@~0.10.x" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "from": "util-deprecate@~1.0.1" } } }, "thunky": { "version": "0.1.0", - "from": "thunky@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + "from": "thunky@^0.1.0" }, "to-mongodb-core": { "version": "2.0.0", - "from": "to-mongodb-core@>=2.0.0 <3.0.0", + "from": "to-mongodb-core@^2.0.0", "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" }, "xtend": { "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", + "from": "xtend@^4.0.0", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" } } }, "redis": { "version": "0.10.3", - "from": "redis@>=0.10.1 <0.11.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" + "from": "redis@~0.10.1" }, "redis-sharelatex": { "version": "0.0.9", - "from": "redis-sharelatex@>=0.0.9 <0.1.0", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-0.0.9.tgz", + "from": "redis-sharelatex@~0.0.9", "dependencies": { "chai": { "version": "1.9.1", "from": "chai@1.9.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", "dependencies": { "assertion-error": { "version": "1.0.0", - "from": "assertion-error@1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" + "from": "assertion-error@1.0.0" }, "deep-eql": { "version": "0.1.3", "from": "deep-eql@0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "dependencies": { "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + "from": "type-detect@0.1.1" } } } @@ -576,109 +569,91 @@ "coffee-script": { "version": "1.8.0", "from": "coffee-script@1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" } } }, "grunt-contrib-coffee": { "version": "0.11.1", "from": "grunt-contrib-coffee@0.11.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "from": "coffee-script@~1.7.0", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "from": "chalk@~0.5.0", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + "from": "ansi-styles@^1.1.0" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "from": "escape-string-regexp@^1.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "from": "has-ansi@^0.1.0", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "from": "ansi-regex@^0.2.1" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "from": "strip-ansi@^0.3.0", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "from": "ansi-regex@^0.2.1" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + "from": "supports-color@^0.2.0" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "from": "lodash@~2.4.1" } } }, "grunt-mocha-test": { "version": "0.12.0", "from": "grunt-mocha-test@0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", "dependencies": { "hooker": { "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + "from": "hooker@~0.2.3" }, "fs-extra": { "version": "0.11.1", - "from": "fs-extra@>=0.11.1 <0.12.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "from": "fs-extra@~0.11.1", "dependencies": { "ncp": { "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + "from": "ncp@^0.6.0" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@^0.5.0", "dependencies": { "minimist": { "version": "0.0.8", @@ -688,77 +663,78 @@ } }, "jsonfile": { - "version": "2.2.3", - "from": "jsonfile@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.2.3.tgz" + "version": "2.4.0", + "from": "jsonfile@^2.0.0", + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "from": "graceful-fs@^4.1.6" + } + } }, "rimraf": { - "version": "2.5.2", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "version": "2.6.1", + "from": "rimraf@^2.2.8", "dependencies": { "glob": { - "version": "7.0.3", - "from": "glob@>=7.0.0 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "version": "7.1.1", + "from": "glob@^7.0.5", "dependencies": { + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@^1.0.0" + }, "inflight": { - "version": "1.0.4", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "version": "1.0.6", + "from": "inflight@^1.0.4", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "wrappy@1" } } }, "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.3", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { - "version": "3.0.0", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.0.tgz", + "version": "3.0.3", + "from": "minimatch@^3.0.2", "dependencies": { "brace-expansion": { - "version": "1.1.3", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.3.tgz", + "version": "1.1.6", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { - "version": "0.3.0", - "from": "balanced-match@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.3.0.tgz" + "version": "0.4.2", + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } } }, "once": { - "version": "1.3.3", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "version": "1.4.0", + "from": "once@^1.3.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { - "version": "1.0.1", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + "version": "1.0.2", + "from": "wrappy@1" } } }, "path-is-absolute": { - "version": "1.0.0", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + "version": "1.0.1", + "from": "path-is-absolute@^1.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -780,8 +756,7 @@ }, "growl": { "version": "1.8.1", - "from": "growl@>=1.8.0 <1.9.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" + "from": "growl@1.8.x" }, "jade": { "version": "0.26.3", @@ -795,8 +770,7 @@ }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + "from": "mkdirp@0.3.0" } } }, @@ -806,53 +780,46 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { - "version": "2.2.0", + "version": "2.6.3", "from": "debug@*", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { - "version": "0.7.1", - "from": "ms@0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + "version": "0.7.2", + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "from": "minimatch@~0.2.11", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + "from": "lru-cache@2" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + "from": "sigmund@~1.0.0" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + "from": "graceful-fs@~2.0.0" }, "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.3", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" } } } @@ -870,8 +837,7 @@ "dependencies": { "redis": { "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" + "from": "redis@0.11.x" }, "q": { "version": "0.9.2", @@ -883,7 +849,6 @@ "sandboxed-module": { "version": "1.0.1", "from": "sandboxed-module@1.0.1", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", @@ -892,32 +857,27 @@ }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + "from": "stack-trace@0.0.9" } } }, "sinon": { "version": "1.10.3", "from": "sinon@1.10.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "formatio@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", + "from": "formatio@~1.0", "dependencies": { "samsam": { "version": "1.1.3", - "from": "samsam@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" + "from": "samsam@~1.1" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1.0.0", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "from": "util@>=0.10.3 <1", "dependencies": { "inherits": { "version": "2.0.1", @@ -932,48 +892,49 @@ }, "request": { "version": "2.33.0", - "from": "request@>=2.33.0 <2.34.0", + "from": "request@~2.33.0", "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", "dependencies": { "qs": { "version": "0.6.6", - "from": "qs@>=0.6.0 <0.7.0", + "from": "qs@~0.6.0", "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@>=5.0.0 <5.1.0", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + "from": "json-stringify-safe@~5.0.0" }, "forever-agent": { "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + "from": "forever-agent@~0.5.0" }, "node-uuid": { - "version": "1.4.7", - "from": "node-uuid@>=1.4.0 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" + "version": "1.4.8", + "from": "node-uuid@~1.4.0" }, "mime": { "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + "from": "mime@~1.2.9" }, "tough-cookie": { - "version": "2.2.2", + "version": "2.3.2", "from": "tough-cookie@>=0.12.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "punycode@^1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } }, "form-data": { "version": "0.1.4", - "from": "form-data@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "from": "form-data@~0.1.0", "dependencies": { "combined-stream": { "version": "0.0.7", - "from": "combined-stream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "from": "combined-stream@~0.0.4", "dependencies": { "delayed-stream": { "version": "0.0.5", @@ -984,25 +945,22 @@ }, "async": { "version": "0.9.2", - "from": "async@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" + "from": "async@~0.9.0" } } }, "tunnel-agent": { "version": "0.3.0", - "from": "tunnel-agent@>=0.3.0 <0.4.0", + "from": "tunnel-agent@~0.3.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" }, "http-signature": { "version": "0.10.1", - "from": "http-signature@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "from": "http-signature@~0.10.0", "dependencies": { "assert-plus": { "version": "0.1.5", - "from": "assert-plus@>=0.1.5 <0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + "from": "assert-plus@^0.1.5" }, "asn1": { "version": "0.1.11", @@ -1018,100 +976,91 @@ }, "oauth-sign": { "version": "0.3.0", - "from": "oauth-sign@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" + "from": "oauth-sign@~0.3.0" }, "hawk": { "version": "1.0.0", - "from": "hawk@>=1.0.0 <1.1.0", + "from": "hawk@~1.0.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "dependencies": { "hoek": { "version": "0.9.1", - "from": "hoek@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + "from": "hoek@0.9.x" }, "boom": { "version": "0.4.2", - "from": "boom@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + "from": "boom@0.4.x" }, "cryptiles": { "version": "0.2.2", - "from": "cryptiles@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + "from": "cryptiles@0.2.x" }, "sntp": { "version": "0.2.4", - "from": "sntp@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + "from": "sntp@0.2.x" } } }, "aws-sign2": { "version": "0.5.0", - "from": "aws-sign2@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + "from": "aws-sign2@~0.5.0" } } }, "s3-streams": { "version": "0.3.0", - "from": "s3-streams@>=0.3.0 <0.4.0", + "from": "s3-streams@^0.3.0", "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "dependencies": { "lodash": { "version": "3.10.1", - "from": "lodash@>=3.9.3 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + "from": "lodash@^3.9.3" }, "readable-stream": { - "version": "2.0.5", - "from": "readable-stream@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.5.tgz", + "version": "2.2.6", + "from": "readable-stream@^2.0.0", "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@^1.0.0" + }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "from": "core-util-is@~1.0.0" }, "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version": "1.0.0", + "from": "isarray@~1.0.0" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "process-nextick-args": { - "version": "1.0.6", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.6.tgz" + "version": "1.0.7", + "from": "process-nextick-args@~1.0.6" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "from": "string_decoder@~0.10.x" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "from": "util-deprecate@~1.0.1" } } }, "bluebird": { - "version": "2.10.2", - "from": "bluebird@>=2.9.27 <3.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz" + "version": "2.11.0", + "from": "bluebird@^2.9.27", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz" } } }, "settings-sharelatex": { "version": "1.0.0", - "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "from": "settings-sharelatex@git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", "dependencies": { "coffee-script": { @@ -1123,8 +1072,7 @@ }, "underscore": { "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + "from": "underscore@~1.7.0" } } } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 9f18cbcc67..879218f780 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -16,7 +16,7 @@ "mongojs": "^1.4.1", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.0", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", "request": "~2.33.0", "redis-sharelatex": "~0.0.9", "redis": "~0.10.1", From b10f3adf2b507d91deb870ae1816eae9491413c1 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 31 Mar 2017 11:33:25 +0100 Subject: [PATCH 364/549] mock out metrics in tests --- .../track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee | 1 + .../test/unit/coffee/PackManager/PackManagerTests.coffee | 1 + 2 files changed, 2 insertions(+) diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 6c3cf774a9..2da3fdaaef 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -26,6 +26,7 @@ describe "MongoAWS", -> "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "JSONStream": @JSONStream = {} "readline-stream": @readline = sinon.stub() + './Metrics': {inc: ()->} @project_id = ObjectId().toString() @doc_id = ObjectId().toString() diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index d1d7d898d6..63309a1ff0 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -20,6 +20,7 @@ describe "PackManager", -> "./LockManager" : {} "./MongoAWS": {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } + './Metrics': {inc: ()->} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From b0b05d1e1b02cb9a8a4534be8b820f382cee0e39 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 31 Mar 2017 15:17:13 +0100 Subject: [PATCH 365/549] improve error handling of releaseLock ported from docupdater --- .../app/coffee/LockManager.coffee | 9 +++++++- .../LockManager/LockManagerTests.coffee | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 89a194e5de..7e7468d1c2 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -3,6 +3,7 @@ redis = require("redis-sharelatex") rclient = redis.createClient(Settings.redis.web) os = require "os" crypto = require "crypto" +logger = require "logger-sharelatex" HOST = os.hostname() PID = process.pid @@ -57,7 +58,13 @@ module.exports = LockManager = callback err, true releaseLock: (key, lockValue, callback) -> - rclient.eval LockManager.unlockScript, 1, key, lockValue, callback + rclient.eval LockManager.unlockScript, 1, key, lockValue, (err, result) -> + if err? + return callback(err) + if result? and result isnt 1 # successful unlock should release exactly one key + logger.error {key:key, lockValue:lockValue, redis_err:err, redis_result:result}, "unlocking error" + return callback(new Error("tried to release timed out lock")) + callback(err,result) runWithLock: (key, runner = ( (releaseLock = (error) ->) -> ), callback = ( (error) -> )) -> LockManager.getLock key, (error, lockValue) -> diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index d6744137ed..fd141ef426 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -15,6 +15,7 @@ describe "LockManager", -> createClient: () => @rclient = auth: sinon.stub() "settings-sharelatex": @Settings + "logger-sharelatex": {error: ->} @key = "lock-key" @callback = sinon.stub() @@ -174,3 +175,24 @@ describe "LockManager", -> it "should call the callback with the error", -> @callback.calledWith(@error).should.equal true + + describe "releaseLock", -> + describe "when the lock is current", -> + beforeEach -> + @rclient.eval = sinon.stub().yields(null, 1) + @LockManager.releaseLock @key, @lockValue, @callback + + it 'should clear the data from redis', -> + @rclient.eval.calledWith(@LockManager.unlockScript, 1, @key, @lockValue).should.equal true + + it 'should call the callback', -> + @callback.called.should.equal true + + describe "when the lock has expired", -> + beforeEach -> + @rclient.eval = sinon.stub().yields(null, 0) + @LockManager.releaseLock @key, @lockValue, @callback + + it 'should return an error if the lock has expired', -> + @callback.calledWith(new Error("tried to release timed out lock")).should.equal true + From 7237467cb4107f287875303cb4dd5fa8a7bf1dea Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 3 Apr 2017 11:06:23 +0100 Subject: [PATCH 366/549] use requestretry to work around web api timeouts --- .../app/coffee/WebApiManager.coffee | 5 +- services/track-changes/npm-shrinkwrap.json | 301 ++++++++++++++++++ services/track-changes/package.json | 1 + .../WebApiManager/WebApiManagerTests.coffee | 2 +- 4 files changed, 306 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 0067c87d03..2a7bcdd2ce 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -1,4 +1,4 @@ -request = require "request" +request = require "requestretry" # allow retry on error https://github.com/FGRibreau/node-request-retry logger = require "logger-sharelatex" Settings = require "settings-sharelatex" @@ -15,6 +15,7 @@ module.exports = WebApiManager = request.get { url: "#{Settings.apis.web.url}#{url}" timeout: MAX_HTTP_REQUEST_LENGTH + maxAttempts: 2 # for node-request-retry auth: user: Settings.apis.web.user pass: Settings.apis.web.pass @@ -28,7 +29,7 @@ module.exports = WebApiManager = if res.statusCode >= 200 and res.statusCode < 300 return callback null, body else - error = new Error("web returned a non-success status code: #{res.statusCode}") + error = new Error("web returned a non-success status code: #{res.statusCode} (attempts: #{res.attempts})") callback error getUserInfo: (user_id, callback = (error, userInfo) ->) -> diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index ad3ba27a7f..85d57c62b8 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1360,6 +1360,307 @@ } } }, + "requestretry": { + "version": "1.12.0", + "from": "requestretry@latest", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.12.0.tgz", + "dependencies": { + "extend": { + "version": "3.0.0", + "from": "extend@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.15.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "request": { + "version": "2.81.0", + "from": "request@>=2.74.0 <3.0.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "caseless@>=0.12.0 <0.13.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + } + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.2", + "from": "form-data@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "dependencies": { + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + } + } + }, + "har-validator": { + "version": "4.2.1", + "from": "har-validator@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "dependencies": { + "ajv": { + "version": "4.11.5", + "from": "ajv@>=4.9.1 <5.0.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "dependencies": { + "co": { + "version": "4.6.0", + "from": "co@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "dependencies": { + "jsonify": { + "version": "0.0.0", + "from": "jsonify@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "har-schema@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@>=3.1.3 <3.2.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "jsprim": { + "version": "1.4.0", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "extsprintf": { + "version": "1.0.2", + "from": "extsprintf@1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "verror": { + "version": "1.3.6", + "from": "verror@1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + } + } + }, + "sshpk": { + "version": "1.11.0", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" + }, + "getpass": { + "version": "0.1.6", + "from": "getpass@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@>=0.14.0 <0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "jodid25519": { + "version": "1.0.2", + "from": "jodid25519@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.1 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "mime-types": { + "version": "2.1.15", + "from": "mime-types@>=2.1.7 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "dependencies": { + "mime-db": { + "version": "1.27.0", + "from": "mime-db@>=1.27.0 <1.28.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "performance-now@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "qs@>=6.4.0 <6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "safe-buffer": { + "version": "5.0.1", + "from": "safe-buffer@>=5.0.1 <6.0.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.0.1", + "from": "uuid@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + } + } + }, + "when": { + "version": "3.7.8", + "from": "when@>=3.7.7 <4.0.0", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" + } + } + }, "s3-streams": { "version": "0.3.0", "from": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 85fc9dff38..bc2a9f3c4d 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -18,6 +18,7 @@ "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", "request": "~2.33.0", + "requestretry": "^1.12.0", "redis-sharelatex": "~0.0.9", "redis": "~0.10.1", "underscore": "~1.7.0", diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee index 6af9cf5914..fe33d08a4a 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee @@ -8,7 +8,7 @@ SandboxedModule = require('sandboxed-module') describe "WebApiManager", -> beforeEach -> @WebApiManager = SandboxedModule.require modulePath, requires: - "request": @request = {} + "requestretry": @request = {} "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } 'settings-sharelatex': @settings = apis: From 9a9f0f5c4d9d2a0e3e45b65481dd530e9f302b37 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 3 Apr 2017 15:17:29 +0100 Subject: [PATCH 367/549] Remove the Metrics module, use metrics-sharelatex --- services/track-changes/app/coffee/Metrics.coffee | 1 - services/track-changes/app/coffee/MongoAWS.coffee | 2 +- services/track-changes/app/coffee/PackManager.coffee | 2 +- .../track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee | 2 +- .../test/unit/coffee/PackManager/PackManagerTests.coffee | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 services/track-changes/app/coffee/Metrics.coffee diff --git a/services/track-changes/app/coffee/Metrics.coffee b/services/track-changes/app/coffee/Metrics.coffee deleted file mode 100644 index c0e7d12731..0000000000 --- a/services/track-changes/app/coffee/Metrics.coffee +++ /dev/null @@ -1 +0,0 @@ -module.exports = require "metrics-sharelatex" diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 42f41863a6..8a0a065317 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -6,7 +6,7 @@ S3S = require 's3-streams' JSONStream = require "JSONStream" ReadlineStream = require "byline" zlib = require "zlib" -Metrics = require "./Metrics" +Metrics = require "metrics-sharelatex" DAYS = 24 * 3600 * 1000 # one day in milliseconds diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index d60500ce56..d8069da518 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -4,7 +4,7 @@ _ = require "underscore" logger = require "logger-sharelatex" LockManager = require "./LockManager" MongoAWS = require "./MongoAWS" -Metrics = require "./Metrics" +Metrics = require "metrics-sharelatex" ProjectIterator = require "./ProjectIterator" # Sharejs operations are stored in a 'pack' object diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 2da3fdaaef..2dc289d89a 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -26,7 +26,7 @@ describe "MongoAWS", -> "./mongojs" : { db: @db = {}, ObjectId: ObjectId } "JSONStream": @JSONStream = {} "readline-stream": @readline = sinon.stub() - './Metrics': {inc: ()->} + 'metrics-sharelatex': {inc: ()->} @project_id = ObjectId().toString() @doc_id = ObjectId().toString() diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index 63309a1ff0..29c9cf6d3d 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -20,7 +20,7 @@ describe "PackManager", -> "./LockManager" : {} "./MongoAWS": {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } - './Metrics': {inc: ()->} + 'metrics-sharelatex': {inc: ()->} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From d9ed026d9174916c58e298cb863cbc1ace731d1b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 12 Apr 2017 16:34:28 +0100 Subject: [PATCH 368/549] simple flush for all projects does not work with redis cluster, only single redis --- services/track-changes/app.coffee | 2 ++ .../app/coffee/HttpController.coffee | 12 ++++++++++++ .../app/coffee/RedisManager.coffee | 9 +++++++++ .../app/coffee/UpdatesManager.coffee | 17 +++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 0a43cd1503..9cd9edee75 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -50,6 +50,8 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post '/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory app.post '/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory +app.post '/flush/all', HttpController.flushAll + packWorker = null # use a single packing worker app.post "/pack", (req, res, next) -> diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index eecc618330..44ba8d48ba 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -22,6 +22,18 @@ module.exports = HttpController = return next(error) if error? res.send 204 + flushAll: (req, res, next = (error) ->) -> + logger.log "flushing all projects" + UpdatesManager.flushAll (error, result) -> + return next(error) if error? + {failed, succeeded} = result + status = "#{succeeded.length} succeeded, #{failed.length} failed" + if failed.length > 0 + logger.log {failed: failed, succeeded: succeeded}, "error flushing projects" + res.status(500).send "#{status}\nfailed to flush:\n#{failed.join('\n')}\n" + else + res.status(200).send "#{status}\nflushed all #{succeeded.length} projects\n" + checkDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index a634bbfed9..25719e753f 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -32,3 +32,12 @@ module.exports = RedisManager = getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> rclient.smembers docsWithHistoryOpsKey(project_id), callback + + # this will only work on single node redis, not redis cluster + getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> + rclient.keys docsWithHistoryOpsKey("*"), (error, project_keys) -> + return callback(error) if error? + project_ids = for key in project_keys + [prefix, project_id] = key.split(":") + project_id + callback(error, project_ids) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index f01681e5f0..e1a7b086d8 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -144,6 +144,23 @@ module.exports = UpdatesManager = UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb async.parallelLimit jobs, 5, callback + # flush all outstanding changes + flushAll: (callback = (error, result) ->) -> + RedisManager.getProjectIdsWithHistoryOps (error, project_ids) -> + return callback(error) if error? + logger.log {count: project_ids?.length, project_ids: project_ids}, "found projects" + jobs = [] + for project_id in project_ids + do (project_id) -> + jobs.push (cb) -> + UpdatesManager.processUncompressedUpdatesForProject project_id, (err) -> + return cb(null, {failed: err?, project_id: project_id}) + async.series jobs, (error, result) -> + return callback(error) if error? + failedProjects = (x.project_id for x in result when x.failed) + succeededProjects = (x.project_id for x in result when not x.failed) + callback(null, {failed: failedProjects, succeeded: succeededProjects}) + getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? From 1ec9f87542d615fb5b5ce43d09bff2ff81e958dd Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 12 Apr 2017 16:47:34 +0100 Subject: [PATCH 369/549] update timeouts to allow for 5 second retry delay now using requestretry module, which has a five second delay between retries. Need to increase the timeout on the health checks to allow for this, as the timeout is currently shorter than the retry delay. --- services/track-changes/app/coffee/HealthChecker.coffee | 4 ++-- services/track-changes/app/coffee/WebApiManager.coffee | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/HealthChecker.coffee b/services/track-changes/app/coffee/HealthChecker.coffee index ff5caf8efa..76165c9954 100644 --- a/services/track-changes/app/coffee/HealthChecker.coffee +++ b/services/track-changes/app/coffee/HealthChecker.coffee @@ -22,7 +22,7 @@ module.exports = else cb() (cb)-> - request.post {url:"#{url}/flush", timeout:3000}, (err, res, body) -> + request.post {url:"#{url}/flush", timeout:10000}, (err, res, body) -> if err? logger.err err:err, project_id:project_id, "error flushing for health check" cb(err) @@ -31,7 +31,7 @@ module.exports = else cb() (cb)-> - request.get {url:"#{url}/updates", timeout:3000}, (err, res, body)-> + request.get {url:"#{url}/updates", timeout:10000}, (err, res, body)-> if err? logger.err err:err, project_id:project_id, "error getting updates for health check" cb(err) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index 2a7bcdd2ce..aee8d431dd 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -3,7 +3,7 @@ logger = require "logger-sharelatex" Settings = require "settings-sharelatex" # Don't let HTTP calls hang for a long time -MAX_HTTP_REQUEST_LENGTH = 30000 # 30 seconds +MAX_HTTP_REQUEST_LENGTH = 15000 # 15 seconds # DEPRECATED! This method of getting user details via track-changes is deprecated # in the way we lay out our services. From 9ce6d77cca93bc33fa567dddaedb84056d83a388 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 13 Apr 2017 11:31:45 +0100 Subject: [PATCH 370/549] add check for dangling updates --- services/track-changes/app.coffee | 1 + .../app/coffee/HttpController.coffee | 10 ++++++++++ .../app/coffee/RedisManager.coffee | 18 +++++++++++++++--- .../app/coffee/UpdatesManager.coffee | 14 ++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 9cd9edee75..43cddca498 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -51,6 +51,7 @@ app.post '/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory app.post '/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory app.post '/flush/all', HttpController.flushAll +app.post '/check/dangling', HttpController.checkDanglingUpdates packWorker = null # use a single packing worker diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 44ba8d48ba..0b59a6e1ec 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -34,6 +34,16 @@ module.exports = HttpController = else res.status(200).send "#{status}\nflushed all #{succeeded.length} projects\n" + checkDanglingUpdates: (req, res, next = (error) ->) -> + logger.log "checking dangling updates" + UpdatesManager.getDanglingUpdates (error, result) -> + return next(error) if error? + if result.length > 0 + logger.log {dangling: result}, "found dangling updates" + res.status(500).send "dangling updates:\n#{result.join('\n')}\n" + else + res.status(200).send "no dangling updates found\n" + checkDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 25719e753f..f29c9bc7ce 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -33,11 +33,23 @@ module.exports = RedisManager = getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> rclient.smembers docsWithHistoryOpsKey(project_id), callback + # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b + _extractIds: (keyList) -> + ids = (key.split(":")[1] for key in keyList) + return ids + # this will only work on single node redis, not redis cluster getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> rclient.keys docsWithHistoryOpsKey("*"), (error, project_keys) -> return callback(error) if error? - project_ids = for key in project_keys - [prefix, project_id] = key.split(":") - project_id + project_ids = RedisManager._extractIds project_keys callback(error, project_ids) + + # this will only work on single node redis, not redis cluster + getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) -> + # return all the docids, to find dangling history entries after + # everything is flushed. + rclient.keys rawUpdatesKey("*"), (error, doc_keys) -> + return callback(error) if error? + doc_ids = RedisManager._extractIds doc_keys + callback(error, doc_ids) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index e1a7b086d8..ca79d26d59 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -159,8 +159,22 @@ module.exports = UpdatesManager = return callback(error) if error? failedProjects = (x.project_id for x in result when x.failed) succeededProjects = (x.project_id for x in result when not x.failed) + RedisManager.getAllDocIdsWithHistoryOps (error, doc_ids) -> callback(null, {failed: failedProjects, succeeded: succeededProjects}) + getDanglingUpdates: (callback = (error, doc_ids) ->) -> + RedisManager.getAllDocIdsWithHistoryOps (error, all_doc_ids) -> + return callback(error) if error? + RedisManager.getProjectIdsWithHistoryOps (error, all_project_ids) -> + return callback(error) if error? + # function to get doc_ids for each project + task = (cb) -> async.concatSeries all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb + # find the dangling doc ids + task (error, project_doc_ids) -> + dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids) + logger.log {all_doc_ids: all_doc_ids, all_project_ids: all_project_ids, project_doc_ids: project_doc_ids, dangling_doc_ids: dangling_doc_ids}, "checking for dangling doc ids" + callback(null, dangling_doc_ids) + getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? From b5b61b98d0d8e19d14f36cd1f4c6ef622644fcac Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 19 Apr 2017 15:39:33 +0100 Subject: [PATCH 371/549] avoid blocking when fetching redis keys use scan instead of keys method --- .../app/coffee/RedisManager.coffee | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index f29c9bc7ce..b58b99f11f 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -33,6 +33,23 @@ module.exports = RedisManager = getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> rclient.smembers docsWithHistoryOpsKey(project_id), callback + # iterate over keys asynchronously using redis scan (non-blocking) + _getKeys: (pattern, callback) -> + cursor = 0 # redis iterator + keySet = {} # use hash to avoid duplicate results + # scan over all keys looking for pattern + doIteration = (cb) -> + rclient.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) -> + return callback(error) if error? + [cursor, keys] = reply + for key in keys + keySet[key] = true + if cursor == '0' # note redis returns string result not numeric + return callback(null, Object.keys(keySet)) + else + doIteration() + doIteration() + # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b _extractIds: (keyList) -> ids = (key.split(":")[1] for key in keyList) @@ -40,7 +57,7 @@ module.exports = RedisManager = # this will only work on single node redis, not redis cluster getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> - rclient.keys docsWithHistoryOpsKey("*"), (error, project_keys) -> + RedisManager._getKeys docsWithHistoryOpsKey("*"), (error, project_keys) -> return callback(error) if error? project_ids = RedisManager._extractIds project_keys callback(error, project_ids) @@ -49,7 +66,7 @@ module.exports = RedisManager = getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) -> # return all the docids, to find dangling history entries after # everything is flushed. - rclient.keys rawUpdatesKey("*"), (error, doc_keys) -> + RedisManager._getKeys rawUpdatesKey("*"), (error, doc_keys) -> return callback(error) if error? doc_ids = RedisManager._extractIds doc_keys callback(error, doc_ids) From 976f19f763a297c6bac083c31036973317b138c3 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 20 Apr 2017 10:14:44 +0100 Subject: [PATCH 372/549] remove spurious call to getAllDocIdsWithHistoryOps --- services/track-changes/app/coffee/UpdatesManager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index ca79d26d59..3c12b2a713 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -159,7 +159,6 @@ module.exports = UpdatesManager = return callback(error) if error? failedProjects = (x.project_id for x in result when x.failed) succeededProjects = (x.project_id for x in result when not x.failed) - RedisManager.getAllDocIdsWithHistoryOps (error, doc_ids) -> callback(null, {failed: failedProjects, succeeded: succeededProjects}) getDanglingUpdates: (callback = (error, doc_ids) ->) -> From 43f51355b26d69be2cafc1fa14e06e08e3e84d4d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 20 Apr 2017 11:01:46 +0100 Subject: [PATCH 373/549] add limit parameter to flushAll --- .../track-changes/app/coffee/HttpController.coffee | 12 ++++++++---- .../track-changes/app/coffee/UpdatesManager.coffee | 7 ++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 0b59a6e1ec..4f99cdfda3 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -23,12 +23,16 @@ module.exports = HttpController = res.send 204 flushAll: (req, res, next = (error) ->) -> - logger.log "flushing all projects" - UpdatesManager.flushAll (error, result) -> + # limit on projects to flush or -1 for all (default) + limit = if req.query.limit? then parseInt(req.query.limit, 10) else -1 + logger.log {limit: limit}, "flushing all projects" + UpdatesManager.flushAll limit, (error, result) -> return next(error) if error? - {failed, succeeded} = result + {failed, succeeded, all} = result status = "#{succeeded.length} succeeded, #{failed.length} failed" - if failed.length > 0 + if limit == 0 + res.status(200).send "#{status}\nwould flush:\n#{all.join('\n')}\n" + else if failed.length > 0 logger.log {failed: failed, succeeded: succeeded}, "error flushing projects" res.status(500).send "#{status}\nfailed to flush:\n#{failed.join('\n')}\n" else diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 3c12b2a713..f3571dc16d 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -145,12 +145,13 @@ module.exports = UpdatesManager = async.parallelLimit jobs, 5, callback # flush all outstanding changes - flushAll: (callback = (error, result) ->) -> + flushAll: (limit, callback = (error, result) ->) -> RedisManager.getProjectIdsWithHistoryOps (error, project_ids) -> return callback(error) if error? logger.log {count: project_ids?.length, project_ids: project_ids}, "found projects" jobs = [] - for project_id in project_ids + selectedProjects = if limit < 0 then project_ids else project_ids[0...limit] + for project_id in selectedProjects do (project_id) -> jobs.push (cb) -> UpdatesManager.processUncompressedUpdatesForProject project_id, (err) -> @@ -159,7 +160,7 @@ module.exports = UpdatesManager = return callback(error) if error? failedProjects = (x.project_id for x in result when x.failed) succeededProjects = (x.project_id for x in result when not x.failed) - callback(null, {failed: failedProjects, succeeded: succeededProjects}) + callback(null, {failed: failedProjects, succeeded: succeededProjects, all: project_ids}) getDanglingUpdates: (callback = (error, doc_ids) ->) -> RedisManager.getAllDocIdsWithHistoryOps (error, all_doc_ids) -> From 9eb36f9692b05d672c4a4d9db4747239e8abf463 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 20 Apr 2017 11:55:32 +0100 Subject: [PATCH 374/549] improve log message for flushing --- services/track-changes/app/coffee/HttpController.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 4f99cdfda3..9ede86f3ee 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -36,7 +36,7 @@ module.exports = HttpController = logger.log {failed: failed, succeeded: succeeded}, "error flushing projects" res.status(500).send "#{status}\nfailed to flush:\n#{failed.join('\n')}\n" else - res.status(200).send "#{status}\nflushed all #{succeeded.length} projects\n" + res.status(200).send "#{status}\nflushed #{succeeded.length} projects of #{all.length}\n" checkDanglingUpdates: (req, res, next = (error) ->) -> logger.log "checking dangling updates" From 2a68b81cc1eb3236fb1c35971c20ade0b6c24b17 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 20 Apr 2017 14:32:23 +0100 Subject: [PATCH 375/549] randomise order of flush --- services/track-changes/app/coffee/UpdatesManager.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index f3571dc16d..48423a39ec 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -150,6 +150,7 @@ module.exports = UpdatesManager = return callback(error) if error? logger.log {count: project_ids?.length, project_ids: project_ids}, "found projects" jobs = [] + project_ids = _.shuffle project_ids # randomise to avoid hitting same projects each time selectedProjects = if limit < 0 then project_ids else project_ids[0...limit] for project_id in selectedProjects do (project_id) -> From 33fb1ddf578647b92ce235f469c32fd2d9327e1b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 20 Apr 2017 16:58:26 +0100 Subject: [PATCH 376/549] script to flush dangling ops --- services/track-changes/fixdangling.coffee | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 services/track-changes/fixdangling.coffee diff --git a/services/track-changes/fixdangling.coffee b/services/track-changes/fixdangling.coffee new file mode 100644 index 0000000000..f45bcecf7b --- /dev/null +++ b/services/track-changes/fixdangling.coffee @@ -0,0 +1,54 @@ +Settings = require "settings-sharelatex" +logger = require "logger-sharelatex" +TrackChangesLogger = logger.initialize("track-changes").logger +async = require "async" +fs = require "fs" +request = require "request" +cli = require "cli" + +mongojs = require "mongojs" +bson = require "bson" +db = mongojs(Settings.mongo.url, ["docs"]) +ObjectId = mongojs.ObjectId + +options = cli.parse({ + port: ['p', 'port number for track changes', 'number'], + force: ['f', 'actually make the fix'] +}); + +if cli.args.length < 1 + console.log "fixdangling -p PORT file_of_doc_ids" + process.exit() + +file = cli.args.pop() +doc_ids = fs.readFileSync(file).toString().trim().split("\n") + +missing = 0 +errored = 0 +success = 0 + +fixDangling = (doc_id, callback) -> + # look up project id from doc id + db.docs.find {_id:ObjectId(doc_id)}, {project_id:1}, (err, result) -> + console.log "doc_id", doc_id, "err", err, "result", result + if err? + errored++ + return callback() + if !result? or result.length == 0 + missing++ + return callback() + project_id = result[0] + console.log "found project_id", project_id, "for doc_id", doc_id + url = "http://localhost:#{options.port}/project/#{project_id}/doc/#{doc_id}/flush" + if options.force + request.post url, (err, response, body) -> + if err? then errored++ else success++ + callback() + else + console.log "URL:", url + success++ + callback() + +async.eachSeries doc_ids, fixDangling, (err) -> + console.log "final result", err, "missing", missing, "errored", errored, "success", success + db.close() From a7ff15ec7d4c4e9b8933a541f8317e5844e78317 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 24 Apr 2017 09:49:11 +0100 Subject: [PATCH 377/549] remove logging, fix query --- services/track-changes/fixdangling.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/fixdangling.coffee b/services/track-changes/fixdangling.coffee index f45bcecf7b..1e656b6fe8 100644 --- a/services/track-changes/fixdangling.coffee +++ b/services/track-changes/fixdangling.coffee @@ -30,14 +30,14 @@ success = 0 fixDangling = (doc_id, callback) -> # look up project id from doc id db.docs.find {_id:ObjectId(doc_id)}, {project_id:1}, (err, result) -> - console.log "doc_id", doc_id, "err", err, "result", result + #console.log "doc_id", doc_id, "err", err, "result", result if err? errored++ return callback() if !result? or result.length == 0 missing++ return callback() - project_id = result[0] + project_id = result[0].project_id console.log "found project_id", project_id, "for doc_id", doc_id url = "http://localhost:#{options.port}/project/#{project_id}/doc/#{doc_id}/flush" if options.force From df388935386cd4b28eaab56d9e71154d14f5e2c6 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 24 Apr 2017 10:11:23 +0100 Subject: [PATCH 378/549] remove old single redis flushing code --- services/track-changes/app.coffee | 3 -- .../app/coffee/HttpController.coffee | 26 ------------- .../app/coffee/RedisManager.coffee | 38 ------------------- .../app/coffee/UpdatesManager.coffee | 32 ---------------- 4 files changed, 99 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 43cddca498..0a43cd1503 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -50,9 +50,6 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post '/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory app.post '/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory -app.post '/flush/all', HttpController.flushAll -app.post '/check/dangling', HttpController.checkDanglingUpdates - packWorker = null # use a single packing worker app.post "/pack", (req, res, next) -> diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 9ede86f3ee..eecc618330 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -22,32 +22,6 @@ module.exports = HttpController = return next(error) if error? res.send 204 - flushAll: (req, res, next = (error) ->) -> - # limit on projects to flush or -1 for all (default) - limit = if req.query.limit? then parseInt(req.query.limit, 10) else -1 - logger.log {limit: limit}, "flushing all projects" - UpdatesManager.flushAll limit, (error, result) -> - return next(error) if error? - {failed, succeeded, all} = result - status = "#{succeeded.length} succeeded, #{failed.length} failed" - if limit == 0 - res.status(200).send "#{status}\nwould flush:\n#{all.join('\n')}\n" - else if failed.length > 0 - logger.log {failed: failed, succeeded: succeeded}, "error flushing projects" - res.status(500).send "#{status}\nfailed to flush:\n#{failed.join('\n')}\n" - else - res.status(200).send "#{status}\nflushed #{succeeded.length} projects of #{all.length}\n" - - checkDanglingUpdates: (req, res, next = (error) ->) -> - logger.log "checking dangling updates" - UpdatesManager.getDanglingUpdates (error, result) -> - return next(error) if error? - if result.length > 0 - logger.log {dangling: result}, "found dangling updates" - res.status(500).send "dangling updates:\n#{result.join('\n')}\n" - else - res.status(200).send "no dangling updates found\n" - checkDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index b58b99f11f..a634bbfed9 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -32,41 +32,3 @@ module.exports = RedisManager = getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> rclient.smembers docsWithHistoryOpsKey(project_id), callback - - # iterate over keys asynchronously using redis scan (non-blocking) - _getKeys: (pattern, callback) -> - cursor = 0 # redis iterator - keySet = {} # use hash to avoid duplicate results - # scan over all keys looking for pattern - doIteration = (cb) -> - rclient.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) -> - return callback(error) if error? - [cursor, keys] = reply - for key in keys - keySet[key] = true - if cursor == '0' # note redis returns string result not numeric - return callback(null, Object.keys(keySet)) - else - doIteration() - doIteration() - - # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b - _extractIds: (keyList) -> - ids = (key.split(":")[1] for key in keyList) - return ids - - # this will only work on single node redis, not redis cluster - getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> - RedisManager._getKeys docsWithHistoryOpsKey("*"), (error, project_keys) -> - return callback(error) if error? - project_ids = RedisManager._extractIds project_keys - callback(error, project_ids) - - # this will only work on single node redis, not redis cluster - getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) -> - # return all the docids, to find dangling history entries after - # everything is flushed. - RedisManager._getKeys rawUpdatesKey("*"), (error, doc_keys) -> - return callback(error) if error? - doc_ids = RedisManager._extractIds doc_keys - callback(error, doc_ids) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 48423a39ec..f01681e5f0 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -144,38 +144,6 @@ module.exports = UpdatesManager = UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb async.parallelLimit jobs, 5, callback - # flush all outstanding changes - flushAll: (limit, callback = (error, result) ->) -> - RedisManager.getProjectIdsWithHistoryOps (error, project_ids) -> - return callback(error) if error? - logger.log {count: project_ids?.length, project_ids: project_ids}, "found projects" - jobs = [] - project_ids = _.shuffle project_ids # randomise to avoid hitting same projects each time - selectedProjects = if limit < 0 then project_ids else project_ids[0...limit] - for project_id in selectedProjects - do (project_id) -> - jobs.push (cb) -> - UpdatesManager.processUncompressedUpdatesForProject project_id, (err) -> - return cb(null, {failed: err?, project_id: project_id}) - async.series jobs, (error, result) -> - return callback(error) if error? - failedProjects = (x.project_id for x in result when x.failed) - succeededProjects = (x.project_id for x in result when not x.failed) - callback(null, {failed: failedProjects, succeeded: succeededProjects, all: project_ids}) - - getDanglingUpdates: (callback = (error, doc_ids) ->) -> - RedisManager.getAllDocIdsWithHistoryOps (error, all_doc_ids) -> - return callback(error) if error? - RedisManager.getProjectIdsWithHistoryOps (error, all_project_ids) -> - return callback(error) if error? - # function to get doc_ids for each project - task = (cb) -> async.concatSeries all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb - # find the dangling doc ids - task (error, project_doc_ids) -> - dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids) - logger.log {all_doc_ids: all_doc_ids, all_project_ids: all_project_ids, project_doc_ids: project_doc_ids, dangling_doc_ids: dangling_doc_ids}, "checking for dangling doc ids" - callback(null, dangling_doc_ids) - getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? From 3045becbb624f1d3d06f3546cb5f3b1408afefed Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 24 Apr 2017 14:23:32 +0100 Subject: [PATCH 379/549] use redis-sharelatex with redis cluster config --- .../track-changes/app/coffee/RedisManager.coffee | 14 ++++++-------- .../track-changes/config/settings.defaults.coffee | 7 +++++++ services/track-changes/package.json | 2 +- .../coffee/RedisManager/RedisManagerTests.coffee | 5 ++++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index a634bbfed9..70cea0805e 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -1,14 +1,12 @@ Settings = require "settings-sharelatex" redis = require("redis-sharelatex") -rclient = redis.createClient(Settings.redis.web) - -rawUpdatesKey = (doc_id) -> "UncompressedHistoryOps:#{doc_id}" -docsWithHistoryOpsKey = (project_id) -> "DocsWithHistoryOps:#{project_id}" +rclient = redis.createClient(Settings.redis.history) +Keys = Settings.redis.history.key_schema module.exports = RedisManager = getOldestDocUpdates: (doc_id, batchSize, callback = (error, jsonUpdates) ->) -> - key = rawUpdatesKey(doc_id) + key = Keys.uncompressedHistoryOps({doc_id}) rclient.lrange key, 0, batchSize - 1, callback expandDocUpdates: (jsonUpdates, callback = (error, rawUpdates) ->) -> @@ -22,13 +20,13 @@ module.exports = RedisManager = multi = rclient.multi() # Delete all the updates which have been applied (exact match) for update in docUpdates or [] - multi.lrem rawUpdatesKey(doc_id), 0, update + multi.lrem Keys.uncompressedHistoryOps({doc_id}), 0, update # It's ok to delete the doc_id from the set here. Even though the list # of updates may not be empty, we will continue to process it until it is. - multi.srem docsWithHistoryOpsKey(project_id), doc_id + multi.srem Keys.docsWithHistoryOps({project_id}), doc_id multi.exec (error, results) -> return callback(error) if error? callback null getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> - rclient.smembers docsWithHistoryOpsKey(project_id), callback + rclient.smembers Keys.docsWithHistoryOps({project_id}), callback diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 749887f4da..084ce7de62 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -22,6 +22,13 @@ module.exports = host: "localhost" port: 6379 pass: "" + history: + port:"6379" + host:"localhost" + password:"" + key_schema: + uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:#{doc_id}" + docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:#{project_id}" trackchanges: s3: diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b87967110c..165178b418 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -19,7 +19,7 @@ "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", "request": "~2.33.0", "requestretry": "^1.12.0", - "redis-sharelatex": "~0.0.9", + "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.0", "redis": "~0.10.1", "underscore": "~1.7.0", "mongo-uri": "^0.1.2", diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee index 12f78fd4aa..be20b38d21 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -14,7 +14,10 @@ describe "RedisManager", -> multi: () => @rclient "settings-sharelatex": redis: - web:{} + history: + key_schema: + uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:#{doc_id}" + docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:#{project_id}" @doc_id = "doc-id-123" @project_id = "project-id-123" @batchSize = 100 From 7836556d10dccf69eaf7140402aec40c33c9e00b Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 24 Apr 2017 14:24:02 +0100 Subject: [PATCH 380/549] update shrinkwrap file --- services/track-changes/npm-shrinkwrap.json | 2239 ++++++++++++-------- 1 file changed, 1370 insertions(+), 869 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 1b5bdef5df..9c01595474 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -2,41 +2,150 @@ "name": "history-sharelatex", "version": "0.1.4", "dependencies": { + "JSONStream": { + "version": "1.3.1", + "from": "JSONStream@1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "dependencies": { + "jsonparse": { + "version": "1.3.0", + "from": "jsonparse@1.3.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.0.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, "async": { "version": "0.2.10", - "from": "async@~0.2.10" + "from": "async@0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "aws-sdk": { + "version": "2.37.0", + "from": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", + "dependencies": { + "buffer": { + "version": "4.9.1", + "from": "buffer@4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "dependencies": { + "base64-js": { + "version": "1.2.0", + "from": "base64-js@1.2.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz" + }, + "ieee754": { + "version": "1.1.8", + "from": "ieee754@1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + } + } + }, + "crypto-browserify": { + "version": "1.0.9", + "from": "crypto-browserify@1.0.9", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz" + }, + "jmespath": { + "version": "0.15.0", + "from": "jmespath@0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" + }, + "querystring": { + "version": "0.2.0", + "from": "querystring@0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + }, + "sax": { + "version": "1.1.5", + "from": "sax@1.1.5", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" + }, + "url": { + "version": "0.10.3", + "from": "url@0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + } + } + }, + "uuid": { + "version": "3.0.0", + "from": "uuid@3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + }, + "xml2js": { + "version": "0.4.15", + "from": "xml2js@0.4.15", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" + }, + "xmlbuilder": { + "version": "2.6.2", + "from": "xmlbuilder@2.6.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", + "dependencies": { + "lodash": { + "version": "3.5.0", + "from": "lodash@3.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" + } + } + } + } }, "bson": { "version": "0.4.23", - "from": "bson@^0.4.20" + "from": "bson@0.4.23", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" }, "byline": { "version": "4.2.2", - "from": "byline@^4.2.1" + "from": "byline@4.2.2", + "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz" }, "cli": { "version": "0.6.6", - "from": "cli@^0.6.6", + "from": "cli@0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@~ 3.2.1", + "from": "glob@3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", "dependencies": { "inherits": { "version": "2.0.3", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@0.3", + "from": "minimatch@0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@2" + "from": "lru-cache@2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@~1.0.0" + "from": "sigmund@1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } } @@ -44,291 +153,221 @@ }, "exit": { "version": "0.1.2", - "from": "exit@0.1.2" + "from": "exit@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" } } }, "express": { "version": "3.3.5", - "from": "express@3.3.5", + "from": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "dependencies": { "connect": { "version": "2.8.5", "from": "connect@2.8.5", + "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "dependencies": { "qs": { "version": "0.6.5", - "from": "qs@0.6.5" + "from": "qs@0.6.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" }, "formidable": { "version": "1.0.14", - "from": "formidable@1.0.14" + "from": "formidable@1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" }, "bytes": { "version": "0.2.0", - "from": "bytes@0.2.0" + "from": "bytes@0.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" }, "pause": { "version": "0.0.1", - "from": "pause@0.0.1" + "from": "pause@0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" }, "uid2": { "version": "0.0.2", - "from": "uid2@0.0.2" + "from": "uid2@0.0.2", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" } } }, "commander": { "version": "1.2.0", - "from": "commander@1.2.0", + "from": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "dependencies": { "keypress": { "version": "0.1.0", - "from": "keypress@0.1.x" + "from": "keypress@0.1.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" } } }, "range-parser": { "version": "0.0.4", - "from": "range-parser@0.0.4" + "from": "range-parser@0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.x" + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0" + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" }, "buffer-crc32": { "version": "0.2.1", - "from": "buffer-crc32@0.2.1" + "from": "buffer-crc32@0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" }, "fresh": { "version": "0.2.0", - "from": "fresh@0.2.0" + "from": "fresh@0.2.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" }, "methods": { "version": "0.0.1", - "from": "methods@0.0.1" + "from": "methods@0.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" }, "send": { "version": "0.1.4", "from": "send@0.1.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "dependencies": { "mime": { "version": "1.2.11", - "from": "mime@~1.2.9" + "from": "mime@1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" } } }, "cookie-signature": { "version": "1.0.1", - "from": "cookie-signature@1.0.1" + "from": "cookie-signature@1.0.1", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" }, "debug": { "version": "2.6.3", - "from": "debug@*", + "from": "debug@2.6.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", "dependencies": { "ms": { "version": "0.7.2", - "from": "ms@0.7.2" + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" } } } } }, + "heap": { + "version": "0.2.6", + "from": "heap@0.2.6", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" + }, "line-reader": { "version": "0.2.4", - "from": "line-reader@^0.2.4" - }, - "mongojs": { - "version": "1.4.1", - "from": "mongojs@^1.4.1", - "dependencies": { - "each-series": { - "version": "1.0.0", - "from": "each-series@^1.0.0" - }, - "mongodb-core": { - "version": "1.3.22-alpha4", - "from": "mongodb-core@^1.2.8", - "dependencies": { - "require_optional": { - "version": "1.0.0", - "from": "require_optional@~1.0.0", - "dependencies": { - "semver": { - "version": "5.3.0", - "from": "semver@^5.1.0" - }, - "resolve-from": { - "version": "2.0.0", - "from": "resolve-from@^2.0.0" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@^1.3.2", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "parse-mongo-url": { - "version": "1.1.1", - "from": "parse-mongo-url@^1.1.0" - }, - "pump": { - "version": "1.0.2", - "from": "pump@^1.0.0", - "dependencies": { - "end-of-stream": { - "version": "1.4.0", - "from": "end-of-stream@^1.1.0" - } - } - }, - "readable-stream": { - "version": "2.2.6", - "from": "readable-stream@^2.0.2", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@^1.0.0" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@~1.0.0" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@~1.0.0" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@~2.0.1" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@~0.10.x" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@~1.0.1" - } - } - }, - "thunky": { - "version": "0.1.0", - "from": "thunky@^0.1.0" - }, - "to-mongodb-core": { - "version": "2.0.0", - "from": "to-mongodb-core@^2.0.0" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@^4.0.0" - } - } - }, - "settings-sharelatex": { - "version": "1.0.0", - "from": "settings-sharelatex@git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0" - } - } + "from": "line-reader@0.2.4", + "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, "logger-sharelatex": { "version": "1.5.6", - "from": "logger-sharelatex@git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", + "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#b2956ec56b582b9f4fc8fdda8dc00c06e77c5537", "dependencies": { "bunyan": { "version": "1.5.1", "from": "bunyan@1.5.1", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", "dependencies": { "dtrace-provider": { "version": "0.6.0", - "from": "dtrace-provider@~0.6", + "from": "dtrace-provider@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "dependencies": { "nan": { - "version": "2.5.1", - "from": "nan@^2.0.8" + "version": "2.6.2", + "from": "nan@>=2.0.8 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" } } }, "mv": { "version": "2.1.1", - "from": "mv@~2", + "from": "mv@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@~0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8" + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "ncp": { "version": "2.0.0", - "from": "ncp@~2.0.0" + "from": "ncp@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" }, "rimraf": { "version": "2.4.5", - "from": "rimraf@~2.4.0", + "from": "rimraf@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "dependencies": { "glob": { "version": "6.0.4", - "from": "glob@^6.0.1", + "from": "glob@>=6.0.1 <7.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", "dependencies": { "inflight": { "version": "1.0.6", - "from": "inflight@^1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@2 || 3", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", + "version": "1.1.7", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@^0.4.1" + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1" + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } } @@ -336,17 +375,20 @@ }, "once": { "version": "1.4.0", - "from": "once@^1.3.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -356,7 +398,8 @@ }, "safe-json-stringify": { "version": "1.0.4", - "from": "safe-json-stringify@~1" + "from": "safe-json-stringify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz" } } }, @@ -367,149 +410,181 @@ }, "grunt-contrib-clean": { "version": "0.6.0", - "from": "grunt-contrib-clean@^0.6.0", + "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", "dependencies": { "rimraf": { "version": "2.2.8", - "from": "rimraf@~2.2.1" + "from": "rimraf@>=2.2.1 <2.3.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@^0.11.0", + "from": "grunt-contrib-coffee@0.11.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@~1.7.0", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@~0.3.5" + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@~0.5.0", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@^1.1.0" + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@^1.0.0" + "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@^0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.1" + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@^0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.1" + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@^0.2.0" + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@~2.4.1" + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } }, "grunt-execute": { "version": "0.2.2", - "from": "grunt-execute@^0.2.2" + "from": "grunt-execute@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" }, "grunt-mocha-test": { "version": "0.11.0", - "from": "grunt-mocha-test@^0.11.0", + "from": "grunt-mocha-test@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz", "dependencies": { "mocha": { "version": "1.20.1", - "from": "mocha@~1.20.0", + "from": "mocha@>=1.20.0 <1.21.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0" + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "growl": { "version": "1.7.0", - "from": "growl@1.7.x" + "from": "growl@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" }, "jade": { "version": "0.26.3", "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1" + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0" + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7" + "from": "diff@1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { - "version": "2.6.3", + "version": "2.6.4", "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", "dependencies": { "ms": { - "version": "0.7.2", - "from": "ms@0.7.2" + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5" + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@~0.2.11", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@2" + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@~1.0.0" + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@~2.0.0" + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" } } } @@ -517,71 +592,87 @@ }, "hooker": { "version": "0.2.3", - "from": "hooker@~0.2.3" + "from": "hooker@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "fs-extra": { "version": "0.9.1", - "from": "fs-extra@~0.9.1", + "from": "fs-extra@>=0.9.1 <0.10.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", "dependencies": { "ncp": { "version": "0.5.1", - "from": "ncp@^0.5.1" + "from": "ncp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@^0.5.0", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8" + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "jsonfile": { "version": "1.1.1", - "from": "jsonfile@~1.1.0" + "from": "jsonfile@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" }, "rimraf": { "version": "2.6.1", - "from": "rimraf@^2.2.8", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@^7.0.5", + "from": "glob@>=7.0.5 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@^1.0.0" + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.6", - "from": "inflight@^1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@^3.0.2", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", + "version": "1.1.7", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@^0.4.1" + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1" + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } } @@ -589,17 +680,20 @@ }, "once": { "version": "1.4.0", - "from": "once@^1.3.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -610,439 +704,234 @@ } }, "raven": { - "version": "1.2.0", - "from": "raven@^1.1.3", + "version": "1.2.1", + "from": "raven@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", "dependencies": { "cookie": { "version": "0.3.1", - "from": "cookie@0.3.1" + "from": "cookie@0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@5.0.1" + "from": "json-stringify-safe@5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, "lsmod": { "version": "1.0.0", - "from": "lsmod@1.0.0" + "from": "lsmod@1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz" }, "uuid": { "version": "3.0.0", - "from": "uuid@3.0.0" + "from": "uuid@3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9" + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } }, "timekeeper": { "version": "1.0.0", - "from": "timekeeper@^1.0.0" + "from": "timekeeper@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" } } }, "metrics-sharelatex": { "version": "1.7.1", - "from": "metrics-sharelatex@git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", + "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#166961924c599b1f9468f2e17846fa2a9d12372d", "dependencies": { "lynx": { "version": "0.1.1", - "from": "lynx@~0.1.1", + "from": "lynx@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", "dependencies": { "mersenne": { "version": "0.0.3", - "from": "mersenne@~0.0.3" + "from": "mersenne@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" }, "statsd-parser": { "version": "0.0.4", - "from": "statsd-parser@~0.0.4" + "from": "statsd-parser@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" } } }, "coffee-script": { "version": "1.6.0", - "from": "coffee-script@1.6.0" + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" }, "underscore": { "version": "1.6.0", - "from": "underscore@~1.6.0" + "from": "underscore@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" } } }, - "request": { - "version": "2.33.0", - "from": "request@~2.33.0", + "mongo-uri": { + "version": "0.1.2", + "from": "mongo-uri@0.1.2", + "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" + }, + "mongojs": { + "version": "1.4.1", + "from": "mongojs@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", "dependencies": { - "qs": { - "version": "0.6.6", - "from": "qs@~0.6.0" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@~5.0.0" - }, - "forever-agent": { - "version": "0.5.2", - "from": "forever-agent@~0.5.0" - }, - "node-uuid": { - "version": "1.4.8", - "from": "node-uuid@~1.4.0" - }, - "mime": { - "version": "1.2.11", - "from": "mime@~1.2.9" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@>=0.12.0", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@^1.4.1" - } - } - }, - "form-data": { - "version": "0.1.4", - "from": "form-data@~0.1.0", - "dependencies": { - "combined-stream": { - "version": "0.0.7", - "from": "combined-stream@~0.0.4", - "dependencies": { - "delayed-stream": { - "version": "0.0.5", - "from": "delayed-stream@0.0.5" - } - } - }, - "async": { - "version": "0.9.2", - "from": "async@~0.9.0" - } - } - }, - "tunnel-agent": { - "version": "0.3.0", - "from": "tunnel-agent@~0.3.0" - }, - "http-signature": { - "version": "0.10.1", - "from": "http-signature@~0.10.0", - "dependencies": { - "assert-plus": { - "version": "0.1.5", - "from": "assert-plus@^0.1.5" - }, - "asn1": { - "version": "0.1.11", - "from": "asn1@0.1.11" - }, - "ctype": { - "version": "0.5.3", - "from": "ctype@0.5.3" - } - } - }, - "oauth-sign": { - "version": "0.3.0", - "from": "oauth-sign@~0.3.0" - }, - "hawk": { + "each-series": { "version": "1.0.0", - "from": "hawk@~1.0.0", + "from": "each-series@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + }, + "mongodb-core": { + "version": "1.3.21", + "from": "mongodb-core@>=1.2.8 <2.0.0", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.21.tgz", "dependencies": { - "hoek": { - "version": "0.9.1", - "from": "hoek@0.9.x" - }, - "boom": { - "version": "0.4.2", - "from": "boom@0.4.x" - }, - "cryptiles": { - "version": "0.2.2", - "from": "cryptiles@0.2.x" - }, - "sntp": { - "version": "0.2.4", - "from": "sntp@0.2.x" + "require_optional": { + "version": "1.0.0", + "from": "require_optional@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", + "dependencies": { + "semver": { + "version": "5.3.0", + "from": "semver@>=5.1.0 <6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + }, + "resolve-from": { + "version": "2.0.0", + "from": "resolve-from@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + } + } } } }, - "aws-sign2": { - "version": "0.5.0", - "from": "aws-sign2@~0.5.0" + "once": { + "version": "1.4.0", + "from": "once@>=1.3.2 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + } + }, + "parse-mongo-url": { + "version": "1.1.1", + "from": "parse-mongo-url@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + }, + "pump": { + "version": "1.0.2", + "from": "pump@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.4.0", + "from": "end-of-stream@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.2.9", + "from": "readable-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "string_decoder": { + "version": "1.0.0", + "from": "string_decoder@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + }, + "thunky": { + "version": "0.1.0", + "from": "thunky@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + }, + "to-mongodb-core": { + "version": "2.0.0", + "from": "to-mongodb-core@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" } } }, - "requestretry": { - "version": "1.12.0", - "from": "requestretry@^1.12.0", - "dependencies": { - "extend": { - "version": "3.0.0", - "from": "extend@^3.0.0" - }, - "lodash": { - "version": "4.17.4", - "from": "lodash@^4.15.0" - }, - "request": { - "version": "2.81.0", - "from": "request@^2.74.0", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@~0.6.0" - }, - "aws4": { - "version": "1.6.0", - "from": "aws4@^1.2.1" - }, - "caseless": { - "version": "0.12.0", - "from": "caseless@~0.12.0" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@~1.0.5", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@~1.0.0" - } - } - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@~0.6.1" - }, - "form-data": { - "version": "2.1.2", - "from": "form-data@~2.1.1", - "dependencies": { - "asynckit": { - "version": "0.4.0", - "from": "asynckit@^0.4.0" - } - } - }, - "har-validator": { - "version": "4.2.1", - "from": "har-validator@~4.2.1", - "dependencies": { - "ajv": { - "version": "4.11.5", - "from": "ajv@^4.9.1", - "dependencies": { - "co": { - "version": "4.6.0", - "from": "co@^4.6.0" - }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "json-stable-stringify@^1.0.1", - "dependencies": { - "jsonify": { - "version": "0.0.0", - "from": "jsonify@~0.0.0" - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "from": "har-schema@^1.0.5" - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@~3.1.3", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "hoek@2.x.x" - }, - "boom": { - "version": "2.10.1", - "from": "boom@2.x.x" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@2.x.x" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@1.x.x" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@~1.1.0", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@^0.2.0" - }, - "jsprim": { - "version": "1.4.0", - "from": "jsprim@^1.2.2", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@1.0.0" - }, - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2" - }, - "json-schema": { - "version": "0.2.3", - "from": "json-schema@0.2.3" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6" - } - } - }, - "sshpk": { - "version": "1.11.0", - "from": "sshpk@^1.7.0", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "asn1@~0.2.3" - }, - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@^1.0.0" - }, - "dashdash": { - "version": "1.14.1", - "from": "dashdash@^1.12.0" - }, - "getpass": { - "version": "0.1.6", - "from": "getpass@^0.1.1" - }, - "jsbn": { - "version": "0.1.1", - "from": "jsbn@~0.1.0" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "tweetnacl@~0.14.0" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@^1.0.0" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@~0.1.1" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "from": "bcrypt-pbkdf@^1.0.0" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@~1.0.0" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@~0.1.2" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@~5.0.1" - }, - "mime-types": { - "version": "2.1.15", - "from": "mime-types@~2.1.7", - "dependencies": { - "mime-db": { - "version": "1.27.0", - "from": "mime-db@~1.27.0" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "from": "oauth-sign@~0.8.1" - }, - "performance-now": { - "version": "0.2.0", - "from": "performance-now@^0.2.0" - }, - "qs": { - "version": "6.4.0", - "from": "qs@~6.4.0" - }, - "safe-buffer": { - "version": "5.0.1", - "from": "safe-buffer@^5.0.1" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@~0.0.4" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@~2.3.0", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@^1.4.1" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@^0.6.0" - }, - "uuid": { - "version": "3.0.1", - "from": "uuid@^3.0.0" - } - } - }, - "when": { - "version": "3.7.8", - "from": "when@^3.7.7" - } - } + "redis": { + "version": "0.10.3", + "from": "redis@0.10.3", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" }, "redis-sharelatex": { - "version": "0.0.9", - "from": "redis-sharelatex@~0.0.9", + "version": "1.0.0", + "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.0", + "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#f6bc41c578d0830827127d89e2bcd53587f02fa8", "dependencies": { "chai": { "version": "1.9.1", "from": "chai@1.9.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", "dependencies": { "assertion-error": { "version": "1.0.0", - "from": "assertion-error@1.0.0" + "from": "assertion-error@1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" }, "deep-eql": { "version": "0.1.3", "from": "deep-eql@0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "dependencies": { "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1" + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" } } } @@ -1051,148 +940,180 @@ "coffee-script": { "version": "1.8.0", "from": "coffee-script@1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@~0.3.5" + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@^0.11.0", + "from": "grunt-contrib-coffee@0.11.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@~1.7.0", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@~0.3.5" + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@~0.5.0", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@^1.1.0" + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@^1.0.0" + "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@^0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.0" + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@^0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.0" + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@^0.2.0" + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@~2.4.1" + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } }, "grunt-mocha-test": { "version": "0.12.0", "from": "grunt-mocha-test@0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", "dependencies": { "hooker": { "version": "0.2.3", - "from": "hooker@~0.2.3" + "from": "hooker@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "fs-extra": { "version": "0.11.1", - "from": "fs-extra@~0.11.1", + "from": "fs-extra@>=0.11.1 <0.12.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", "dependencies": { "ncp": { "version": "0.6.0", - "from": "ncp@^0.6.0" + "from": "ncp@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@^0.5.0", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8" + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "jsonfile": { "version": "2.4.0", - "from": "jsonfile@^2.0.0", + "from": "jsonfile@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@^4.1.6" + "from": "graceful-fs@>=4.1.6 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" } } }, "rimraf": { "version": "2.6.1", - "from": "rimraf@^2.2.8", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@^7.0.5", + "from": "glob@>=7.0.5 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@^1.0.0" + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.6", - "from": "inflight@^1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@2 || 3", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", + "version": "1.1.7", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@^0.4.1" + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1" + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } } @@ -1200,17 +1121,20 @@ }, "once": { "version": "1.4.0", - "from": "once@^1.3.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -1220,75 +1144,145 @@ } } }, + "ioredis": { + "version": "2.5.0", + "from": "ioredis@>=2.5.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.5.0.tgz", + "dependencies": { + "bluebird": { + "version": "3.5.0", + "from": "bluebird@>=3.3.4 <4.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz" + }, + "cluster-key-slot": { + "version": "1.0.8", + "from": "cluster-key-slot@>=1.0.6 <2.0.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz" + }, + "debug": { + "version": "2.6.4", + "from": "debug@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", + "dependencies": { + "ms": { + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" + } + } + }, + "double-ended-queue": { + "version": "2.1.0-0", + "from": "double-ended-queue@>=2.1.0-0 <3.0.0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" + }, + "flexbuffer": { + "version": "0.0.6", + "from": "flexbuffer@0.0.6", + "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.8.2 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "redis-commands": { + "version": "1.3.1", + "from": "redis-commands@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz" + }, + "redis-parser": { + "version": "1.3.0", + "from": "redis-parser@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz" + } + } + }, "mocha": { "version": "1.21.4", "from": "mocha@1.21.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0" + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "growl": { "version": "1.8.1", - "from": "growl@1.8.x" + "from": "growl@>=1.8.0 <1.9.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" }, "jade": { "version": "0.26.3", "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1" + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0" + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7" + "from": "diff@1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { - "version": "2.6.3", + "version": "2.6.4", "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", "dependencies": { "ms": { - "version": "0.7.2", - "from": "ms@0.7.2" + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5" + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@~0.2.11", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@2" + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@~1.0.0" + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@~2.0.0" + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" } } } @@ -1296,57 +1290,69 @@ }, "redis": { "version": "0.12.1", - "from": "redis@0.12.1" + "from": "redis@0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" }, "redis-sentinel": { "version": "0.1.1", "from": "redis-sentinel@0.1.1", + "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "dependencies": { "redis": { "version": "0.11.0", - "from": "redis@0.11.x" + "from": "redis@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" }, "q": { "version": "0.9.2", - "from": "q@0.9.2" + "from": "q@0.9.2", + "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" } } }, "sandboxed-module": { "version": "1.0.1", "from": "sandboxed-module@1.0.1", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", - "from": "require-like@0.1.2" + "from": "require-like@0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9" + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } }, "sinon": { "version": "1.10.3", "from": "sinon@1.10.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "formatio@~1.0", + "from": "formatio@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "dependencies": { "samsam": { "version": "1.1.3", - "from": "samsam@~1.1" + "from": "samsam@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1", + "from": "util@>=0.10.3 <1.0.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } } @@ -1354,125 +1360,566 @@ } } }, - "redis": { - "version": "0.10.3", - "from": "redis@~0.10.1" + "request": { + "version": "2.33.0", + "from": "request@2.33.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", + "dependencies": { + "qs": { + "version": "0.6.6", + "from": "qs@0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "node-uuid": { + "version": "1.4.8", + "from": "node-uuid@1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" + }, + "mime": { + "version": "1.2.11", + "from": "mime@1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "punycode@1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@0.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + }, + "async": { + "version": "0.9.2", + "from": "async@0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.3.0", + "from": "tunnel-agent@0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@0.10.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" + } + } + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" + }, + "hawk": { + "version": "1.0.0", + "from": "hawk@1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + } + } }, - "underscore": { - "version": "1.7.0", - "from": "underscore@~1.7.0" - }, - "mongo-uri": { - "version": "0.1.2", - "from": "mongo-uri@^0.1.2" + "requestretry": { + "version": "1.12.0", + "from": "requestretry@1.12.0", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.12.0.tgz", + "dependencies": { + "extend": { + "version": "3.0.0", + "from": "extend@3.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "request": { + "version": "2.81.0", + "from": "request@2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "aws4": { + "version": "1.6.0", + "from": "aws4@1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "caseless@0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + } + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.2", + "from": "form-data@2.1.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "dependencies": { + "asynckit": { + "version": "0.4.0", + "from": "asynckit@0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + } + } + }, + "har-validator": { + "version": "4.2.1", + "from": "har-validator@4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "dependencies": { + "ajv": { + "version": "4.11.5", + "from": "ajv@4.11.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "dependencies": { + "co": { + "version": "4.6.0", + "from": "co@4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "dependencies": { + "jsonify": { + "version": "0.0.0", + "from": "jsonify@0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "har-schema@1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "hoek@2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "boom@2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "jsprim": { + "version": "1.4.0", + "from": "jsprim@1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "extsprintf": { + "version": "1.0.2", + "from": "extsprintf@1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "verror": { + "version": "1.3.6", + "from": "verror@1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + } + } + }, + "sshpk": { + "version": "1.11.0", + "from": "sshpk@1.11.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "asn1@0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + }, + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" + }, + "getpass": { + "version": "0.1.6", + "from": "getpass@0.1.6", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "jodid25519": { + "version": "1.0.2", + "from": "jodid25519@1.0.2", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "bcrypt-pbkdf@1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "mime-types": { + "version": "2.1.15", + "from": "mime-types@2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "dependencies": { + "mime-db": { + "version": "1.27.0", + "from": "mime-db@1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "performance-now@0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "qs@6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "safe-buffer": { + "version": "5.0.1", + "from": "safe-buffer@5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "punycode@1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.0.1", + "from": "uuid@3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + } + } + }, + "when": { + "version": "3.7.8", + "from": "when@3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" + } + } }, "s3-streams": { "version": "0.3.0", - "from": "s3-streams@^0.3.0", + "from": "s3-streams@0.3.0", + "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", "dependencies": { "lodash": { "version": "3.10.1", - "from": "lodash@^3.9.3" + "from": "lodash@3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" }, "readable-stream": { "version": "2.2.6", - "from": "readable-stream@^2.0.0", + "from": "readable-stream@2.2.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@^1.0.0" + "from": "buffer-shims@1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@~1.0.0" + "from": "isarray@1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" + "from": "process-nextick-args@1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@~1.0.1" + "from": "util-deprecate@1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } }, "bluebird": { "version": "2.11.0", - "from": "bluebird@^2.9.27" + "from": "bluebird@2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz" } } }, - "JSONStream": { - "version": "1.3.1", - "from": "JSONStream@^1.0.4", + "settings-sharelatex": { + "version": "1.0.0", + "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", "dependencies": { - "jsonparse": { - "version": "1.3.0", - "from": "jsonparse@^1.2.0" - }, - "through": { - "version": "2.3.8", - "from": "through@>=2.2.7 <3" + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" } } }, - "heap": { - "version": "0.2.6", - "from": "heap@^0.2.6" + "underscore": { + "version": "1.7.0", + "from": "underscore@1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" }, "v8-profiler": { "version": "5.7.0", - "from": "v8-profiler@^5.6.5", + "from": "v8-profiler@5.7.0", + "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", "dependencies": { "nan": { "version": "2.5.1", - "from": "nan@^2.5.1" + "from": "nan@2.5.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" }, "node-pre-gyp": { "version": "0.6.34", - "from": "node-pre-gyp@^0.6.34", + "from": "node-pre-gyp@0.6.34", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@^0.5.1", + "from": "mkdirp@0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8" + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "nopt": { "version": "4.0.1", - "from": "nopt@^4.0.1", + "from": "nopt@4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "dependencies": { "abbrev": { "version": "1.1.0", - "from": "abbrev@1" + "from": "abbrev@1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" }, "osenv": { "version": "0.1.4", - "from": "osenv@^0.1.4", + "from": "osenv@0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", "dependencies": { "os-homedir": { "version": "1.0.2", - "from": "os-homedir@^1.0.0" + "from": "os-homedir@1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" }, "os-tmpdir": { "version": "1.0.2", - "from": "os-tmpdir@^1.0.0" + "from": "os-tmpdir@1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" } } } @@ -1480,47 +1927,58 @@ }, "npmlog": { "version": "4.0.2", - "from": "npmlog@^4.0.2", + "from": "npmlog@4.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", "dependencies": { "are-we-there-yet": { "version": "1.1.2", - "from": "are-we-there-yet@~1.1.2", + "from": "are-we-there-yet@1.1.2", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", "dependencies": { "delegates": { "version": "1.0.0", - "from": "delegates@^1.0.0" + "from": "delegates@1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" }, "readable-stream": { "version": "2.2.6", - "from": "readable-stream@^2.0.0 || ^1.1.13", + "from": "readable-stream@2.2.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@^1.0.0" + "from": "buffer-shims@1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@~1.0.0" + "from": "isarray@1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" + "from": "process-nextick-args@1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@~1.0.1" + "from": "util-deprecate@1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } } @@ -1528,43 +1986,53 @@ }, "console-control-strings": { "version": "1.1.0", - "from": "console-control-strings@~1.1.0" + "from": "console-control-strings@1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" }, "gauge": { "version": "2.7.3", - "from": "gauge@~2.7.1", + "from": "gauge@2.7.3", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", "dependencies": { "aproba": { "version": "1.1.1", - "from": "aproba@^1.0.3" + "from": "aproba@1.1.1", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" }, "has-unicode": { "version": "2.0.1", - "from": "has-unicode@^2.0.0" + "from": "has-unicode@2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" }, "object-assign": { "version": "4.1.1", - "from": "object-assign@^4.1.0" + "from": "object-assign@4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" }, "signal-exit": { "version": "3.0.2", - "from": "signal-exit@^3.0.0" + "from": "signal-exit@3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" }, "string-width": { "version": "1.0.2", - "from": "string-width@^1.0.1", + "from": "string-width@1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "dependencies": { "code-point-at": { "version": "1.1.0", - "from": "code-point-at@^1.0.0" + "from": "code-point-at@1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@^1.0.0", + "from": "is-fullwidth-code-point@1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "dependencies": { "number-is-nan": { "version": "1.0.1", - "from": "number-is-nan@^1.0.0" + "from": "number-is-nan@1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" } } } @@ -1572,112 +2040,135 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@^3.0.1", + "from": "strip-ansi@3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "dependencies": { "ansi-regex": { "version": "2.1.1", - "from": "ansi-regex@^2.0.0" + "from": "ansi-regex@2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" } } }, "wide-align": { "version": "1.1.0", - "from": "wide-align@^1.1.0" + "from": "wide-align@1.1.0", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" } } }, "set-blocking": { "version": "2.0.0", - "from": "set-blocking@~2.0.0" + "from": "set-blocking@2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" } } }, "rc": { "version": "1.2.1", - "from": "rc@^1.1.7", + "from": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", "dependencies": { "deep-extend": { "version": "0.4.1", - "from": "deep-extend@~0.4.0" + "from": "deep-extend@0.4.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" }, "ini": { "version": "1.3.4", - "from": "ini@~1.3.0" + "from": "ini@1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" }, "minimist": { "version": "1.2.0", - "from": "minimist@^1.2.0" + "from": "minimist@1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" }, "strip-json-comments": { "version": "2.0.1", - "from": "strip-json-comments@~2.0.1" + "from": "strip-json-comments@2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" } } }, "request": { "version": "2.81.0", - "from": "request@^2.81.0", + "from": "request@2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", "dependencies": { "aws-sign2": { "version": "0.6.0", - "from": "aws-sign2@~0.6.0" + "from": "aws-sign2@0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" }, "aws4": { "version": "1.6.0", - "from": "aws4@^1.2.1" + "from": "aws4@1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" }, "caseless": { "version": "0.12.0", - "from": "caseless@~0.12.0" + "from": "caseless@0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" }, "combined-stream": { "version": "1.0.5", - "from": "combined-stream@~1.0.5", + "from": "combined-stream@1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "dependencies": { "delayed-stream": { "version": "1.0.0", - "from": "delayed-stream@~1.0.0" + "from": "delayed-stream@1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" } } }, "extend": { "version": "3.0.0", - "from": "extend@~3.0.0" + "from": "extend@3.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" }, "forever-agent": { "version": "0.6.1", - "from": "forever-agent@~0.6.1" + "from": "forever-agent@0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" }, "form-data": { "version": "2.1.2", - "from": "form-data@~2.1.1", + "from": "form-data@2.1.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", "dependencies": { "asynckit": { "version": "0.4.0", - "from": "asynckit@^0.4.0" + "from": "asynckit@0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" } } }, "har-validator": { "version": "4.2.1", - "from": "har-validator@~4.2.1", + "from": "har-validator@4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", "dependencies": { "ajv": { "version": "4.11.5", - "from": "ajv@^4.9.1", + "from": "ajv@4.11.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", "dependencies": { "co": { "version": "4.6.0", - "from": "co@^4.6.0" + "from": "co@4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" }, "json-stable-stringify": { "version": "1.0.1", - "from": "json-stable-stringify@^1.0.1", + "from": "json-stable-stringify@1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "dependencies": { "jsonify": { "version": "0.0.0", - "from": "jsonify@~0.0.0" + "from": "jsonify@0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" } } } @@ -1685,101 +2176,124 @@ }, "har-schema": { "version": "1.0.5", - "from": "har-schema@^1.0.5" + "from": "har-schema@1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" } } }, "hawk": { "version": "3.1.3", - "from": "hawk@~3.1.3", + "from": "hawk@3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", "dependencies": { "hoek": { "version": "2.16.3", - "from": "hoek@2.x.x" + "from": "hoek@2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" }, "boom": { "version": "2.10.1", - "from": "boom@2.x.x" + "from": "boom@2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" }, "cryptiles": { "version": "2.0.5", - "from": "cryptiles@2.x.x" + "from": "cryptiles@2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" }, "sntp": { "version": "1.0.9", - "from": "sntp@1.x.x" + "from": "sntp@1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" } } }, "http-signature": { "version": "1.1.1", - "from": "http-signature@~1.1.0", + "from": "http-signature@1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", "dependencies": { "assert-plus": { "version": "0.2.0", - "from": "assert-plus@^0.2.0" + "from": "assert-plus@0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" }, "jsprim": { "version": "1.4.0", - "from": "jsprim@^1.2.2", + "from": "jsprim@1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@1.0.0" + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" }, "extsprintf": { "version": "1.0.2", - "from": "extsprintf@1.0.2" + "from": "extsprintf@1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" }, "json-schema": { "version": "0.2.3", - "from": "json-schema@0.2.3" + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" }, "verror": { "version": "1.3.6", - "from": "verror@1.3.6" + "from": "verror@1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" } } }, "sshpk": { "version": "1.11.0", - "from": "sshpk@^1.7.0", + "from": "sshpk@1.11.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", "dependencies": { "asn1": { "version": "0.2.3", - "from": "asn1@~0.2.3" + "from": "asn1@0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" }, "assert-plus": { "version": "1.0.0", - "from": "assert-plus@^1.0.0" + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" }, "dashdash": { "version": "1.14.1", - "from": "dashdash@^1.12.0" + "from": "dashdash@1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" }, "getpass": { "version": "0.1.6", - "from": "getpass@^0.1.1" + "from": "getpass@0.1.6", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" }, "jsbn": { "version": "0.1.1", - "from": "jsbn@~0.1.0" + "from": "jsbn@0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" }, "tweetnacl": { "version": "0.14.5", - "from": "tweetnacl@~0.14.0" + "from": "tweetnacl@0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" }, "jodid25519": { "version": "1.0.2", - "from": "jodid25519@^1.0.0" + "from": "jodid25519@1.0.2", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" }, "ecc-jsbn": { "version": "0.1.1", - "from": "ecc-jsbn@~0.1.1" + "from": "ecc-jsbn@0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" }, "bcrypt-pbkdf": { "version": "1.0.1", - "from": "bcrypt-pbkdf@^1.0.0" + "from": "bcrypt-pbkdf@1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" } } } @@ -1787,107 +2301,131 @@ }, "is-typedarray": { "version": "1.0.0", - "from": "is-typedarray@~1.0.0" + "from": "is-typedarray@1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" }, "isstream": { "version": "0.1.2", - "from": "isstream@~0.1.2" + "from": "isstream@0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@~5.0.1" + "from": "json-stringify-safe@5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, "mime-types": { "version": "2.1.15", - "from": "mime-types@~2.1.7", + "from": "mime-types@2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", "dependencies": { "mime-db": { "version": "1.27.0", - "from": "mime-db@~1.27.0" + "from": "mime-db@1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" } } }, "oauth-sign": { "version": "0.8.2", - "from": "oauth-sign@~0.8.1" + "from": "oauth-sign@0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" }, "performance-now": { "version": "0.2.0", - "from": "performance-now@^0.2.0" + "from": "performance-now@0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" }, "qs": { "version": "6.4.0", - "from": "qs@~6.4.0" + "from": "qs@6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" }, "safe-buffer": { "version": "5.0.1", - "from": "safe-buffer@^5.0.1" + "from": "safe-buffer@5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" }, "stringstream": { "version": "0.0.5", - "from": "stringstream@~0.0.4" + "from": "stringstream@0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" }, "tough-cookie": { "version": "2.3.2", - "from": "tough-cookie@~2.3.0", + "from": "tough-cookie@2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", "dependencies": { "punycode": { "version": "1.4.1", - "from": "punycode@^1.4.1" + "from": "punycode@1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" } } }, "tunnel-agent": { "version": "0.6.0", - "from": "tunnel-agent@^0.6.0" + "from": "tunnel-agent@0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" }, "uuid": { "version": "3.0.1", - "from": "uuid@^3.0.0" + "from": "uuid@3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" } } }, "rimraf": { "version": "2.6.1", - "from": "rimraf@^2.6.1", + "from": "rimraf@2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@^7.0.5", + "from": "glob@7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@^1.0.0" + "from": "fs.realpath@1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.6", - "from": "inflight@^1.0.4", + "from": "inflight@1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@^3.0.2", + "from": "minimatch@3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@^1.0.0", + "from": "brace-expansion@1.1.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@^0.4.1" + "from": "balanced-match@0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1" + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } } @@ -1895,17 +2433,20 @@ }, "once": { "version": "1.4.0", - "from": "once@^1.3.0", + "from": "once@1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" + "from": "path-is-absolute@1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -1913,83 +2454,101 @@ }, "semver": { "version": "5.3.0", - "from": "semver@^5.3.0" + "from": "semver@5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" }, "tar": { "version": "2.2.1", - "from": "tar@^2.2.1", + "from": "tar@2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "dependencies": { "block-stream": { "version": "0.0.9", - "from": "block-stream@*" + "from": "block-stream@0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" }, "fstream": { "version": "1.0.11", - "from": "fstream@^1.0.2", + "from": "fstream@1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@^4.1.2" + "from": "graceful-fs@4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" } } }, "tar-pack": { "version": "3.4.0", - "from": "tar-pack@^3.4.0", + "from": "tar-pack@3.4.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", "dependencies": { "debug": { "version": "2.6.3", - "from": "debug@^2.2.0", + "from": "debug@2.6.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", "dependencies": { "ms": { "version": "0.7.2", - "from": "ms@0.7.2" + "from": "ms@0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" } } }, "fstream": { "version": "1.0.11", - "from": "fstream@^1.0.10", + "from": "fstream@1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@^4.1.2" + "from": "graceful-fs@4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" } } }, "fstream-ignore": { "version": "1.0.5", - "from": "fstream-ignore@^1.0.5", + "from": "fstream-ignore@1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", "dependencies": { "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@^3.0.0", + "from": "minimatch@3.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@^1.0.0", + "from": "brace-expansion@1.1.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@^0.4.1" + "from": "balanced-match@0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1" + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } } @@ -1999,126 +2558,68 @@ }, "once": { "version": "1.4.0", - "from": "once@^1.3.3", + "from": "once@1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "readable-stream": { "version": "2.2.6", - "from": "readable-stream@^2.1.4", + "from": "readable-stream@2.2.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@^1.0.0" + "from": "buffer-shims@1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@~1.0.0" + "from": "core-util-is@1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" }, "isarray": { "version": "1.0.0", - "from": "isarray@~1.0.0" + "from": "isarray@1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@~2.0.1" + "from": "inherits@2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" + "from": "process-nextick-args@1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@~0.10.x" + "from": "string_decoder@0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@~1.0.1" + "from": "util-deprecate@1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" } } }, "uid-number": { "version": "0.0.6", - "from": "uid-number@^0.0.6" + "from": "uid-number@0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" } } } } } } - }, - "aws-sdk": { - "version": "2.37.0", - "from": "aws-sdk@^2.1.34", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", - "dependencies": { - "buffer": { - "version": "4.9.1", - "from": "buffer@4.9.1", - "dependencies": { - "base64-js": { - "version": "1.2.0", - "from": "base64-js@^1.0.2" - }, - "ieee754": { - "version": "1.1.8", - "from": "ieee754@^1.1.4" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@^1.0.0" - } - } - }, - "crypto-browserify": { - "version": "1.0.9", - "from": "crypto-browserify@1.0.9" - }, - "jmespath": { - "version": "0.15.0", - "from": "jmespath@0.15.0" - }, - "querystring": { - "version": "0.2.0", - "from": "querystring@0.2.0" - }, - "sax": { - "version": "1.1.5", - "from": "sax@1.1.5" - }, - "url": { - "version": "0.10.3", - "from": "url@0.10.3", - "dependencies": { - "punycode": { - "version": "1.3.2", - "from": "punycode@1.3.2" - } - } - }, - "uuid": { - "version": "3.0.0", - "from": "uuid@3.0.0" - }, - "xml2js": { - "version": "0.4.15", - "from": "xml2js@0.4.15" - }, - "xmlbuilder": { - "version": "2.6.2", - "from": "xmlbuilder@2.6.2", - "dependencies": { - "lodash": { - "version": "3.5.0", - "from": "lodash@~3.5.0" - } - } - } - } } } } From 3b5a07d530cad00dbb19246a559104018c1bd7e7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 26 Apr 2017 16:25:34 +0100 Subject: [PATCH 381/549] fix update to work with redis cluster the items do not have the same tag, so we must delete the entry from the docsWithHistoryOps set outside the multi. --- services/track-changes/app/coffee/RedisManager.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 70cea0805e..fd901e689e 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -21,12 +21,13 @@ module.exports = RedisManager = # Delete all the updates which have been applied (exact match) for update in docUpdates or [] multi.lrem Keys.uncompressedHistoryOps({doc_id}), 0, update - # It's ok to delete the doc_id from the set here. Even though the list - # of updates may not be empty, we will continue to process it until it is. - multi.srem Keys.docsWithHistoryOps({project_id}), doc_id multi.exec (error, results) -> return callback(error) if error? - callback null + # It's ok to delete the doc_id from the set here. Even though the list + # of updates may not be empty, we will continue to process it until it is. + rclient.srem Keys.docsWithHistoryOps({project_id}), doc_id, (error) -> + return callback(error) if error? + callback null getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> rclient.smembers Keys.docsWithHistoryOps({project_id}), callback From 0966f694da11509c906f0a3362427baa042f637c Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 27 Apr 2017 10:14:44 +0100 Subject: [PATCH 382/549] fix acceptance tests to work with redis cluster --- .../acceptance/coffee/helpers/TrackChangesClient.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 4e1d1d1eff..e927333004 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -1,9 +1,10 @@ async = require 'async' zlib = require 'zlib' request = require "request" -rclient = require("redis").createClient() # Only works locally for now -{db, ObjectId} = require "../../../../app/js/mongojs" Settings = require "settings-sharelatex" +rclient = require("redis-sharelatex").createClient(Settings.redis.history) # Only works locally for now +Keys = Settings.redis.history.key_schema +{db, ObjectId} = require "../../../../app/js/mongojs" module.exports = TrackChangesClient = flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> @@ -49,9 +50,9 @@ module.exports = TrackChangesClient = }, callback pushRawUpdates: (project_id, doc_id, updates, callback = (error) ->) -> - rclient.sadd "DocsWithHistoryOps:#{project_id}", doc_id, (error) -> + rclient.sadd Keys.docsWithHistoryOps({project_id}), doc_id, (error) -> return callback(error) if error? - rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback + rclient.rpush Keys.uncompressedHistoryOps({doc_id}), (JSON.stringify(u) for u in updates)..., callback getDiff: (project_id, doc_id, from, to, callback = (error, diff) ->) -> request.get { From 534bd8f7ad51b183373a65fa0742f86139452b52 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 2 May 2017 14:19:49 +0100 Subject: [PATCH 383/549] add flushing code with error check for cluster --- services/track-changes/app.coffee | 3 ++ .../app/coffee/HttpController.coffee | 26 ++++++++++++ .../app/coffee/RedisManager.coffee | 40 +++++++++++++++++++ .../app/coffee/UpdatesManager.coffee | 32 +++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 0a43cd1503..43cddca498 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -50,6 +50,9 @@ app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpContro app.post '/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory app.post '/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory +app.post '/flush/all', HttpController.flushAll +app.post '/check/dangling', HttpController.checkDanglingUpdates + packWorker = null # use a single packing worker app.post "/pack", (req, res, next) -> diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index eecc618330..9ede86f3ee 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -22,6 +22,32 @@ module.exports = HttpController = return next(error) if error? res.send 204 + flushAll: (req, res, next = (error) ->) -> + # limit on projects to flush or -1 for all (default) + limit = if req.query.limit? then parseInt(req.query.limit, 10) else -1 + logger.log {limit: limit}, "flushing all projects" + UpdatesManager.flushAll limit, (error, result) -> + return next(error) if error? + {failed, succeeded, all} = result + status = "#{succeeded.length} succeeded, #{failed.length} failed" + if limit == 0 + res.status(200).send "#{status}\nwould flush:\n#{all.join('\n')}\n" + else if failed.length > 0 + logger.log {failed: failed, succeeded: succeeded}, "error flushing projects" + res.status(500).send "#{status}\nfailed to flush:\n#{failed.join('\n')}\n" + else + res.status(200).send "#{status}\nflushed #{succeeded.length} projects of #{all.length}\n" + + checkDanglingUpdates: (req, res, next = (error) ->) -> + logger.log "checking dangling updates" + UpdatesManager.getDanglingUpdates (error, result) -> + return next(error) if error? + if result.length > 0 + logger.log {dangling: result}, "found dangling updates" + res.status(500).send "dangling updates:\n#{result.join('\n')}\n" + else + res.status(200).send "no dangling updates found\n" + checkDoc: (req, res, next = (error) ->) -> doc_id = req.params.doc_id project_id = req.params.project_id diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index fd901e689e..ef64a1141d 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -31,3 +31,43 @@ module.exports = RedisManager = getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> rclient.smembers Keys.docsWithHistoryOps({project_id}), callback + + # iterate over keys asynchronously using redis scan (non-blocking) + _getKeys: (pattern, callback) -> + cursor = 0 # redis iterator + keySet = {} # use hash to avoid duplicate results + # scan over all keys looking for pattern + doIteration = (cb) -> + rclient.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) -> + return callback(error) if error? + [cursor, keys] = reply + for key in keys + keySet[key] = true + if cursor == '0' # note redis returns string result not numeric + return callback(null, Object.keys(keySet)) + else + doIteration() + doIteration() + + # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b + _extractIds: (keyList) -> + ids = (key.split(":")[1] for key in keyList) + return ids + + # this will only work on single node redis, not redis cluster + getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> + return callback(new Error("not supported")) if rclient.nodes? + RedisManager._getKeys Keys.docsWithHistoryOps({project_id:"*"}), (error, project_keys) -> + return callback(error) if error? + project_ids = RedisManager._extractIds project_keys + callback(error, project_ids) + + # this will only work on single node redis, not redis cluster + getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) -> + return callback(new Error("not supported")) if rclient.nodes? + # return all the docids, to find dangling history entries after + # everything is flushed. + RedisManager._getKeys Keys.uncompressedHistoryOps({doc_id:"*"}), (error, doc_keys) -> + return callback(error) if error? + doc_ids = RedisManager._extractIds doc_keys + callback(error, doc_ids) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index f01681e5f0..48423a39ec 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -144,6 +144,38 @@ module.exports = UpdatesManager = UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb async.parallelLimit jobs, 5, callback + # flush all outstanding changes + flushAll: (limit, callback = (error, result) ->) -> + RedisManager.getProjectIdsWithHistoryOps (error, project_ids) -> + return callback(error) if error? + logger.log {count: project_ids?.length, project_ids: project_ids}, "found projects" + jobs = [] + project_ids = _.shuffle project_ids # randomise to avoid hitting same projects each time + selectedProjects = if limit < 0 then project_ids else project_ids[0...limit] + for project_id in selectedProjects + do (project_id) -> + jobs.push (cb) -> + UpdatesManager.processUncompressedUpdatesForProject project_id, (err) -> + return cb(null, {failed: err?, project_id: project_id}) + async.series jobs, (error, result) -> + return callback(error) if error? + failedProjects = (x.project_id for x in result when x.failed) + succeededProjects = (x.project_id for x in result when not x.failed) + callback(null, {failed: failedProjects, succeeded: succeededProjects, all: project_ids}) + + getDanglingUpdates: (callback = (error, doc_ids) ->) -> + RedisManager.getAllDocIdsWithHistoryOps (error, all_doc_ids) -> + return callback(error) if error? + RedisManager.getProjectIdsWithHistoryOps (error, all_project_ids) -> + return callback(error) if error? + # function to get doc_ids for each project + task = (cb) -> async.concatSeries all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb + # find the dangling doc ids + task (error, project_doc_ids) -> + dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids) + logger.log {all_doc_ids: all_doc_ids, all_project_ids: all_project_ids, project_doc_ids: project_doc_ids, dangling_doc_ids: dangling_doc_ids}, "checking for dangling doc ids" + callback(null, dangling_doc_ids) + getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> return callback(error) if error? From 35810fa702e731285ffcd8d5d5fc8efef1ecca17 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 May 2017 16:32:23 +0100 Subject: [PATCH 384/549] Revert "update shrinkwrap file" This reverts commit d16d3a1db97049cd99bfd6d965e4fab77453ae67. --- services/track-changes/npm-shrinkwrap.json | 2215 ++++++++------------ 1 file changed, 857 insertions(+), 1358 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 9c01595474..1b5bdef5df 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -2,150 +2,41 @@ "name": "history-sharelatex", "version": "0.1.4", "dependencies": { - "JSONStream": { - "version": "1.3.1", - "from": "JSONStream@1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "dependencies": { - "jsonparse": { - "version": "1.3.0", - "from": "jsonparse@1.3.0", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.0.tgz" - }, - "through": { - "version": "2.3.8", - "from": "through@2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - } - } - }, "async": { "version": "0.2.10", - "from": "async@0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" - }, - "aws-sdk": { - "version": "2.37.0", - "from": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", - "dependencies": { - "buffer": { - "version": "4.9.1", - "from": "buffer@4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "dependencies": { - "base64-js": { - "version": "1.2.0", - "from": "base64-js@1.2.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz" - }, - "ieee754": { - "version": "1.1.8", - "from": "ieee754@1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - } - } - }, - "crypto-browserify": { - "version": "1.0.9", - "from": "crypto-browserify@1.0.9", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz" - }, - "jmespath": { - "version": "0.15.0", - "from": "jmespath@0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" - }, - "querystring": { - "version": "0.2.0", - "from": "querystring@0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - }, - "sax": { - "version": "1.1.5", - "from": "sax@1.1.5", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.5.tgz" - }, - "url": { - "version": "0.10.3", - "from": "url@0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "dependencies": { - "punycode": { - "version": "1.3.2", - "from": "punycode@1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - } - } - }, - "uuid": { - "version": "3.0.0", - "from": "uuid@3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" - }, - "xml2js": { - "version": "0.4.15", - "from": "xml2js@0.4.15", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.15.tgz" - }, - "xmlbuilder": { - "version": "2.6.2", - "from": "xmlbuilder@2.6.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.6.2.tgz", - "dependencies": { - "lodash": { - "version": "3.5.0", - "from": "lodash@3.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.5.0.tgz" - } - } - } - } + "from": "async@~0.2.10" }, "bson": { "version": "0.4.23", - "from": "bson@0.4.23", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" + "from": "bson@^0.4.20" }, "byline": { "version": "4.2.2", - "from": "byline@4.2.2", - "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz" + "from": "byline@^4.2.1" }, "cli": { "version": "0.6.6", - "from": "cli@0.6.6", - "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "from": "cli@^0.6.6", "dependencies": { "glob": { "version": "3.2.11", - "from": "glob@3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "from": "glob@~ 3.2.1", "dependencies": { "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@~2.0.1" }, "minimatch": { "version": "0.3.0", - "from": "minimatch@0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "from": "minimatch@0.3", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + "from": "lru-cache@2" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + "from": "sigmund@~1.0.0" } } } @@ -153,221 +44,291 @@ }, "exit": { "version": "0.1.2", - "from": "exit@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + "from": "exit@0.1.2" } } }, "express": { "version": "3.3.5", - "from": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", + "from": "express@3.3.5", "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", "dependencies": { "connect": { "version": "2.8.5", "from": "connect@2.8.5", - "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", "dependencies": { "qs": { "version": "0.6.5", - "from": "qs@0.6.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" + "from": "qs@0.6.5" }, "formidable": { "version": "1.0.14", - "from": "formidable@1.0.14", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" + "from": "formidable@1.0.14" }, "bytes": { "version": "0.2.0", - "from": "bytes@0.2.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" + "from": "bytes@0.2.0" }, "pause": { "version": "0.0.1", - "from": "pause@0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + "from": "pause@0.0.1" }, "uid2": { "version": "0.0.2", - "from": "uid2@0.0.2", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" + "from": "uid2@0.0.2" } } }, "commander": { "version": "1.2.0", - "from": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", + "from": "commander@1.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", "dependencies": { "keypress": { "version": "0.1.0", - "from": "keypress@0.1.0", - "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" + "from": "keypress@0.1.x" } } }, "range-parser": { "version": "0.0.4", - "from": "range-parser@0.0.4", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" + "from": "range-parser@0.0.4" }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@0.3.x" }, "cookie": { "version": "0.1.0", - "from": "cookie@0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + "from": "cookie@0.1.0" }, "buffer-crc32": { "version": "0.2.1", - "from": "buffer-crc32@0.2.1", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" + "from": "buffer-crc32@0.2.1" }, "fresh": { "version": "0.2.0", - "from": "fresh@0.2.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" + "from": "fresh@0.2.0" }, "methods": { "version": "0.0.1", - "from": "methods@0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" + "from": "methods@0.0.1" }, "send": { "version": "0.1.4", "from": "send@0.1.4", - "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", "dependencies": { "mime": { "version": "1.2.11", - "from": "mime@1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + "from": "mime@~1.2.9" } } }, "cookie-signature": { "version": "1.0.1", - "from": "cookie-signature@1.0.1", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" + "from": "cookie-signature@1.0.1" }, "debug": { "version": "2.6.3", - "from": "debug@2.6.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "from": "debug@*", "dependencies": { "ms": { "version": "0.7.2", - "from": "ms@0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "from": "ms@0.7.2" } } } } }, - "heap": { - "version": "0.2.6", - "from": "heap@0.2.6", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" - }, "line-reader": { "version": "0.2.4", - "from": "line-reader@0.2.4", - "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" + "from": "line-reader@^0.2.4" + }, + "mongojs": { + "version": "1.4.1", + "from": "mongojs@^1.4.1", + "dependencies": { + "each-series": { + "version": "1.0.0", + "from": "each-series@^1.0.0" + }, + "mongodb-core": { + "version": "1.3.22-alpha4", + "from": "mongodb-core@^1.2.8", + "dependencies": { + "require_optional": { + "version": "1.0.0", + "from": "require_optional@~1.0.0", + "dependencies": { + "semver": { + "version": "5.3.0", + "from": "semver@^5.1.0" + }, + "resolve-from": { + "version": "2.0.0", + "from": "resolve-from@^2.0.0" + } + } + } + } + }, + "once": { + "version": "1.4.0", + "from": "once@^1.3.2", + "dependencies": { + "wrappy": { + "version": "1.0.2", + "from": "wrappy@1" + } + } + }, + "parse-mongo-url": { + "version": "1.1.1", + "from": "parse-mongo-url@^1.1.0" + }, + "pump": { + "version": "1.0.2", + "from": "pump@^1.0.0", + "dependencies": { + "end-of-stream": { + "version": "1.4.0", + "from": "end-of-stream@^1.1.0" + } + } + }, + "readable-stream": { + "version": "2.2.6", + "from": "readable-stream@^2.0.2", + "dependencies": { + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@^1.0.0" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@~1.0.0" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@~1.0.0" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@~2.0.1" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@~1.0.6" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@~1.0.1" + } + } + }, + "thunky": { + "version": "0.1.0", + "from": "thunky@^0.1.0" + }, + "to-mongodb-core": { + "version": "2.0.0", + "from": "to-mongodb-core@^2.0.0" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@^4.0.0" + } + } + }, + "settings-sharelatex": { + "version": "1.0.0", + "from": "settings-sharelatex@git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0" + } + } }, "logger-sharelatex": { "version": "1.5.6", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", + "from": "logger-sharelatex@git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#b2956ec56b582b9f4fc8fdda8dc00c06e77c5537", "dependencies": { "bunyan": { "version": "1.5.1", "from": "bunyan@1.5.1", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", "dependencies": { "dtrace-provider": { "version": "0.6.0", - "from": "dtrace-provider@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "from": "dtrace-provider@~0.6", "dependencies": { "nan": { - "version": "2.6.2", - "from": "nan@>=2.0.8 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz" + "version": "2.5.1", + "from": "nan@^2.0.8" } } }, "mv": { "version": "2.1.1", - "from": "mv@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "from": "mv@~2", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@~0.5.1", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "from": "minimist@0.0.8" } } }, "ncp": { "version": "2.0.0", - "from": "ncp@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz" + "from": "ncp@~2.0.0" }, "rimraf": { "version": "2.4.5", - "from": "rimraf@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "from": "rimraf@~2.4.0", "dependencies": { "glob": { "version": "6.0.4", - "from": "glob@>=6.0.1 <7.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "from": "glob@^6.0.1", "dependencies": { "inflight": { "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "from": "inflight@^1.0.4", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "from": "minimatch@2 || 3", "dependencies": { "brace-expansion": { - "version": "1.1.7", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "version": "1.1.6", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } @@ -375,20 +336,17 @@ }, "once": { "version": "1.4.0", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "from": "once@^1.3.0", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "from": "path-is-absolute@^1.0.0" } } } @@ -398,8 +356,7 @@ }, "safe-json-stringify": { "version": "1.0.4", - "from": "safe-json-stringify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz" + "from": "safe-json-stringify@~1" } } }, @@ -410,181 +367,149 @@ }, "grunt-contrib-clean": { "version": "0.6.0", - "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", + "from": "grunt-contrib-clean@^0.6.0", "dependencies": { "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.2.1 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + "from": "rimraf@~2.2.1" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@0.11.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "from": "grunt-contrib-coffee@^0.11.0", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "from": "coffee-script@~1.7.0", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "from": "chalk@~0.5.0", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + "from": "ansi-styles@^1.1.0" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "from": "escape-string-regexp@^1.0.0" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "from": "has-ansi@^0.1.0", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "from": "ansi-regex@^0.2.1" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "from": "strip-ansi@^0.3.0", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "from": "ansi-regex@^0.2.1" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + "from": "supports-color@^0.2.0" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "from": "lodash@~2.4.1" } } }, "grunt-execute": { "version": "0.2.2", - "from": "grunt-execute@>=0.2.2 <0.3.0", - "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" + "from": "grunt-execute@^0.2.2" }, "grunt-mocha-test": { "version": "0.11.0", - "from": "grunt-mocha-test@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz", + "from": "grunt-mocha-test@^0.11.0", "dependencies": { "mocha": { "version": "1.20.1", - "from": "mocha@>=1.20.0 <1.21.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz", + "from": "mocha@~1.20.0", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + "from": "commander@2.0.0" }, "growl": { "version": "1.7.0", - "from": "growl@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" + "from": "growl@1.7.x" }, "jade": { "version": "0.26.3", "from": "jade@0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + "from": "commander@0.6.1" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + "from": "mkdirp@0.3.0" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" + "from": "diff@1.0.7" }, "debug": { - "version": "2.6.4", + "version": "2.6.3", "from": "debug@*", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", "dependencies": { "ms": { - "version": "0.7.3", - "from": "ms@0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" + "version": "0.7.2", + "from": "ms@0.7.2" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@0.3.5" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "from": "minimatch@~0.2.11", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + "from": "lru-cache@2" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + "from": "sigmund@~1.0.0" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + "from": "graceful-fs@~2.0.0" }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" } } } @@ -592,87 +517,71 @@ }, "hooker": { "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + "from": "hooker@~0.2.3" }, "fs-extra": { "version": "0.9.1", - "from": "fs-extra@>=0.9.1 <0.10.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", + "from": "fs-extra@~0.9.1", "dependencies": { "ncp": { "version": "0.5.1", - "from": "ncp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" + "from": "ncp@^0.5.1" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@^0.5.0", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "from": "minimist@0.0.8" } } }, "jsonfile": { "version": "1.1.1", - "from": "jsonfile@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" + "from": "jsonfile@~1.1.0" }, "rimraf": { "version": "2.6.1", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "from": "rimraf@^2.2.8", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "from": "glob@^7.0.5", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "from": "fs.realpath@^1.0.0" }, "inflight": { "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "from": "inflight@^1.0.4", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "from": "minimatch@^3.0.2", "dependencies": { "brace-expansion": { - "version": "1.1.7", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "version": "1.1.6", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } @@ -680,20 +589,17 @@ }, "once": { "version": "1.4.0", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "from": "once@^1.3.0", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "from": "path-is-absolute@^1.0.0" } } } @@ -704,234 +610,439 @@ } }, "raven": { - "version": "1.2.1", - "from": "raven@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "version": "1.2.0", + "from": "raven@^1.1.3", "dependencies": { "cookie": { "version": "0.3.1", - "from": "cookie@0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" + "from": "cookie@0.3.1" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + "from": "json-stringify-safe@5.0.1" }, "lsmod": { "version": "1.0.0", - "from": "lsmod@1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz" + "from": "lsmod@1.0.0" }, "uuid": { "version": "3.0.0", - "from": "uuid@3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + "from": "uuid@3.0.0" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + "from": "stack-trace@0.0.9" } } }, "timekeeper": { "version": "1.0.0", - "from": "timekeeper@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" + "from": "timekeeper@^1.0.0" } } }, "metrics-sharelatex": { "version": "1.7.1", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", + "from": "metrics-sharelatex@git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#166961924c599b1f9468f2e17846fa2a9d12372d", "dependencies": { "lynx": { "version": "0.1.1", - "from": "lynx@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "from": "lynx@~0.1.1", "dependencies": { "mersenne": { "version": "0.0.3", - "from": "mersenne@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.3.tgz" + "from": "mersenne@~0.0.3" }, "statsd-parser": { "version": "0.0.4", - "from": "statsd-parser@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + "from": "statsd-parser@~0.0.4" } } }, "coffee-script": { "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + "from": "coffee-script@1.6.0" }, "underscore": { "version": "1.6.0", - "from": "underscore@>=1.6.0 <1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + "from": "underscore@~1.6.0" } } }, - "mongo-uri": { - "version": "0.1.2", - "from": "mongo-uri@0.1.2", - "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" - }, - "mongojs": { - "version": "1.4.1", - "from": "mongojs@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-1.4.1.tgz", + "request": { + "version": "2.33.0", + "from": "request@~2.33.0", "dependencies": { - "each-series": { - "version": "1.0.0", - "from": "each-series@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + "qs": { + "version": "0.6.6", + "from": "qs@~0.6.0" }, - "mongodb-core": { - "version": "1.3.21", - "from": "mongodb-core@>=1.2.8 <2.0.0", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.21.tgz", + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@~5.0.0" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0" + }, + "node-uuid": { + "version": "1.4.8", + "from": "node-uuid@~1.4.0" + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.9" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@>=0.12.0", "dependencies": { - "require_optional": { - "version": "1.0.0", - "from": "require_optional@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz", + "punycode": { + "version": "1.4.1", + "from": "punycode@^1.4.1" + } + } + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@~0.1.0", + "dependencies": { + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@~0.0.4", "dependencies": { - "semver": { - "version": "5.3.0", - "from": "semver@>=5.1.0 <6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" - }, - "resolve-from": { - "version": "2.0.0", - "from": "resolve-from@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5" } } + }, + "async": { + "version": "0.9.2", + "from": "async@~0.9.0" } } }, - "once": { - "version": "1.4.0", - "from": "once@>=1.3.2 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "tunnel-agent": { + "version": "0.3.0", + "from": "tunnel-agent@~0.3.0" + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@~0.10.0", "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@^0.1.5" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11" + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3" } } }, - "parse-mongo-url": { - "version": "1.1.1", - "from": "parse-mongo-url@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@~0.3.0" }, - "pump": { - "version": "1.0.2", - "from": "pump@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", + "hawk": { + "version": "1.0.0", + "from": "hawk@~1.0.0", "dependencies": { - "end-of-stream": { - "version": "1.4.0", - "from": "end-of-stream@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz" + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x" } } }, - "readable-stream": { - "version": "2.2.9", - "from": "readable-stream@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "string_decoder": { - "version": "1.0.0", - "from": "string_decoder@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - }, - "thunky": { - "version": "0.1.0", - "from": "thunky@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" - }, - "to-mongodb-core": { - "version": "2.0.0", - "from": "to-mongodb-core@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0" } } }, - "redis": { - "version": "0.10.3", - "from": "redis@0.10.3", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" + "requestretry": { + "version": "1.12.0", + "from": "requestretry@^1.12.0", + "dependencies": { + "extend": { + "version": "3.0.0", + "from": "extend@^3.0.0" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@^4.15.0" + }, + "request": { + "version": "2.81.0", + "from": "request@^2.74.0", + "dependencies": { + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@~0.6.0" + }, + "aws4": { + "version": "1.6.0", + "from": "aws4@^1.2.1" + }, + "caseless": { + "version": "0.12.0", + "from": "caseless@~0.12.0" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@~1.0.5", + "dependencies": { + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@~1.0.0" + } + } + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@~0.6.1" + }, + "form-data": { + "version": "2.1.2", + "from": "form-data@~2.1.1", + "dependencies": { + "asynckit": { + "version": "0.4.0", + "from": "asynckit@^0.4.0" + } + } + }, + "har-validator": { + "version": "4.2.1", + "from": "har-validator@~4.2.1", + "dependencies": { + "ajv": { + "version": "4.11.5", + "from": "ajv@^4.9.1", + "dependencies": { + "co": { + "version": "4.6.0", + "from": "co@^4.6.0" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@^1.0.1", + "dependencies": { + "jsonify": { + "version": "0.0.0", + "from": "jsonify@~0.0.0" + } + } + } + } + }, + "har-schema": { + "version": "1.0.5", + "from": "har-schema@^1.0.5" + } + } + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@~3.1.3", + "dependencies": { + "hoek": { + "version": "2.16.3", + "from": "hoek@2.x.x" + }, + "boom": { + "version": "2.10.1", + "from": "boom@2.x.x" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@2.x.x" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@1.x.x" + } + } + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@~1.1.0", + "dependencies": { + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@^0.2.0" + }, + "jsprim": { + "version": "1.4.0", + "from": "jsprim@^1.2.2", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0" + }, + "extsprintf": { + "version": "1.0.2", + "from": "extsprintf@1.0.2" + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3" + }, + "verror": { + "version": "1.3.6", + "from": "verror@1.3.6" + } + } + }, + "sshpk": { + "version": "1.11.0", + "from": "sshpk@^1.7.0", + "dependencies": { + "asn1": { + "version": "0.2.3", + "from": "asn1@~0.2.3" + }, + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@^1.0.0" + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@^1.12.0" + }, + "getpass": { + "version": "0.1.6", + "from": "getpass@^0.1.1" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@~0.1.0" + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@~0.14.0" + }, + "jodid25519": { + "version": "1.0.2", + "from": "jodid25519@^1.0.0" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@~0.1.1" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "bcrypt-pbkdf@^1.0.0" + } + } + } + } + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@~1.0.0" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@~0.1.2" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@~5.0.1" + }, + "mime-types": { + "version": "2.1.15", + "from": "mime-types@~2.1.7", + "dependencies": { + "mime-db": { + "version": "1.27.0", + "from": "mime-db@~1.27.0" + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@~0.8.1" + }, + "performance-now": { + "version": "0.2.0", + "from": "performance-now@^0.2.0" + }, + "qs": { + "version": "6.4.0", + "from": "qs@~6.4.0" + }, + "safe-buffer": { + "version": "5.0.1", + "from": "safe-buffer@^5.0.1" + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@~0.0.4" + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@~2.3.0", + "dependencies": { + "punycode": { + "version": "1.4.1", + "from": "punycode@^1.4.1" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@^0.6.0" + }, + "uuid": { + "version": "3.0.1", + "from": "uuid@^3.0.0" + } + } + }, + "when": { + "version": "3.7.8", + "from": "when@^3.7.7" + } + } }, "redis-sharelatex": { - "version": "1.0.0", - "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.0", - "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#f6bc41c578d0830827127d89e2bcd53587f02fa8", + "version": "0.0.9", + "from": "redis-sharelatex@~0.0.9", "dependencies": { "chai": { "version": "1.9.1", "from": "chai@1.9.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", "dependencies": { "assertion-error": { "version": "1.0.0", - "from": "assertion-error@1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" + "from": "assertion-error@1.0.0" }, "deep-eql": { "version": "0.1.3", "from": "deep-eql@0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "dependencies": { "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + "from": "type-detect@0.1.1" } } } @@ -940,180 +1051,148 @@ "coffee-script": { "version": "1.8.0", "from": "coffee-script@1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@0.11.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "from": "grunt-contrib-coffee@^0.11.0", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "from": "coffee-script@~1.7.0", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@~0.3.5" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "from": "chalk@~0.5.0", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + "from": "ansi-styles@^1.1.0" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + "from": "escape-string-regexp@^1.0.0" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "from": "has-ansi@^0.1.0", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "from": "ansi-regex@^0.2.0" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "from": "strip-ansi@^0.3.0", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + "from": "ansi-regex@^0.2.0" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + "from": "supports-color@^0.2.0" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + "from": "lodash@~2.4.1" } } }, "grunt-mocha-test": { "version": "0.12.0", "from": "grunt-mocha-test@0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", "dependencies": { "hooker": { "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + "from": "hooker@~0.2.3" }, "fs-extra": { "version": "0.11.1", - "from": "fs-extra@>=0.11.1 <0.12.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "from": "fs-extra@~0.11.1", "dependencies": { "ncp": { "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + "from": "ncp@^0.6.0" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@^0.5.0", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "from": "minimist@0.0.8" } } }, "jsonfile": { "version": "2.4.0", - "from": "jsonfile@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "from": "jsonfile@^2.0.0", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@>=4.1.6 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + "from": "graceful-fs@^4.1.6" } } }, "rimraf": { "version": "2.6.1", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "from": "rimraf@^2.2.8", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "from": "glob@^7.0.5", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "from": "fs.realpath@^1.0.0" }, "inflight": { "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "from": "inflight@^1.0.4", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "from": "minimatch@2 || 3", "dependencies": { "brace-expansion": { - "version": "1.1.7", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "version": "1.1.6", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } @@ -1121,20 +1200,17 @@ }, "once": { "version": "1.4.0", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "from": "once@^1.3.0", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "from": "path-is-absolute@^1.0.0" } } } @@ -1144,145 +1220,75 @@ } } }, - "ioredis": { - "version": "2.5.0", - "from": "ioredis@>=2.5.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.5.0.tgz", - "dependencies": { - "bluebird": { - "version": "3.5.0", - "from": "bluebird@>=3.3.4 <4.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz" - }, - "cluster-key-slot": { - "version": "1.0.8", - "from": "cluster-key-slot@>=1.0.6 <2.0.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz" - }, - "debug": { - "version": "2.6.4", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", - "dependencies": { - "ms": { - "version": "0.7.3", - "from": "ms@0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" - } - } - }, - "double-ended-queue": { - "version": "2.1.0-0", - "from": "double-ended-queue@>=2.1.0-0 <3.0.0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" - }, - "flexbuffer": { - "version": "0.0.6", - "from": "flexbuffer@0.0.6", - "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" - }, - "lodash": { - "version": "4.17.4", - "from": "lodash@>=4.8.2 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" - }, - "redis-commands": { - "version": "1.3.1", - "from": "redis-commands@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz" - }, - "redis-parser": { - "version": "1.3.0", - "from": "redis-parser@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz" - } - } - }, "mocha": { "version": "1.21.4", "from": "mocha@1.21.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + "from": "commander@2.0.0" }, "growl": { "version": "1.8.1", - "from": "growl@>=1.8.0 <1.9.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" + "from": "growl@1.8.x" }, "jade": { "version": "0.26.3", "from": "jade@0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + "from": "commander@0.6.1" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + "from": "mkdirp@0.3.0" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" + "from": "diff@1.0.7" }, "debug": { - "version": "2.6.4", + "version": "2.6.3", "from": "debug@*", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.4.tgz", "dependencies": { "ms": { - "version": "0.7.3", - "from": "ms@0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" + "version": "0.7.2", + "from": "ms@0.7.2" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + "from": "mkdirp@0.3.5" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "from": "minimatch@~0.2.11", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + "from": "lru-cache@2" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + "from": "sigmund@~1.0.0" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + "from": "graceful-fs@~2.0.0" }, "inherits": { "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" } } } @@ -1290,69 +1296,57 @@ }, "redis": { "version": "0.12.1", - "from": "redis@0.12.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + "from": "redis@0.12.1" }, "redis-sentinel": { "version": "0.1.1", "from": "redis-sentinel@0.1.1", - "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "dependencies": { "redis": { "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" + "from": "redis@0.11.x" }, "q": { "version": "0.9.2", - "from": "q@0.9.2", - "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" + "from": "q@0.9.2" } } }, "sandboxed-module": { "version": "1.0.1", "from": "sandboxed-module@1.0.1", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", - "from": "require-like@0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" + "from": "require-like@0.1.2" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + "from": "stack-trace@0.0.9" } } }, "sinon": { "version": "1.10.3", "from": "sinon@1.10.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "formatio@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", + "from": "formatio@~1.0", "dependencies": { "samsam": { "version": "1.1.3", - "from": "samsam@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" + "from": "samsam@~1.1" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1.0.0", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "from": "util@>=0.10.3 <1", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "from": "inherits@2.0.1" } } } @@ -1360,566 +1354,125 @@ } } }, - "request": { - "version": "2.33.0", - "from": "request@2.33.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", - "dependencies": { - "qs": { - "version": "0.6.6", - "from": "qs@0.6.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "forever-agent": { - "version": "0.5.2", - "from": "forever-agent@0.5.2", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" - }, - "node-uuid": { - "version": "1.4.8", - "from": "node-uuid@1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" - }, - "mime": { - "version": "1.2.11", - "from": "mime@1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - } - } - }, - "form-data": { - "version": "0.1.4", - "from": "form-data@0.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", - "dependencies": { - "combined-stream": { - "version": "0.0.7", - "from": "combined-stream@0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "dependencies": { - "delayed-stream": { - "version": "0.0.5", - "from": "delayed-stream@0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" - } - } - }, - "async": { - "version": "0.9.2", - "from": "async@0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" - } - } - }, - "tunnel-agent": { - "version": "0.3.0", - "from": "tunnel-agent@0.3.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" - }, - "http-signature": { - "version": "0.10.1", - "from": "http-signature@0.10.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", - "dependencies": { - "assert-plus": { - "version": "0.1.5", - "from": "assert-plus@0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" - }, - "asn1": { - "version": "0.1.11", - "from": "asn1@0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" - }, - "ctype": { - "version": "0.5.3", - "from": "ctype@0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" - } - } - }, - "oauth-sign": { - "version": "0.3.0", - "from": "oauth-sign@0.3.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" - }, - "hawk": { - "version": "1.0.0", - "from": "hawk@1.0.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", - "dependencies": { - "hoek": { - "version": "0.9.1", - "from": "hoek@0.9.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" - }, - "boom": { - "version": "0.4.2", - "from": "boom@0.4.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" - }, - "cryptiles": { - "version": "0.2.2", - "from": "cryptiles@0.2.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" - }, - "sntp": { - "version": "0.2.4", - "from": "sntp@0.2.4", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" - } - } - }, - "aws-sign2": { - "version": "0.5.0", - "from": "aws-sign2@0.5.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" - } - } + "redis": { + "version": "0.10.3", + "from": "redis@~0.10.1" }, - "requestretry": { - "version": "1.12.0", - "from": "requestretry@1.12.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.12.0.tgz", - "dependencies": { - "extend": { - "version": "3.0.0", - "from": "extend@3.0.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "lodash": { - "version": "4.17.4", - "from": "lodash@4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" - }, - "request": { - "version": "2.81.0", - "from": "request@2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.6.0", - "from": "aws4@1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" - }, - "caseless": { - "version": "0.12.0", - "from": "caseless@0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - } - } - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "2.1.2", - "from": "form-data@2.1.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", - "dependencies": { - "asynckit": { - "version": "0.4.0", - "from": "asynckit@0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - } - } - }, - "har-validator": { - "version": "4.2.1", - "from": "har-validator@4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "dependencies": { - "ajv": { - "version": "4.11.5", - "from": "ajv@4.11.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", - "dependencies": { - "co": { - "version": "4.6.0", - "from": "co@4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" - }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "json-stable-stringify@1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "dependencies": { - "jsonify": { - "version": "0.0.0", - "from": "jsonify@0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "from": "har-schema@1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "hoek@2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "boom@2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "jsprim": { - "version": "1.4.0", - "from": "jsprim@1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "json-schema": { - "version": "0.2.3", - "from": "json-schema@0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - } - } - }, - "sshpk": { - "version": "1.11.0", - "from": "sshpk@1.11.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "asn1@0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "dashdash": { - "version": "1.14.1", - "from": "dashdash@1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - }, - "getpass": { - "version": "0.1.6", - "from": "getpass@0.1.6", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" - }, - "jsbn": { - "version": "0.1.1", - "from": "jsbn@0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "tweetnacl@0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "from": "bcrypt-pbkdf@1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "mime-types": { - "version": "2.1.15", - "from": "mime-types@2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "dependencies": { - "mime-db": { - "version": "1.27.0", - "from": "mime-db@1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "from": "oauth-sign@0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" - }, - "performance-now": { - "version": "0.2.0", - "from": "performance-now@0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" - }, - "qs": { - "version": "6.4.0", - "from": "qs@6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" - }, - "safe-buffer": { - "version": "5.0.1", - "from": "safe-buffer@5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - }, - "uuid": { - "version": "3.0.1", - "from": "uuid@3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" - } - } - }, - "when": { - "version": "3.7.8", - "from": "when@3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" - } - } + "underscore": { + "version": "1.7.0", + "from": "underscore@~1.7.0" + }, + "mongo-uri": { + "version": "0.1.2", + "from": "mongo-uri@^0.1.2" }, "s3-streams": { "version": "0.3.0", - "from": "s3-streams@0.3.0", - "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", + "from": "s3-streams@^0.3.0", "dependencies": { "lodash": { "version": "3.10.1", - "from": "lodash@3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + "from": "lodash@^3.9.3" }, "readable-stream": { "version": "2.2.6", - "from": "readable-stream@2.2.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "from": "readable-stream@^2.0.0", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + "from": "buffer-shims@^1.0.0" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + "from": "core-util-is@~1.0.0" }, "isarray": { "version": "1.0.0", - "from": "isarray@1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "from": "isarray@~1.0.0" }, "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@~2.0.1" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + "from": "process-nextick-args@~1.0.6" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "from": "string_decoder@~0.10.x" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "from": "util-deprecate@~1.0.1" } } }, "bluebird": { "version": "2.11.0", - "from": "bluebird@2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz" + "from": "bluebird@^2.9.27" } } }, - "settings-sharelatex": { - "version": "1.0.0", - "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "JSONStream": { + "version": "1.3.1", + "from": "JSONStream@^1.0.4", "dependencies": { - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + "jsonparse": { + "version": "1.3.0", + "from": "jsonparse@^1.2.0" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.2.7 <3" } } }, - "underscore": { - "version": "1.7.0", - "from": "underscore@1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + "heap": { + "version": "0.2.6", + "from": "heap@^0.2.6" }, "v8-profiler": { "version": "5.7.0", - "from": "v8-profiler@5.7.0", - "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "from": "v8-profiler@^5.6.5", "dependencies": { "nan": { "version": "2.5.1", - "from": "nan@2.5.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz" + "from": "nan@^2.5.1" }, "node-pre-gyp": { "version": "0.6.34", - "from": "node-pre-gyp@0.6.34", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz", + "from": "node-pre-gyp@^0.6.34", "dependencies": { "mkdirp": { "version": "0.5.1", - "from": "mkdirp@0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "from": "mkdirp@^0.5.1", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "from": "minimist@0.0.8" } } }, "nopt": { "version": "4.0.1", - "from": "nopt@4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "from": "nopt@^4.0.1", "dependencies": { "abbrev": { "version": "1.1.0", - "from": "abbrev@1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz" + "from": "abbrev@1" }, "osenv": { "version": "0.1.4", - "from": "osenv@0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "from": "osenv@^0.1.4", "dependencies": { "os-homedir": { "version": "1.0.2", - "from": "os-homedir@1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + "from": "os-homedir@^1.0.0" }, "os-tmpdir": { "version": "1.0.2", - "from": "os-tmpdir@1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + "from": "os-tmpdir@^1.0.0" } } } @@ -1927,58 +1480,47 @@ }, "npmlog": { "version": "4.0.2", - "from": "npmlog@4.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.0.2.tgz", + "from": "npmlog@^4.0.2", "dependencies": { "are-we-there-yet": { "version": "1.1.2", - "from": "are-we-there-yet@1.1.2", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz", + "from": "are-we-there-yet@~1.1.2", "dependencies": { "delegates": { "version": "1.0.0", - "from": "delegates@1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + "from": "delegates@^1.0.0" }, "readable-stream": { "version": "2.2.6", - "from": "readable-stream@2.2.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "from": "readable-stream@^2.0.0 || ^1.1.13", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + "from": "buffer-shims@^1.0.0" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + "from": "core-util-is@~1.0.0" }, "isarray": { "version": "1.0.0", - "from": "isarray@1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "from": "isarray@~1.0.0" }, "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@~2.0.1" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + "from": "process-nextick-args@~1.0.6" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "from": "string_decoder@~0.10.x" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "from": "util-deprecate@~1.0.1" } } } @@ -1986,53 +1528,43 @@ }, "console-control-strings": { "version": "1.1.0", - "from": "console-control-strings@1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + "from": "console-control-strings@~1.1.0" }, "gauge": { "version": "2.7.3", - "from": "gauge@2.7.3", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.3.tgz", + "from": "gauge@~2.7.1", "dependencies": { "aproba": { "version": "1.1.1", - "from": "aproba@1.1.1", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz" + "from": "aproba@^1.0.3" }, "has-unicode": { "version": "2.0.1", - "from": "has-unicode@2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + "from": "has-unicode@^2.0.0" }, "object-assign": { "version": "4.1.1", - "from": "object-assign@4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + "from": "object-assign@^4.1.0" }, "signal-exit": { "version": "3.0.2", - "from": "signal-exit@3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + "from": "signal-exit@^3.0.0" }, "string-width": { "version": "1.0.2", - "from": "string-width@1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "from": "string-width@^1.0.1", "dependencies": { "code-point-at": { "version": "1.1.0", - "from": "code-point-at@1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + "from": "code-point-at@^1.0.0" }, "is-fullwidth-code-point": { "version": "1.0.0", - "from": "is-fullwidth-code-point@1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "from": "is-fullwidth-code-point@^1.0.0", "dependencies": { "number-is-nan": { "version": "1.0.1", - "from": "number-is-nan@1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + "from": "number-is-nan@^1.0.0" } } } @@ -2040,135 +1572,112 @@ }, "strip-ansi": { "version": "3.0.1", - "from": "strip-ansi@3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "from": "strip-ansi@^3.0.1", "dependencies": { "ansi-regex": { "version": "2.1.1", - "from": "ansi-regex@2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + "from": "ansi-regex@^2.0.0" } } }, "wide-align": { "version": "1.1.0", - "from": "wide-align@1.1.0", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" + "from": "wide-align@^1.1.0" } } }, "set-blocking": { "version": "2.0.0", - "from": "set-blocking@2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + "from": "set-blocking@~2.0.0" } } }, "rc": { "version": "1.2.1", - "from": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "from": "rc@^1.1.7", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", "dependencies": { "deep-extend": { "version": "0.4.1", - "from": "deep-extend@0.4.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" + "from": "deep-extend@~0.4.0" }, "ini": { "version": "1.3.4", - "from": "ini@1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + "from": "ini@~1.3.0" }, "minimist": { "version": "1.2.0", - "from": "minimist@1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + "from": "minimist@^1.2.0" }, "strip-json-comments": { "version": "2.0.1", - "from": "strip-json-comments@2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + "from": "strip-json-comments@~2.0.1" } } }, "request": { "version": "2.81.0", - "from": "request@2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "from": "request@^2.81.0", "dependencies": { "aws-sign2": { "version": "0.6.0", - "from": "aws-sign2@0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + "from": "aws-sign2@~0.6.0" }, "aws4": { "version": "1.6.0", - "from": "aws4@1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz" + "from": "aws4@^1.2.1" }, "caseless": { "version": "0.12.0", - "from": "caseless@0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + "from": "caseless@~0.12.0" }, "combined-stream": { "version": "1.0.5", - "from": "combined-stream@1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "from": "combined-stream@~1.0.5", "dependencies": { "delayed-stream": { "version": "1.0.0", - "from": "delayed-stream@1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + "from": "delayed-stream@~1.0.0" } } }, "extend": { "version": "3.0.0", - "from": "extend@3.0.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" + "from": "extend@~3.0.0" }, "forever-agent": { "version": "0.6.1", - "from": "forever-agent@0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + "from": "forever-agent@~0.6.1" }, "form-data": { "version": "2.1.2", - "from": "form-data@2.1.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "from": "form-data@~2.1.1", "dependencies": { "asynckit": { "version": "0.4.0", - "from": "asynckit@0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + "from": "asynckit@^0.4.0" } } }, "har-validator": { "version": "4.2.1", - "from": "har-validator@4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "from": "har-validator@~4.2.1", "dependencies": { "ajv": { "version": "4.11.5", - "from": "ajv@4.11.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.5.tgz", + "from": "ajv@^4.9.1", "dependencies": { "co": { "version": "4.6.0", - "from": "co@4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + "from": "co@^4.6.0" }, "json-stable-stringify": { "version": "1.0.1", - "from": "json-stable-stringify@1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "from": "json-stable-stringify@^1.0.1", "dependencies": { "jsonify": { "version": "0.0.0", - "from": "jsonify@0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + "from": "jsonify@~0.0.0" } } } @@ -2176,124 +1685,101 @@ }, "har-schema": { "version": "1.0.5", - "from": "har-schema@1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + "from": "har-schema@^1.0.5" } } }, "hawk": { "version": "3.1.3", - "from": "hawk@3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "from": "hawk@~3.1.3", "dependencies": { "hoek": { "version": "2.16.3", - "from": "hoek@2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + "from": "hoek@2.x.x" }, "boom": { "version": "2.10.1", - "from": "boom@2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + "from": "boom@2.x.x" }, "cryptiles": { "version": "2.0.5", - "from": "cryptiles@2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + "from": "cryptiles@2.x.x" }, "sntp": { "version": "1.0.9", - "from": "sntp@1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + "from": "sntp@1.x.x" } } }, "http-signature": { "version": "1.1.1", - "from": "http-signature@1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "from": "http-signature@~1.1.0", "dependencies": { "assert-plus": { "version": "0.2.0", - "from": "assert-plus@0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + "from": "assert-plus@^0.2.0" }, "jsprim": { "version": "1.4.0", - "from": "jsprim@1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "from": "jsprim@^1.2.2", "dependencies": { "assert-plus": { "version": "1.0.0", - "from": "assert-plus@1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "from": "assert-plus@1.0.0" }, "extsprintf": { "version": "1.0.2", - "from": "extsprintf@1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + "from": "extsprintf@1.0.2" }, "json-schema": { "version": "0.2.3", - "from": "json-schema@0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + "from": "json-schema@0.2.3" }, "verror": { "version": "1.3.6", - "from": "verror@1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" + "from": "verror@1.3.6" } } }, "sshpk": { "version": "1.11.0", - "from": "sshpk@1.11.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.11.0.tgz", + "from": "sshpk@^1.7.0", "dependencies": { "asn1": { "version": "0.2.3", - "from": "asn1@0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" + "from": "asn1@~0.2.3" }, "assert-plus": { "version": "1.0.0", - "from": "assert-plus@1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + "from": "assert-plus@^1.0.0" }, "dashdash": { "version": "1.14.1", - "from": "dashdash@1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" + "from": "dashdash@^1.12.0" }, "getpass": { "version": "0.1.6", - "from": "getpass@0.1.6", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz" + "from": "getpass@^0.1.1" }, "jsbn": { "version": "0.1.1", - "from": "jsbn@0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + "from": "jsbn@~0.1.0" }, "tweetnacl": { "version": "0.14.5", - "from": "tweetnacl@0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + "from": "tweetnacl@~0.14.0" }, "jodid25519": { "version": "1.0.2", - "from": "jodid25519@1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz" + "from": "jodid25519@^1.0.0" }, "ecc-jsbn": { "version": "0.1.1", - "from": "ecc-jsbn@0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz" + "from": "ecc-jsbn@~0.1.1" }, "bcrypt-pbkdf": { "version": "1.0.1", - "from": "bcrypt-pbkdf@1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz" + "from": "bcrypt-pbkdf@^1.0.0" } } } @@ -2301,131 +1787,107 @@ }, "is-typedarray": { "version": "1.0.0", - "from": "is-typedarray@1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + "from": "is-typedarray@~1.0.0" }, "isstream": { "version": "0.1.2", - "from": "isstream@0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + "from": "isstream@~0.1.2" }, "json-stringify-safe": { "version": "5.0.1", - "from": "json-stringify-safe@5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + "from": "json-stringify-safe@~5.0.1" }, "mime-types": { "version": "2.1.15", - "from": "mime-types@2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "from": "mime-types@~2.1.7", "dependencies": { "mime-db": { "version": "1.27.0", - "from": "mime-db@1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" + "from": "mime-db@~1.27.0" } } }, "oauth-sign": { "version": "0.8.2", - "from": "oauth-sign@0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + "from": "oauth-sign@~0.8.1" }, "performance-now": { "version": "0.2.0", - "from": "performance-now@0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + "from": "performance-now@^0.2.0" }, "qs": { "version": "6.4.0", - "from": "qs@6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + "from": "qs@~6.4.0" }, "safe-buffer": { "version": "5.0.1", - "from": "safe-buffer@5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz" + "from": "safe-buffer@^5.0.1" }, "stringstream": { "version": "0.0.5", - "from": "stringstream@0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" + "from": "stringstream@~0.0.4" }, "tough-cookie": { "version": "2.3.2", - "from": "tough-cookie@2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "from": "tough-cookie@~2.3.0", "dependencies": { "punycode": { "version": "1.4.1", - "from": "punycode@1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + "from": "punycode@^1.4.1" } } }, "tunnel-agent": { "version": "0.6.0", - "from": "tunnel-agent@0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + "from": "tunnel-agent@^0.6.0" }, "uuid": { "version": "3.0.1", - "from": "uuid@3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" + "from": "uuid@^3.0.0" } } }, "rimraf": { "version": "2.6.1", - "from": "rimraf@2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "from": "rimraf@^2.6.1", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "from": "glob@^7.0.5", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "from": "fs.realpath@^1.0.0" }, "inflight": { "version": "1.0.6", - "from": "inflight@1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "from": "inflight@^1.0.4", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@3.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "from": "minimatch@^3.0.2", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@1.1.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } @@ -2433,20 +1895,17 @@ }, "once": { "version": "1.4.0", - "from": "once@1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "from": "once@^1.3.0", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "from": "path-is-absolute@^1.0.0" } } } @@ -2454,101 +1913,83 @@ }, "semver": { "version": "5.3.0", - "from": "semver@5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz" + "from": "semver@^5.3.0" }, "tar": { "version": "2.2.1", - "from": "tar@2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "from": "tar@^2.2.1", "dependencies": { "block-stream": { "version": "0.0.9", - "from": "block-stream@0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" + "from": "block-stream@*" }, "fstream": { "version": "1.0.11", - "from": "fstream@1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "from": "fstream@^1.0.2", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + "from": "graceful-fs@^4.1.2" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" } } }, "tar-pack": { "version": "3.4.0", - "from": "tar-pack@3.4.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "from": "tar-pack@^3.4.0", "dependencies": { "debug": { "version": "2.6.3", - "from": "debug@2.6.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz", + "from": "debug@^2.2.0", "dependencies": { "ms": { "version": "0.7.2", - "from": "ms@0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz" + "from": "ms@0.7.2" } } }, "fstream": { "version": "1.0.11", - "from": "fstream@1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "from": "fstream@^1.0.10", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + "from": "graceful-fs@^4.1.2" }, "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" } } }, "fstream-ignore": { "version": "1.0.5", - "from": "fstream-ignore@1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "from": "fstream-ignore@^1.0.5", "dependencies": { "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@2" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@3.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "from": "minimatch@^3.0.0", "dependencies": { "brace-expansion": { "version": "1.1.6", - "from": "brace-expansion@1.1.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "from": "brace-expansion@^1.0.0", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" + "from": "balanced-match@^0.4.1" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "from": "concat-map@0.0.1" } } } @@ -2558,68 +1999,126 @@ }, "once": { "version": "1.4.0", - "from": "once@1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "from": "once@^1.3.3", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "from": "wrappy@1" } } }, "readable-stream": { "version": "2.2.6", - "from": "readable-stream@2.2.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.6.tgz", + "from": "readable-stream@^2.1.4", "dependencies": { "buffer-shims": { "version": "1.0.0", - "from": "buffer-shims@1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + "from": "buffer-shims@^1.0.0" }, "core-util-is": { "version": "1.0.2", - "from": "core-util-is@1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + "from": "core-util-is@~1.0.0" }, "isarray": { "version": "1.0.0", - "from": "isarray@1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + "from": "isarray@~1.0.0" }, "inherits": { "version": "2.0.3", - "from": "inherits@2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + "from": "inherits@~2.0.1" }, "process-nextick-args": { "version": "1.0.7", - "from": "process-nextick-args@1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + "from": "process-nextick-args@~1.0.6" }, "string_decoder": { "version": "0.10.31", - "from": "string_decoder@0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "from": "string_decoder@~0.10.x" }, "util-deprecate": { "version": "1.0.2", - "from": "util-deprecate@1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + "from": "util-deprecate@~1.0.1" } } }, "uid-number": { "version": "0.0.6", - "from": "uid-number@0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + "from": "uid-number@^0.0.6" } } } } } } + }, + "aws-sdk": { + "version": "2.37.0", + "from": "aws-sdk@^2.1.34", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", + "dependencies": { + "buffer": { + "version": "4.9.1", + "from": "buffer@4.9.1", + "dependencies": { + "base64-js": { + "version": "1.2.0", + "from": "base64-js@^1.0.2" + }, + "ieee754": { + "version": "1.1.8", + "from": "ieee754@^1.1.4" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@^1.0.0" + } + } + }, + "crypto-browserify": { + "version": "1.0.9", + "from": "crypto-browserify@1.0.9" + }, + "jmespath": { + "version": "0.15.0", + "from": "jmespath@0.15.0" + }, + "querystring": { + "version": "0.2.0", + "from": "querystring@0.2.0" + }, + "sax": { + "version": "1.1.5", + "from": "sax@1.1.5" + }, + "url": { + "version": "0.10.3", + "from": "url@0.10.3", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@1.3.2" + } + } + }, + "uuid": { + "version": "3.0.0", + "from": "uuid@3.0.0" + }, + "xml2js": { + "version": "0.4.15", + "from": "xml2js@0.4.15" + }, + "xmlbuilder": { + "version": "2.6.2", + "from": "xmlbuilder@2.6.2", + "dependencies": { + "lodash": { + "version": "3.5.0", + "from": "lodash@~3.5.0" + } + } + } + } } } } From e0ed321d7b38b76c05d6b79561a33fab4a8ff517 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 May 2017 16:37:02 +0100 Subject: [PATCH 385/549] fix shrinkwrap file to add redis-sharelatex --- services/track-changes/npm-shrinkwrap.json | 244 +++++++++++++++------ 1 file changed, 183 insertions(+), 61 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 1b5bdef5df..9cb3741716 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1025,24 +1025,29 @@ } }, "redis-sharelatex": { - "version": "0.0.9", - "from": "redis-sharelatex@~0.0.9", + "version": "1.0.0", + "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.0", + "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#0cd669a9ed73c0330e3b912fc9d9a42dae3c4fbd", "dependencies": { "chai": { "version": "1.9.1", "from": "chai@1.9.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", "dependencies": { "assertion-error": { "version": "1.0.0", - "from": "assertion-error@1.0.0" + "from": "assertion-error@1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" }, "deep-eql": { "version": "0.1.3", "from": "deep-eql@0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "dependencies": { "type-detect": { "version": "0.1.1", - "from": "type-detect@0.1.1" + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" } } } @@ -1051,148 +1056,180 @@ "coffee-script": { "version": "1.8.0", "from": "coffee-script@1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@~0.3.5" + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "grunt-contrib-coffee": { "version": "0.11.1", - "from": "grunt-contrib-coffee@^0.11.0", + "from": "grunt-contrib-coffee@0.11.1", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { "version": "1.7.1", - "from": "coffee-script@~1.7.0", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", "dependencies": { "mkdirp": { "version": "0.3.5", - "from": "mkdirp@~0.3.5" + "from": "mkdirp@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" } } }, "chalk": { "version": "0.5.1", - "from": "chalk@~0.5.0", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", "dependencies": { "ansi-styles": { "version": "1.1.0", - "from": "ansi-styles@^1.1.0" + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "escape-string-regexp": { "version": "1.0.5", - "from": "escape-string-regexp@^1.0.0" + "from": "escape-string-regexp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" }, "has-ansi": { "version": "0.1.0", - "from": "has-ansi@^0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.0" + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "strip-ansi": { "version": "0.3.0", - "from": "strip-ansi@^0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", "dependencies": { "ansi-regex": { "version": "0.2.1", - "from": "ansi-regex@^0.2.0" + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" } } }, "supports-color": { "version": "0.2.0", - "from": "supports-color@^0.2.0" + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" } } }, "lodash": { "version": "2.4.2", - "from": "lodash@~2.4.1" + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } }, "grunt-mocha-test": { "version": "0.12.0", "from": "grunt-mocha-test@0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", "dependencies": { "hooker": { "version": "0.2.3", - "from": "hooker@~0.2.3" + "from": "hooker@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" }, "fs-extra": { "version": "0.11.1", - "from": "fs-extra@~0.11.1", + "from": "fs-extra@>=0.11.1 <0.12.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", "dependencies": { "ncp": { "version": "0.6.0", - "from": "ncp@^0.6.0" + "from": "ncp@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" }, "mkdirp": { "version": "0.5.1", - "from": "mkdirp@^0.5.0", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", - "from": "minimist@0.0.8" + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "jsonfile": { "version": "2.4.0", - "from": "jsonfile@^2.0.0", + "from": "jsonfile@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "dependencies": { "graceful-fs": { "version": "4.1.11", - "from": "graceful-fs@^4.1.6" + "from": "graceful-fs@>=4.1.6 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" } } }, "rimraf": { "version": "2.6.1", - "from": "rimraf@^2.2.8", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "dependencies": { "glob": { "version": "7.1.1", - "from": "glob@^7.0.5", + "from": "glob@>=7.0.5 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "dependencies": { "fs.realpath": { "version": "1.0.0", - "from": "fs.realpath@^1.0.0" + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" }, "inflight": { "version": "1.0.6", - "from": "inflight@^1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, "minimatch": { "version": "3.0.3", - "from": "minimatch@2 || 3", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "dependencies": { "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", + "version": "1.1.7", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "dependencies": { "balanced-match": { "version": "0.4.2", - "from": "balanced-match@^0.4.1" + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" }, "concat-map": { "version": "0.0.1", - "from": "concat-map@0.0.1" + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" } } } @@ -1200,17 +1237,20 @@ }, "once": { "version": "1.4.0", - "from": "once@^1.3.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "dependencies": { "wrappy": { "version": "1.0.2", - "from": "wrappy@1" + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } } }, "path-is-absolute": { "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" } } } @@ -1220,75 +1260,145 @@ } } }, + "ioredis": { + "version": "2.5.0", + "from": "ioredis@>=2.5.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.5.0.tgz", + "dependencies": { + "bluebird": { + "version": "3.5.0", + "from": "bluebird@>=3.3.4 <4.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz" + }, + "cluster-key-slot": { + "version": "1.0.8", + "from": "cluster-key-slot@>=1.0.6 <2.0.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz" + }, + "debug": { + "version": "2.6.6", + "from": "debug@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz", + "dependencies": { + "ms": { + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" + } + } + }, + "double-ended-queue": { + "version": "2.1.0-0", + "from": "double-ended-queue@>=2.1.0-0 <3.0.0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" + }, + "flexbuffer": { + "version": "0.0.6", + "from": "flexbuffer@0.0.6", + "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" + }, + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.8.2 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + }, + "redis-commands": { + "version": "1.3.1", + "from": "redis-commands@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz" + }, + "redis-parser": { + "version": "1.3.0", + "from": "redis-parser@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz" + } + } + }, "mocha": { "version": "1.21.4", "from": "mocha@1.21.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", "dependencies": { "commander": { "version": "2.0.0", - "from": "commander@2.0.0" + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "growl": { "version": "1.8.1", - "from": "growl@1.8.x" + "from": "growl@>=1.8.0 <1.9.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" }, "jade": { "version": "0.26.3", "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", - "from": "commander@0.6.1" + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", - "from": "mkdirp@0.3.0" + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "diff": { "version": "1.0.7", - "from": "diff@1.0.7" + "from": "diff@1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, "debug": { - "version": "2.6.3", + "version": "2.6.6", "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz", "dependencies": { "ms": { - "version": "0.7.2", - "from": "ms@0.7.2" + "version": "0.7.3", + "from": "ms@0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" } } }, "mkdirp": { "version": "0.3.5", - "from": "mkdirp@0.3.5" + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", - "from": "minimatch@~0.2.11", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.3", - "from": "lru-cache@2" + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" }, "sigmund": { "version": "1.0.1", - "from": "sigmund@~1.0.0" + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", - "from": "graceful-fs@~2.0.0" + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.3", - "from": "inherits@2" + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" } } } @@ -1296,57 +1406,69 @@ }, "redis": { "version": "0.12.1", - "from": "redis@0.12.1" + "from": "redis@0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" }, "redis-sentinel": { "version": "0.1.1", "from": "redis-sentinel@0.1.1", + "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", "dependencies": { "redis": { "version": "0.11.0", - "from": "redis@0.11.x" + "from": "redis@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" }, "q": { "version": "0.9.2", - "from": "q@0.9.2" + "from": "q@0.9.2", + "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" } } }, "sandboxed-module": { "version": "1.0.1", "from": "sandboxed-module@1.0.1", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", "dependencies": { "require-like": { "version": "0.1.2", - "from": "require-like@0.1.2" + "from": "require-like@0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@0.0.9" + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } } }, "sinon": { "version": "1.10.3", "from": "sinon@1.10.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", "dependencies": { "formatio": { "version": "1.0.2", - "from": "formatio@~1.0", + "from": "formatio@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", "dependencies": { "samsam": { "version": "1.1.3", - "from": "samsam@~1.1" + "from": "samsam@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" } } }, "util": { "version": "0.10.3", - "from": "util@>=0.10.3 <1", + "from": "util@>=0.10.3 <1.0.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "dependencies": { "inherits": { "version": "2.0.1", - "from": "inherits@2.0.1" + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } } From 842b91b93a67977a030fb186453519e6b6613178 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 5 May 2017 09:38:58 +0100 Subject: [PATCH 386/549] update redis-sharelatex to v1.0.1 manual edit to npm-shrinkwrap.json to preserve existing versions --- services/track-changes/npm-shrinkwrap.json | 18 +++++++++++++++--- services/track-changes/package.json | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 9cb3741716..83c0d1273e 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1025,10 +1025,22 @@ } }, "redis-sharelatex": { - "version": "1.0.0", - "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.0", - "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#0cd669a9ed73c0330e3b912fc9d9a42dae3c4fbd", + "version": "1.0.1", + "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.1", + "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#0c581688a2077346ab79b67f1f6086a002e4dc17", "dependencies": { + "async": { + "version": "2.4.0", + "from": "async@>=2.4.0 <3.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.4.0.tgz", + "dependencies": { + "lodash": { + "version": "4.17.4", + "from": "lodash@>=4.14.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" + } + } + }, "chai": { "version": "1.9.1", "from": "chai@1.9.1", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 165178b418..fa9946d3bf 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -19,7 +19,7 @@ "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", "request": "~2.33.0", "requestretry": "^1.12.0", - "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.0", + "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.1", "redis": "~0.10.1", "underscore": "~1.7.0", "mongo-uri": "^0.1.2", From b82567ef790a19d203419e092157f21e50f6d73e Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 5 May 2017 11:30:11 +0100 Subject: [PATCH 387/549] support scan operations on redis cluster --- .../app/coffee/RedisManager.coffee | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index ef64a1141d..56dcfd4372 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -2,6 +2,7 @@ Settings = require "settings-sharelatex" redis = require("redis-sharelatex") rclient = redis.createClient(Settings.redis.history) Keys = Settings.redis.history.key_schema +async = require "async" module.exports = RedisManager = @@ -33,12 +34,19 @@ module.exports = RedisManager = rclient.smembers Keys.docsWithHistoryOps({project_id}), callback # iterate over keys asynchronously using redis scan (non-blocking) + # handle all the cluster nodes or single redis server _getKeys: (pattern, callback) -> + nodes = rclient.nodes?('master') || [ rclient ]; + doKeyLookupForNode = (node, cb) -> + RedisManager._getKeysFromNode node, pattern, cb + async.concatSeries nodes, doKeyLookupForNode, callback + + _getKeysFromNode: (node, pattern, callback) -> cursor = 0 # redis iterator keySet = {} # use hash to avoid duplicate results # scan over all keys looking for pattern doIteration = (cb) -> - rclient.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) -> + node.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) -> return callback(error) if error? [cursor, keys] = reply for key in keys @@ -50,21 +58,20 @@ module.exports = RedisManager = doIteration() # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b + # or DocsWithHistoryOps:{57fd0b1f53a8396d22b2c24b} (for redis cluster) _extractIds: (keyList) -> - ids = (key.split(":")[1] for key in keyList) + ids = for key in keyList + m = key.match(/:\{?([0-9a-f]{24})\}?/) # extract object id + m[1] return ids - # this will only work on single node redis, not redis cluster getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> - return callback(new Error("not supported")) if rclient.nodes? RedisManager._getKeys Keys.docsWithHistoryOps({project_id:"*"}), (error, project_keys) -> return callback(error) if error? project_ids = RedisManager._extractIds project_keys callback(error, project_ids) - # this will only work on single node redis, not redis cluster getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) -> - return callback(new Error("not supported")) if rclient.nodes? # return all the docids, to find dangling history entries after # everything is flushed. RedisManager._getKeys Keys.uncompressedHistoryOps({doc_id:"*"}), (error, doc_keys) -> From 9936710a1a2aa726f4a545bae1b7aede7ce43dad Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 9 May 2017 13:58:40 +0100 Subject: [PATCH 388/549] replace error with warning for archiving timeout --- services/track-changes/app/coffee/PackWorker.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index be5c511d1f..40520be73a 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -87,7 +87,7 @@ processUpdates = (pending) -> logger.error {err, result}, "error in pack archive worker" return callback(err) if shutDownRequested - logger.error "shutting down pack archive worker" + logger.warn "shutting down pack archive worker" return callback(new Error("shutdown")) setTimeout () -> callback(err, result) From c6e83c6cb8a417097e2b84fa226d293e953cf254 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 9 May 2017 13:59:09 +0100 Subject: [PATCH 389/549] allow archiving to exit properly on hard timeout --- services/track-changes/app/coffee/PackWorker.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 40520be73a..e5927d5b45 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -37,10 +37,11 @@ shutDownTimer = setTimeout () -> # start the shutdown on the next pack shutDownRequested = true # do a hard shutdown after a further 5 minutes - setTimeout () -> + hardTimeout = setTimeout () -> logger.error "HARD TIMEOUT in pack archive worker" process.exit() , 5*60*1000 + hardTimeout.unref() , TIMEOUT logger.log "checking for updates, limit=#{LIMIT}, delay=#{DOCUMENT_PACK_DELAY}, timeout=#{TIMEOUT}" From cac7556ad54ddae75522126d379079a1c46635a0 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 9 May 2017 11:32:24 +0100 Subject: [PATCH 390/549] only check packs for archiving once each week --- services/track-changes/app/coffee/PackWorker.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index e5927d5b45..563973d13a 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -116,11 +116,13 @@ if pending? logger.log "got #{pending.length} entries from #{source}" processUpdates pending else + oneWeekAgo = new Date(Date.now() - 7 * DAYS) db.docHistory.find({ expiresAt: {$exists: false} project_id: {$exists: true} v_end: {$exists: true} - _id: {$lt: ObjectIdFromDate(new Date(Date.now() - 7 * DAYS))} + _id: {$lt: ObjectIdFromDate(oneWeekAgo)} + last_checked: {$lt: oneWeekAgo} }, {_id:1, doc_id:1, project_id:1}).sort({ last_checked:1 }).limit LIMIT, (err, results) -> From c26bccd39006a7b3e1c454edc1f0a9f658b5dc56 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 9 May 2017 11:33:21 +0100 Subject: [PATCH 391/549] make arguments handling for packworker more robust --- services/track-changes/app.coffee | 2 +- services/track-changes/app/coffee/PackWorker.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 43cddca498..a83d78c7f1 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -61,7 +61,7 @@ app.post "/pack", (req, res, next) -> else logger.log "running pack" packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js', - [req.query.limit, req.query.delay, req.query.timeout]) + [req.query.limit || 1000, req.query.delay || 1000, req.query.timeout || 30*60*1000]) packWorker.on 'exit', (code, signal) -> logger.log {code, signal}, "history auto pack exited" packWorker = null diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 563973d13a..12c4a37aff 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -22,7 +22,7 @@ source = process.argv[2] DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 TIMEOUT = Number(process.argv[4]) || 30*60*1000 -if source.match(/[^0-9]/) +if !source.match(/^[0-9]+$/) file = fs.readFileSync source result = for line in file.toString().split('\n') [project_id, doc_id] = line.split(' ') From 2781d9fd80fa8efd9ebc982951f92433969e54ee Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 9 May 2017 13:43:21 +0100 Subject: [PATCH 392/549] keep track of processed/total docs in archiving --- services/track-changes/app/coffee/PackWorker.coffee | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.coffee index 12c4a37aff..e4c31c3b4a 100644 --- a/services/track-changes/app/coffee/PackWorker.coffee +++ b/services/track-changes/app/coffee/PackWorker.coffee @@ -21,6 +21,8 @@ PackManager = require "./PackManager" source = process.argv[2] DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 TIMEOUT = Number(process.argv[4]) || 30*60*1000 +COUNT = 0 # number processed +TOTAL = 0 # total number to process if !source.match(/^[0-9]+$/) file = fs.readFileSync source @@ -62,7 +64,7 @@ finish = () -> db.close () -> logger.log 'closing LockManager Redis Connection' LockManager.close () -> - logger.log 'ready to exit from pack archive worker' + logger.log {processedCount: COUNT, allCount: TOTAL}, 'ready to exit from pack archive worker' hardTimeout = setTimeout () -> logger.error 'hard exit from pack archive worker' process.exit(1) @@ -75,7 +77,8 @@ process.on 'exit', (code) -> processUpdates = (pending) -> async.eachSeries pending, (result, callback) -> {_id, project_id, doc_id} = result - logger.log {project_id, doc_id}, "processing" + COUNT++ + logger.log {project_id, doc_id}, "processing #{COUNT}/#{TOTAL}" if not project_id? or not doc_id? logger.log {project_id, doc_id}, "skipping pack, missing project/doc id" return callback() @@ -131,5 +134,6 @@ else finish() return pending = _.uniq results, false, (result) -> result.doc_id.toString() - logger.log "found #{pending.length} documents to archive" + TOTAL = pending.length + logger.log "found #{TOTAL} documents to archive" processUpdates pending From ff76d1cf62967bcc4006fc2b9afda32ac6c3420f Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 10 May 2017 13:48:43 +0100 Subject: [PATCH 393/549] update to redis-sharelatex v1.0.2 --- services/track-changes/npm-shrinkwrap.json | 6 +++--- services/track-changes/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 83c0d1273e..ae4842c5d1 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -1025,9 +1025,9 @@ } }, "redis-sharelatex": { - "version": "1.0.1", - "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.1", - "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#0c581688a2077346ab79b67f1f6086a002e4dc17", + "version": "1.0.2", + "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", + "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#143b7eb192675f36d835080e534a4ac4899f918a", "dependencies": { "async": { "version": "2.4.0", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index fa9946d3bf..a2656c3840 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -19,7 +19,7 @@ "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", "request": "~2.33.0", "requestretry": "^1.12.0", - "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.1", + "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", "redis": "~0.10.1", "underscore": "~1.7.0", "mongo-uri": "^0.1.2", From fb6e028182fcdd7e2a7ebafb18082e72be369684 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 15 May 2017 10:34:24 +0100 Subject: [PATCH 394/549] Migrate lock to be redis-cluster compatible --- services/track-changes/app/coffee/LockManager.coffee | 2 +- services/track-changes/app/coffee/PackManager.coffee | 6 ++++-- services/track-changes/app/coffee/UpdatesManager.coffee | 3 ++- services/track-changes/config/settings.defaults.coffee | 5 ++++- .../test/unit/coffee/LockManager/LockManagerTests.coffee | 2 +- .../test/unit/coffee/PackManager/PackManagerTests.coffee | 2 ++ .../unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 3 +++ 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 7e7468d1c2..18a764035c 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -1,6 +1,6 @@ Settings = require "settings-sharelatex" redis = require("redis-sharelatex") -rclient = redis.createClient(Settings.redis.web) +rclient = redis.createClient(Settings.redis.lock) os = require "os" crypto = require "crypto" logger = require "logger-sharelatex" diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.coffee index d8069da518..d33978229d 100644 --- a/services/track-changes/app/coffee/PackManager.coffee +++ b/services/track-changes/app/coffee/PackManager.coffee @@ -6,6 +6,8 @@ LockManager = require "./LockManager" MongoAWS = require "./MongoAWS" Metrics = require "metrics-sharelatex" ProjectIterator = require "./ProjectIterator" +Settings = require "settings-sharelatex" +keys = Settings.redis.lock.key_schema # Sharejs operations are stored in a 'pack' object # @@ -319,7 +321,7 @@ module.exports = PackManager = insertPacksIntoIndexWithLock: (project_id, doc_id, newPacks, callback) -> LockManager.runWithLock( - "HistoryIndexLock:#{doc_id}", + keys.historyIndexLock({doc_id}), (releaseLock) -> PackManager._insertPacksIntoIndex project_id, doc_id, newPacks, releaseLock callback @@ -438,7 +440,7 @@ module.exports = PackManager = markPackAsFinalisedWithLock: (project_id, doc_id, pack_id, callback) -> LockManager.runWithLock( - "HistoryLock:#{doc_id}", + keys.historyLock({doc_id}), (releaseLock) -> PackManager._markPackAsFinalised project_id, doc_id, pack_id, releaseLock callback diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index 48423a39ec..af001f7959 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -9,6 +9,7 @@ logger = require "logger-sharelatex" async = require "async" _ = require "underscore" Settings = require "settings-sharelatex" +keys = Settings.redis.lock.key_schema module.exports = UpdatesManager = compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> @@ -126,7 +127,7 @@ module.exports = UpdatesManager = UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> return callback(error) if error? LockManager.runWithLock( - "HistoryLock:#{doc_id}", + keys.historyLock({doc_id}), (releaseLock) -> UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, releaseLock callback diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 084ce7de62..907d3c0033 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -18,10 +18,13 @@ module.exports = user: "sharelatex" pass: "password" redis: - web: + lock: host: "localhost" port: 6379 pass: "" + key_schema: + historyLock: ({doc_id}) -> "HistoryLock:#{doc_id}" + historyIndexLock: ({project_id}) -> "HistoryIndexLock:#{project_id}" history: port:"6379" host:"localhost" diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index fd141ef426..ad4df198a9 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -9,7 +9,7 @@ describe "LockManager", -> beforeEach -> @Settings = redis: - web:{} + lock:{} @LockManager = SandboxedModule.require modulePath, requires: "redis-sharelatex": createClient: () => @rclient = diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index 29c9cf6d3d..bedc65683e 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -21,6 +21,8 @@ describe "PackManager", -> "./MongoAWS": {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } 'metrics-sharelatex': {inc: ()->} + "settings-sharelatex": + redis: lock: key_schema: {} @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index c422fffec0..88f1c7998a 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -17,6 +17,9 @@ describe "UpdatesManager", -> "./UpdateTrimmer": @UpdateTrimmer = {} "./DocArchiveManager": @DocArchiveManager = {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } + "settings-sharelatex": + redis: lock: key_schema: + historyLock: ({doc_id}) -> "HistoryLock:#{doc_id}" @doc_id = "doc-id-123" @project_id = "project-id-123" @callback = sinon.stub() From d9763eb1057fbfd511afb85669e58ce30df04f50 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 23 May 2017 13:55:10 +0100 Subject: [PATCH 395/549] log corrupt updates --- services/track-changes/app/coffee/UpdatesManager.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.coffee index af001f7959..3a2cbf29c1 100644 --- a/services/track-changes/app/coffee/UpdatesManager.coffee +++ b/services/track-changes/app/coffee/UpdatesManager.coffee @@ -97,7 +97,9 @@ module.exports = UpdatesManager = length = docUpdates.length # parse the redis strings into ShareJs updates RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> - return callback(error) if error? + if error? + logger.err project_id: project_id, doc_id: doc_id, docUpdates: docUpdates, "failed to parse docUpdates" + return callback(error) logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> return callback(error) if error? From b71a15a6a09f0a562afebc3a3e286fdc568b3b37 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 1 Jun 2017 13:36:13 +0100 Subject: [PATCH 396/549] null byte check for JSON.stringify in archiving --- services/track-changes/app/coffee/MongoAWS.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 8a0a065317..7e56913a9d 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -46,6 +46,9 @@ module.exports = MongoAWS = return callback new Error("cannot find pack to send to s3") if not result? return callback new Error("refusing to send pack with TTL to s3") if result.expiresAt? uncompressedData = JSON.stringify(result) + if uncompressedData.indexOf("\u0000") != -1 + error = new Error("null bytes found in upload") + logger.error err: error, project_id: project_id, doc_id: doc_id, pack_id: pack_id, error.message zlib.gzip uncompressedData, (err, buf) -> logger.log {project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack" return callback(err) if err? From e8dfe3099fecac13e4f470316ba19040d716bf39 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 2 Jun 2017 14:13:33 +0100 Subject: [PATCH 397/549] add missing callback for null byte check --- services/track-changes/app/coffee/MongoAWS.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 7e56913a9d..4308763a7a 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -49,6 +49,7 @@ module.exports = MongoAWS = if uncompressedData.indexOf("\u0000") != -1 error = new Error("null bytes found in upload") logger.error err: error, project_id: project_id, doc_id: doc_id, pack_id: pack_id, error.message + return callback(error) zlib.gzip uncompressedData, (err, buf) -> logger.log {project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack" return callback(err) if err? From c5252f893bd21bba2fa8b5e4f376114c5fcee08e Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 28 Jun 2017 15:38:31 +0100 Subject: [PATCH 398/549] Don't return all updates if no version is given --- .../app/coffee/DiffManager.coffee | 8 +-- .../DiffManager/DiffManagerTests.coffee | 52 +++++++++++++------ .../PackManager/PackManagerTests.coffee | 1 + 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.coffee index c01c73a533..af1cfaf03f 100644 --- a/services/track-changes/app/coffee/DiffManager.coffee +++ b/services/track-changes/app/coffee/DiffManager.coffee @@ -4,12 +4,14 @@ DiffGenerator = require "./DiffGenerator" logger = require "logger-sharelatex" module.exports = DiffManager = - getLatestDocAndUpdates: (project_id, doc_id, fromVersion, toVersion, callback = (error, content, version, updates) ->) -> + getLatestDocAndUpdates: (project_id, doc_id, fromVersion, callback = (error, content, version, updates) ->) -> # Get updates last, since then they must be ahead and it # might be possible to rewind to the same version as the doc. DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> return callback(error) if error? - UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, to: toVersion, (error, updates) -> + if !fromVersion? # If we haven't been given a version, just return lastest doc and no updates + return callback(null, content, version, []) + UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, (error, updates) -> return callback(error) if error? callback(null, content, version, updates) @@ -56,7 +58,7 @@ module.exports = DiffManager = _tryGetDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) -> logger.log project_id: project_id, doc_id: doc_id, version: version, "getting document before version" - DiffManager.getLatestDocAndUpdates project_id, doc_id, version, null, (error, content, version, updates) -> + DiffManager.getLatestDocAndUpdates project_id, doc_id, version, (error, content, version, updates) -> return callback(error) if error? # bail out if we hit a broken update diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee index 723193403c..f0bca22e90 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee @@ -26,20 +26,40 @@ describe "DiffManager", -> @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @content, @version) @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) - @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback - it "should get the latest version of the doc", -> - @DocumentUpdaterManager.getDocument - .calledWith(@project_id, @doc_id) - .should.equal true + describe "with a fromVersion", -> + beforeEach -> + @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @callback - it "should get the latest updates", -> - @UpdatesManager.getDocUpdatesWithUserInfo - .calledWith(@project_id, @doc_id, from: @from, to: @to) - .should.equal true + it "should get the latest version of the doc", -> + @DocumentUpdaterManager.getDocument + .calledWith(@project_id, @doc_id) + .should.equal true - it "should call the callback with the content, version and updates", -> - @callback.calledWith(null, @content, @version, @updates).should.equal true + it "should get the latest updates", -> + @UpdatesManager.getDocUpdatesWithUserInfo + .calledWith(@project_id, @doc_id, from: @from) + .should.equal true + + it "should call the callback with the content, version and updates", -> + @callback.calledWith(null, @content, @version, @updates).should.equal true + + describe "with no fromVersion", -> + beforeEach -> + @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, null, @callback + + it "should get the latest version of the doc", -> + @DocumentUpdaterManager.getDocument + .calledWith(@project_id, @doc_id) + .should.equal true + + it "should not get the latest updates", -> + @UpdatesManager.getDocUpdatesWithUserInfo + .called.should.equal false + + it "should call the callback with the content, version and blank updates", -> + @callback.calledWith(null, @content, @version, []).should.equal true + describe "getDiff", -> beforeEach -> @@ -80,7 +100,7 @@ describe "DiffManager", -> describe "when the updates are inconsistent", -> beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) @DiffGenerator.buildDiff = sinon.stub().throws(@error = new Error("inconsistent!")) @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback @@ -177,7 +197,7 @@ describe "DiffManager", -> describe "with matching versions", -> beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) @DiffGenerator.rewindUpdates = sinon.spy (content, updates) => # the rewindUpdates method reverses the 'updates' array updates.reverse() @@ -187,7 +207,7 @@ describe "DiffManager", -> it "should get the latest doc and version with all recent updates", -> @DiffManager.getLatestDocAndUpdates - .calledWith(@project_id, @doc_id, @fromVersion, null) + .calledWith(@project_id, @doc_id, @fromVersion) .should.equal true it "should rewind the diff", -> @@ -200,7 +220,7 @@ describe "DiffManager", -> beforeEach -> @version = 50 @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) @DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback it "should call the callback with an error with retry = true set", -> @@ -210,7 +230,7 @@ describe "DiffManager", -> describe "when the updates are inconsistent", -> beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @content, @version, @updates) + @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) @DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!")) @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index 29c9cf6d3d..dd79d0bf21 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -21,6 +21,7 @@ describe "PackManager", -> "./MongoAWS": {} "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } 'metrics-sharelatex': {inc: ()->} + "./ProjectIterator": require("../../../../app/js/ProjectIterator.js") # Cache for speed @callback = sinon.stub() @doc_id = ObjectId().toString() @project_id = ObjectId().toString() From 004d1c134cd9d7dc761b3c21600afeb830a3a732 Mon Sep 17 00:00:00 2001 From: Joe Green Date: Wed, 16 Aug 2017 10:39:03 +0100 Subject: [PATCH 399/549] Create Jenkinsfile --- services/track-changes/Jenkinsfile | 76 ++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 services/track-changes/Jenkinsfile diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile new file mode 100644 index 0000000000..3bf41fae36 --- /dev/null +++ b/services/track-changes/Jenkinsfile @@ -0,0 +1,76 @@ +pipeline { + + agent { + docker { + image 'sharelatex/node:0.10.22' + args "-v /var/lib/jenkins/.npm:/tmp/.npm" + } + } + + environment { + HOME = "/tmp" + } + + triggers { + pollSCM('* * * * *') + cron('@daily') + } + + stages { + stage('Set up') { + steps { + // we need to disable logallrefupdates, else git clones during the npm install will require git to lookup the user id + // which does not exist in the container's /etc/passwd file, causing the clone to fail. + sh 'git config --global core.logallrefupdates false' + } + } + stage('Install') { + steps { + sh 'rm -fr node_modules' + sh 'npm install' + sh 'npm install --quiet grunt-cli' + } + } + stage('Compile') { + steps { + sh 'node_modules/.bin/grunt compile' + } + } + stage('Test') { + steps { + sh 'NODE_ENV=development node_modules/.bin/grunt test:unit' + } + } + stage('Package') { + steps { + sh 'touch build.tar.gz' // Avoid tar warning about files changing during read + sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' + } + } + stage('Publish') { + steps { + withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { + s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") + } + } + } + } + + post { + failure { + mail(from: "${EMAIL_ALERT_FROM}", + to: "${EMAIL_ALERT_TO}", + subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", + body: "Build: ${BUILD_URL}") + } + } + + // The options directive is for configuration that applies to the whole job. + options { + // we'd like to make sure remove old builds, so we don't fill up our storage! + buildDiscarder(logRotator(numToKeepStr:'50')) + + // And we'd really like to be sure that this build doesn't hang forever, so let's time it out after: + timeout(time: 30, unit: 'MINUTES') + } +} From 095ac313476725c5b177bdb740a5801fb935f139 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Thu, 24 Aug 2017 14:32:27 +0100 Subject: [PATCH 400/549] wip: updating to node 6 --- services/track-changes/.nvmrc | 2 +- .../app/coffee/WebApiManager.coffee | 1 + services/track-changes/npm-shrinkwrap.json | 2258 ----------------- services/track-changes/package.json | 25 +- .../unit/coffee/DocArchive/MongoAWS.coffee | 2 +- .../DocumentUpdaterManagerTests.coffee | 8 +- .../LockManager/LockManagerTests.coffee | 2 +- .../PackManager/PackManagerTests.coffee | 8 +- .../UpdatesManager/UpdatesManagerTests.coffee | 14 +- .../WebApiManager/WebApiManagerTests.coffee | 8 +- 10 files changed, 38 insertions(+), 2290 deletions(-) delete mode 100644 services/track-changes/npm-shrinkwrap.json diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index 994fe99096..e18a34b9d6 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -0.10.22 \ No newline at end of file +6.11.2 diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index aee8d431dd..a95808deee 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -30,6 +30,7 @@ module.exports = WebApiManager = return callback null, body else error = new Error("web returned a non-success status code: #{res.statusCode} (attempts: #{res.attempts})") + console.log error.message callback error getUserInfo: (user_id, callback = (error, userInfo) ->) -> diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json deleted file mode 100644 index ae4842c5d1..0000000000 --- a/services/track-changes/npm-shrinkwrap.json +++ /dev/null @@ -1,2258 +0,0 @@ -{ - "name": "history-sharelatex", - "version": "0.1.4", - "dependencies": { - "async": { - "version": "0.2.10", - "from": "async@~0.2.10" - }, - "bson": { - "version": "0.4.23", - "from": "bson@^0.4.20" - }, - "byline": { - "version": "4.2.2", - "from": "byline@^4.2.1" - }, - "cli": { - "version": "0.6.6", - "from": "cli@^0.6.6", - "dependencies": { - "glob": { - "version": "3.2.11", - "from": "glob@~ 3.2.1", - "dependencies": { - "inherits": { - "version": "2.0.3", - "from": "inherits@~2.0.1" - }, - "minimatch": { - "version": "0.3.0", - "from": "minimatch@0.3", - "dependencies": { - "lru-cache": { - "version": "2.7.3", - "from": "lru-cache@2" - }, - "sigmund": { - "version": "1.0.1", - "from": "sigmund@~1.0.0" - } - } - } - } - }, - "exit": { - "version": "0.1.2", - "from": "exit@0.1.2" - } - } - }, - "express": { - "version": "3.3.5", - "from": "express@3.3.5", - "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", - "dependencies": { - "connect": { - "version": "2.8.5", - "from": "connect@2.8.5", - "dependencies": { - "qs": { - "version": "0.6.5", - "from": "qs@0.6.5" - }, - "formidable": { - "version": "1.0.14", - "from": "formidable@1.0.14" - }, - "bytes": { - "version": "0.2.0", - "from": "bytes@0.2.0" - }, - "pause": { - "version": "0.0.1", - "from": "pause@0.0.1" - }, - "uid2": { - "version": "0.0.2", - "from": "uid2@0.0.2" - } - } - }, - "commander": { - "version": "1.2.0", - "from": "commander@1.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", - "dependencies": { - "keypress": { - "version": "0.1.0", - "from": "keypress@0.1.x" - } - } - }, - "range-parser": { - "version": "0.0.4", - "from": "range-parser@0.0.4" - }, - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@0.3.x" - }, - "cookie": { - "version": "0.1.0", - "from": "cookie@0.1.0" - }, - "buffer-crc32": { - "version": "0.2.1", - "from": "buffer-crc32@0.2.1" - }, - "fresh": { - "version": "0.2.0", - "from": "fresh@0.2.0" - }, - "methods": { - "version": "0.0.1", - "from": "methods@0.0.1" - }, - "send": { - "version": "0.1.4", - "from": "send@0.1.4", - "dependencies": { - "mime": { - "version": "1.2.11", - "from": "mime@~1.2.9" - } - } - }, - "cookie-signature": { - "version": "1.0.1", - "from": "cookie-signature@1.0.1" - }, - "debug": { - "version": "2.6.3", - "from": "debug@*", - "dependencies": { - "ms": { - "version": "0.7.2", - "from": "ms@0.7.2" - } - } - } - } - }, - "line-reader": { - "version": "0.2.4", - "from": "line-reader@^0.2.4" - }, - "mongojs": { - "version": "1.4.1", - "from": "mongojs@^1.4.1", - "dependencies": { - "each-series": { - "version": "1.0.0", - "from": "each-series@^1.0.0" - }, - "mongodb-core": { - "version": "1.3.22-alpha4", - "from": "mongodb-core@^1.2.8", - "dependencies": { - "require_optional": { - "version": "1.0.0", - "from": "require_optional@~1.0.0", - "dependencies": { - "semver": { - "version": "5.3.0", - "from": "semver@^5.1.0" - }, - "resolve-from": { - "version": "2.0.0", - "from": "resolve-from@^2.0.0" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@^1.3.2", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "parse-mongo-url": { - "version": "1.1.1", - "from": "parse-mongo-url@^1.1.0" - }, - "pump": { - "version": "1.0.2", - "from": "pump@^1.0.0", - "dependencies": { - "end-of-stream": { - "version": "1.4.0", - "from": "end-of-stream@^1.1.0" - } - } - }, - "readable-stream": { - "version": "2.2.6", - "from": "readable-stream@^2.0.2", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@^1.0.0" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@~1.0.0" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@~1.0.0" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@~2.0.1" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@~0.10.x" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@~1.0.1" - } - } - }, - "thunky": { - "version": "0.1.0", - "from": "thunky@^0.1.0" - }, - "to-mongodb-core": { - "version": "2.0.0", - "from": "to-mongodb-core@^2.0.0" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@^4.0.0" - } - } - }, - "settings-sharelatex": { - "version": "1.0.0", - "from": "settings-sharelatex@git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0" - } - } - }, - "logger-sharelatex": { - "version": "1.5.6", - "from": "logger-sharelatex@git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#b2956ec56b582b9f4fc8fdda8dc00c06e77c5537", - "dependencies": { - "bunyan": { - "version": "1.5.1", - "from": "bunyan@1.5.1", - "dependencies": { - "dtrace-provider": { - "version": "0.6.0", - "from": "dtrace-provider@~0.6", - "dependencies": { - "nan": { - "version": "2.5.1", - "from": "nan@^2.0.8" - } - } - }, - "mv": { - "version": "2.1.1", - "from": "mv@~2", - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@~0.5.1", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8" - } - } - }, - "ncp": { - "version": "2.0.0", - "from": "ncp@~2.0.0" - }, - "rimraf": { - "version": "2.4.5", - "from": "rimraf@~2.4.0", - "dependencies": { - "glob": { - "version": "6.0.4", - "from": "glob@^6.0.1", - "dependencies": { - "inflight": { - "version": "1.0.6", - "from": "inflight@^1.0.4", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - }, - "minimatch": { - "version": "3.0.3", - "from": "minimatch@2 || 3", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@^0.4.1" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@^1.3.0", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" - } - } - } - } - } - } - }, - "safe-json-stringify": { - "version": "1.0.4", - "from": "safe-json-stringify@~1" - } - } - }, - "coffee-script": { - "version": "1.12.4", - "from": "coffee-script@1.12.4", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz" - }, - "grunt-contrib-clean": { - "version": "0.6.0", - "from": "grunt-contrib-clean@^0.6.0", - "dependencies": { - "rimraf": { - "version": "2.2.8", - "from": "rimraf@~2.2.1" - } - } - }, - "grunt-contrib-coffee": { - "version": "0.11.1", - "from": "grunt-contrib-coffee@^0.11.0", - "dependencies": { - "coffee-script": { - "version": "1.7.1", - "from": "coffee-script@~1.7.0", - "dependencies": { - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@~0.3.5" - } - } - }, - "chalk": { - "version": "0.5.1", - "from": "chalk@~0.5.0", - "dependencies": { - "ansi-styles": { - "version": "1.1.0", - "from": "ansi-styles@^1.1.0" - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@^1.0.0" - }, - "has-ansi": { - "version": "0.1.0", - "from": "has-ansi@^0.1.0", - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "from": "ansi-regex@^0.2.1" - } - } - }, - "strip-ansi": { - "version": "0.3.0", - "from": "strip-ansi@^0.3.0", - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "from": "ansi-regex@^0.2.1" - } - } - }, - "supports-color": { - "version": "0.2.0", - "from": "supports-color@^0.2.0" - } - } - }, - "lodash": { - "version": "2.4.2", - "from": "lodash@~2.4.1" - } - } - }, - "grunt-execute": { - "version": "0.2.2", - "from": "grunt-execute@^0.2.2" - }, - "grunt-mocha-test": { - "version": "0.11.0", - "from": "grunt-mocha-test@^0.11.0", - "dependencies": { - "mocha": { - "version": "1.20.1", - "from": "mocha@~1.20.0", - "dependencies": { - "commander": { - "version": "2.0.0", - "from": "commander@2.0.0" - }, - "growl": { - "version": "1.7.0", - "from": "growl@1.7.x" - }, - "jade": { - "version": "0.26.3", - "from": "jade@0.26.3", - "dependencies": { - "commander": { - "version": "0.6.1", - "from": "commander@0.6.1" - }, - "mkdirp": { - "version": "0.3.0", - "from": "mkdirp@0.3.0" - } - } - }, - "diff": { - "version": "1.0.7", - "from": "diff@1.0.7" - }, - "debug": { - "version": "2.6.3", - "from": "debug@*", - "dependencies": { - "ms": { - "version": "0.7.2", - "from": "ms@0.7.2" - } - } - }, - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@0.3.5" - }, - "glob": { - "version": "3.2.3", - "from": "glob@3.2.3", - "dependencies": { - "minimatch": { - "version": "0.2.14", - "from": "minimatch@~0.2.11", - "dependencies": { - "lru-cache": { - "version": "2.7.3", - "from": "lru-cache@2" - }, - "sigmund": { - "version": "1.0.1", - "from": "sigmund@~1.0.0" - } - } - }, - "graceful-fs": { - "version": "2.0.3", - "from": "graceful-fs@~2.0.0" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - } - } - } - } - }, - "hooker": { - "version": "0.2.3", - "from": "hooker@~0.2.3" - }, - "fs-extra": { - "version": "0.9.1", - "from": "fs-extra@~0.9.1", - "dependencies": { - "ncp": { - "version": "0.5.1", - "from": "ncp@^0.5.1" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@^0.5.0", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8" - } - } - }, - "jsonfile": { - "version": "1.1.1", - "from": "jsonfile@~1.1.0" - }, - "rimraf": { - "version": "2.6.1", - "from": "rimraf@^2.2.8", - "dependencies": { - "glob": { - "version": "7.1.1", - "from": "glob@^7.0.5", - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "from": "fs.realpath@^1.0.0" - }, - "inflight": { - "version": "1.0.6", - "from": "inflight@^1.0.4", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - }, - "minimatch": { - "version": "3.0.3", - "from": "minimatch@^3.0.2", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@^0.4.1" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@^1.3.0", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" - } - } - } - } - } - } - } - } - }, - "raven": { - "version": "1.2.0", - "from": "raven@^1.1.3", - "dependencies": { - "cookie": { - "version": "0.3.1", - "from": "cookie@0.3.1" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@5.0.1" - }, - "lsmod": { - "version": "1.0.0", - "from": "lsmod@1.0.0" - }, - "uuid": { - "version": "3.0.0", - "from": "uuid@3.0.0" - }, - "stack-trace": { - "version": "0.0.9", - "from": "stack-trace@0.0.9" - } - } - }, - "timekeeper": { - "version": "1.0.0", - "from": "timekeeper@^1.0.0" - } - } - }, - "metrics-sharelatex": { - "version": "1.7.1", - "from": "metrics-sharelatex@git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#166961924c599b1f9468f2e17846fa2a9d12372d", - "dependencies": { - "lynx": { - "version": "0.1.1", - "from": "lynx@~0.1.1", - "dependencies": { - "mersenne": { - "version": "0.0.3", - "from": "mersenne@~0.0.3" - }, - "statsd-parser": { - "version": "0.0.4", - "from": "statsd-parser@~0.0.4" - } - } - }, - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0" - }, - "underscore": { - "version": "1.6.0", - "from": "underscore@~1.6.0" - } - } - }, - "request": { - "version": "2.33.0", - "from": "request@~2.33.0", - "dependencies": { - "qs": { - "version": "0.6.6", - "from": "qs@~0.6.0" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@~5.0.0" - }, - "forever-agent": { - "version": "0.5.2", - "from": "forever-agent@~0.5.0" - }, - "node-uuid": { - "version": "1.4.8", - "from": "node-uuid@~1.4.0" - }, - "mime": { - "version": "1.2.11", - "from": "mime@~1.2.9" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@>=0.12.0", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@^1.4.1" - } - } - }, - "form-data": { - "version": "0.1.4", - "from": "form-data@~0.1.0", - "dependencies": { - "combined-stream": { - "version": "0.0.7", - "from": "combined-stream@~0.0.4", - "dependencies": { - "delayed-stream": { - "version": "0.0.5", - "from": "delayed-stream@0.0.5" - } - } - }, - "async": { - "version": "0.9.2", - "from": "async@~0.9.0" - } - } - }, - "tunnel-agent": { - "version": "0.3.0", - "from": "tunnel-agent@~0.3.0" - }, - "http-signature": { - "version": "0.10.1", - "from": "http-signature@~0.10.0", - "dependencies": { - "assert-plus": { - "version": "0.1.5", - "from": "assert-plus@^0.1.5" - }, - "asn1": { - "version": "0.1.11", - "from": "asn1@0.1.11" - }, - "ctype": { - "version": "0.5.3", - "from": "ctype@0.5.3" - } - } - }, - "oauth-sign": { - "version": "0.3.0", - "from": "oauth-sign@~0.3.0" - }, - "hawk": { - "version": "1.0.0", - "from": "hawk@~1.0.0", - "dependencies": { - "hoek": { - "version": "0.9.1", - "from": "hoek@0.9.x" - }, - "boom": { - "version": "0.4.2", - "from": "boom@0.4.x" - }, - "cryptiles": { - "version": "0.2.2", - "from": "cryptiles@0.2.x" - }, - "sntp": { - "version": "0.2.4", - "from": "sntp@0.2.x" - } - } - }, - "aws-sign2": { - "version": "0.5.0", - "from": "aws-sign2@~0.5.0" - } - } - }, - "requestretry": { - "version": "1.12.0", - "from": "requestretry@^1.12.0", - "dependencies": { - "extend": { - "version": "3.0.0", - "from": "extend@^3.0.0" - }, - "lodash": { - "version": "4.17.4", - "from": "lodash@^4.15.0" - }, - "request": { - "version": "2.81.0", - "from": "request@^2.74.0", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@~0.6.0" - }, - "aws4": { - "version": "1.6.0", - "from": "aws4@^1.2.1" - }, - "caseless": { - "version": "0.12.0", - "from": "caseless@~0.12.0" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@~1.0.5", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@~1.0.0" - } - } - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@~0.6.1" - }, - "form-data": { - "version": "2.1.2", - "from": "form-data@~2.1.1", - "dependencies": { - "asynckit": { - "version": "0.4.0", - "from": "asynckit@^0.4.0" - } - } - }, - "har-validator": { - "version": "4.2.1", - "from": "har-validator@~4.2.1", - "dependencies": { - "ajv": { - "version": "4.11.5", - "from": "ajv@^4.9.1", - "dependencies": { - "co": { - "version": "4.6.0", - "from": "co@^4.6.0" - }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "json-stable-stringify@^1.0.1", - "dependencies": { - "jsonify": { - "version": "0.0.0", - "from": "jsonify@~0.0.0" - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "from": "har-schema@^1.0.5" - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@~3.1.3", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "hoek@2.x.x" - }, - "boom": { - "version": "2.10.1", - "from": "boom@2.x.x" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@2.x.x" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@1.x.x" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@~1.1.0", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@^0.2.0" - }, - "jsprim": { - "version": "1.4.0", - "from": "jsprim@^1.2.2", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@1.0.0" - }, - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2" - }, - "json-schema": { - "version": "0.2.3", - "from": "json-schema@0.2.3" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6" - } - } - }, - "sshpk": { - "version": "1.11.0", - "from": "sshpk@^1.7.0", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "asn1@~0.2.3" - }, - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@^1.0.0" - }, - "dashdash": { - "version": "1.14.1", - "from": "dashdash@^1.12.0" - }, - "getpass": { - "version": "0.1.6", - "from": "getpass@^0.1.1" - }, - "jsbn": { - "version": "0.1.1", - "from": "jsbn@~0.1.0" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "tweetnacl@~0.14.0" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@^1.0.0" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@~0.1.1" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "from": "bcrypt-pbkdf@^1.0.0" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@~1.0.0" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@~0.1.2" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@~5.0.1" - }, - "mime-types": { - "version": "2.1.15", - "from": "mime-types@~2.1.7", - "dependencies": { - "mime-db": { - "version": "1.27.0", - "from": "mime-db@~1.27.0" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "from": "oauth-sign@~0.8.1" - }, - "performance-now": { - "version": "0.2.0", - "from": "performance-now@^0.2.0" - }, - "qs": { - "version": "6.4.0", - "from": "qs@~6.4.0" - }, - "safe-buffer": { - "version": "5.0.1", - "from": "safe-buffer@^5.0.1" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@~0.0.4" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@~2.3.0", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@^1.4.1" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@^0.6.0" - }, - "uuid": { - "version": "3.0.1", - "from": "uuid@^3.0.0" - } - } - }, - "when": { - "version": "3.7.8", - "from": "when@^3.7.7" - } - } - }, - "redis-sharelatex": { - "version": "1.0.2", - "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", - "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#143b7eb192675f36d835080e534a4ac4899f918a", - "dependencies": { - "async": { - "version": "2.4.0", - "from": "async@>=2.4.0 <3.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.4.0.tgz", - "dependencies": { - "lodash": { - "version": "4.17.4", - "from": "lodash@>=4.14.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" - } - } - }, - "chai": { - "version": "1.9.1", - "from": "chai@1.9.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.9.1.tgz", - "dependencies": { - "assertion-error": { - "version": "1.0.0", - "from": "assertion-error@1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz" - }, - "deep-eql": { - "version": "0.1.3", - "from": "deep-eql@0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "dependencies": { - "type-detect": { - "version": "0.1.1", - "from": "type-detect@0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" - } - } - } - } - }, - "coffee-script": { - "version": "1.8.0", - "from": "coffee-script@1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", - "dependencies": { - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" - } - } - }, - "grunt-contrib-coffee": { - "version": "0.11.1", - "from": "grunt-contrib-coffee@0.11.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", - "dependencies": { - "coffee-script": { - "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", - "dependencies": { - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@>=0.3.5 <0.4.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" - } - } - }, - "chalk": { - "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "dependencies": { - "ansi-styles": { - "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "has-ansi": { - "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" - } - } - }, - "strip-ansi": { - "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" - } - } - }, - "supports-color": { - "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" - } - } - }, - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - } - } - }, - "grunt-mocha-test": { - "version": "0.12.0", - "from": "grunt-mocha-test@0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.12.0.tgz", - "dependencies": { - "hooker": { - "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" - }, - "fs-extra": { - "version": "0.11.1", - "from": "fs-extra@>=0.11.1 <0.12.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", - "dependencies": { - "ncp": { - "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - } - } - }, - "jsonfile": { - "version": "2.4.0", - "from": "jsonfile@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "graceful-fs@>=4.1.6 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" - } - } - }, - "rimraf": { - "version": "2.6.1", - "from": "rimraf@>=2.2.8 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "dependencies": { - "glob": { - "version": "7.1.1", - "from": "glob@>=7.0.5 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "inflight": { - "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "minimatch": { - "version": "3.0.3", - "from": "minimatch@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "dependencies": { - "brace-expansion": { - "version": "1.1.7", - "from": "brace-expansion@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - } - } - } - } - } - } - } - } - }, - "ioredis": { - "version": "2.5.0", - "from": "ioredis@>=2.5.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-2.5.0.tgz", - "dependencies": { - "bluebird": { - "version": "3.5.0", - "from": "bluebird@>=3.3.4 <4.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz" - }, - "cluster-key-slot": { - "version": "1.0.8", - "from": "cluster-key-slot@>=1.0.6 <2.0.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.8.tgz" - }, - "debug": { - "version": "2.6.6", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz", - "dependencies": { - "ms": { - "version": "0.7.3", - "from": "ms@0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" - } - } - }, - "double-ended-queue": { - "version": "2.1.0-0", - "from": "double-ended-queue@>=2.1.0-0 <3.0.0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz" - }, - "flexbuffer": { - "version": "0.0.6", - "from": "flexbuffer@0.0.6", - "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" - }, - "lodash": { - "version": "4.17.4", - "from": "lodash@>=4.8.2 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" - }, - "redis-commands": { - "version": "1.3.1", - "from": "redis-commands@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.1.tgz" - }, - "redis-parser": { - "version": "1.3.0", - "from": "redis-parser@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-1.3.0.tgz" - } - } - }, - "mocha": { - "version": "1.21.4", - "from": "mocha@1.21.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.4.tgz", - "dependencies": { - "commander": { - "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" - }, - "growl": { - "version": "1.8.1", - "from": "growl@>=1.8.0 <1.9.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" - }, - "jade": { - "version": "0.26.3", - "from": "jade@0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "dependencies": { - "commander": { - "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" - }, - "mkdirp": { - "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" - } - } - }, - "diff": { - "version": "1.0.7", - "from": "diff@1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" - }, - "debug": { - "version": "2.6.6", - "from": "debug@*", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz", - "dependencies": { - "ms": { - "version": "0.7.3", - "from": "ms@0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz" - } - } - }, - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" - }, - "glob": { - "version": "3.2.3", - "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", - "dependencies": { - "minimatch": { - "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "dependencies": { - "lru-cache": { - "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" - }, - "sigmund": { - "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" - } - } - }, - "graceful-fs": { - "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - } - } - } - }, - "redis": { - "version": "0.12.1", - "from": "redis@0.12.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" - }, - "redis-sentinel": { - "version": "0.1.1", - "from": "redis-sentinel@0.1.1", - "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", - "dependencies": { - "redis": { - "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" - }, - "q": { - "version": "0.9.2", - "from": "q@0.9.2", - "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" - } - } - }, - "sandboxed-module": { - "version": "1.0.1", - "from": "sandboxed-module@1.0.1", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-1.0.1.tgz", - "dependencies": { - "require-like": { - "version": "0.1.2", - "from": "require-like@0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" - }, - "stack-trace": { - "version": "0.0.9", - "from": "stack-trace@0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" - } - } - }, - "sinon": { - "version": "1.10.3", - "from": "sinon@1.10.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.10.3.tgz", - "dependencies": { - "formatio": { - "version": "1.0.2", - "from": "formatio@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.0.2.tgz", - "dependencies": { - "samsam": { - "version": "1.1.3", - "from": "samsam@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.3.tgz" - } - } - }, - "util": { - "version": "0.10.3", - "from": "util@>=0.10.3 <1.0.0", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "dependencies": { - "inherits": { - "version": "2.0.1", - "from": "inherits@2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - } - } - } - } - } - } - }, - "redis": { - "version": "0.10.3", - "from": "redis@~0.10.1" - }, - "underscore": { - "version": "1.7.0", - "from": "underscore@~1.7.0" - }, - "mongo-uri": { - "version": "0.1.2", - "from": "mongo-uri@^0.1.2" - }, - "s3-streams": { - "version": "0.3.0", - "from": "s3-streams@^0.3.0", - "dependencies": { - "lodash": { - "version": "3.10.1", - "from": "lodash@^3.9.3" - }, - "readable-stream": { - "version": "2.2.6", - "from": "readable-stream@^2.0.0", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@^1.0.0" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@~1.0.0" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@~1.0.0" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@~2.0.1" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@~0.10.x" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@~1.0.1" - } - } - }, - "bluebird": { - "version": "2.11.0", - "from": "bluebird@^2.9.27" - } - } - }, - "JSONStream": { - "version": "1.3.1", - "from": "JSONStream@^1.0.4", - "dependencies": { - "jsonparse": { - "version": "1.3.0", - "from": "jsonparse@^1.2.0" - }, - "through": { - "version": "2.3.8", - "from": "through@>=2.2.7 <3" - } - } - }, - "heap": { - "version": "0.2.6", - "from": "heap@^0.2.6" - }, - "v8-profiler": { - "version": "5.7.0", - "from": "v8-profiler@^5.6.5", - "dependencies": { - "nan": { - "version": "2.5.1", - "from": "nan@^2.5.1" - }, - "node-pre-gyp": { - "version": "0.6.34", - "from": "node-pre-gyp@^0.6.34", - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@^0.5.1", - "dependencies": { - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8" - } - } - }, - "nopt": { - "version": "4.0.1", - "from": "nopt@^4.0.1", - "dependencies": { - "abbrev": { - "version": "1.1.0", - "from": "abbrev@1" - }, - "osenv": { - "version": "0.1.4", - "from": "osenv@^0.1.4", - "dependencies": { - "os-homedir": { - "version": "1.0.2", - "from": "os-homedir@^1.0.0" - }, - "os-tmpdir": { - "version": "1.0.2", - "from": "os-tmpdir@^1.0.0" - } - } - } - } - }, - "npmlog": { - "version": "4.0.2", - "from": "npmlog@^4.0.2", - "dependencies": { - "are-we-there-yet": { - "version": "1.1.2", - "from": "are-we-there-yet@~1.1.2", - "dependencies": { - "delegates": { - "version": "1.0.0", - "from": "delegates@^1.0.0" - }, - "readable-stream": { - "version": "2.2.6", - "from": "readable-stream@^2.0.0 || ^1.1.13", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@^1.0.0" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@~1.0.0" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@~1.0.0" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@~2.0.1" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@~0.10.x" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@~1.0.1" - } - } - } - } - }, - "console-control-strings": { - "version": "1.1.0", - "from": "console-control-strings@~1.1.0" - }, - "gauge": { - "version": "2.7.3", - "from": "gauge@~2.7.1", - "dependencies": { - "aproba": { - "version": "1.1.1", - "from": "aproba@^1.0.3" - }, - "has-unicode": { - "version": "2.0.1", - "from": "has-unicode@^2.0.0" - }, - "object-assign": { - "version": "4.1.1", - "from": "object-assign@^4.1.0" - }, - "signal-exit": { - "version": "3.0.2", - "from": "signal-exit@^3.0.0" - }, - "string-width": { - "version": "1.0.2", - "from": "string-width@^1.0.1", - "dependencies": { - "code-point-at": { - "version": "1.1.0", - "from": "code-point-at@^1.0.0" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "from": "is-fullwidth-code-point@^1.0.0", - "dependencies": { - "number-is-nan": { - "version": "1.0.1", - "from": "number-is-nan@^1.0.0" - } - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@^3.0.1", - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "from": "ansi-regex@^2.0.0" - } - } - }, - "wide-align": { - "version": "1.1.0", - "from": "wide-align@^1.1.0" - } - } - }, - "set-blocking": { - "version": "2.0.0", - "from": "set-blocking@~2.0.0" - } - } - }, - "rc": { - "version": "1.2.1", - "from": "rc@^1.1.7", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "dependencies": { - "deep-extend": { - "version": "0.4.1", - "from": "deep-extend@~0.4.0" - }, - "ini": { - "version": "1.3.4", - "from": "ini@~1.3.0" - }, - "minimist": { - "version": "1.2.0", - "from": "minimist@^1.2.0" - }, - "strip-json-comments": { - "version": "2.0.1", - "from": "strip-json-comments@~2.0.1" - } - } - }, - "request": { - "version": "2.81.0", - "from": "request@^2.81.0", - "dependencies": { - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@~0.6.0" - }, - "aws4": { - "version": "1.6.0", - "from": "aws4@^1.2.1" - }, - "caseless": { - "version": "0.12.0", - "from": "caseless@~0.12.0" - }, - "combined-stream": { - "version": "1.0.5", - "from": "combined-stream@~1.0.5", - "dependencies": { - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@~1.0.0" - } - } - }, - "extend": { - "version": "3.0.0", - "from": "extend@~3.0.0" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@~0.6.1" - }, - "form-data": { - "version": "2.1.2", - "from": "form-data@~2.1.1", - "dependencies": { - "asynckit": { - "version": "0.4.0", - "from": "asynckit@^0.4.0" - } - } - }, - "har-validator": { - "version": "4.2.1", - "from": "har-validator@~4.2.1", - "dependencies": { - "ajv": { - "version": "4.11.5", - "from": "ajv@^4.9.1", - "dependencies": { - "co": { - "version": "4.6.0", - "from": "co@^4.6.0" - }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "json-stable-stringify@^1.0.1", - "dependencies": { - "jsonify": { - "version": "0.0.0", - "from": "jsonify@~0.0.0" - } - } - } - } - }, - "har-schema": { - "version": "1.0.5", - "from": "har-schema@^1.0.5" - } - } - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@~3.1.3", - "dependencies": { - "hoek": { - "version": "2.16.3", - "from": "hoek@2.x.x" - }, - "boom": { - "version": "2.10.1", - "from": "boom@2.x.x" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@2.x.x" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@1.x.x" - } - } - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@~1.1.0", - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@^0.2.0" - }, - "jsprim": { - "version": "1.4.0", - "from": "jsprim@^1.2.2", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@1.0.0" - }, - "extsprintf": { - "version": "1.0.2", - "from": "extsprintf@1.0.2" - }, - "json-schema": { - "version": "0.2.3", - "from": "json-schema@0.2.3" - }, - "verror": { - "version": "1.3.6", - "from": "verror@1.3.6" - } - } - }, - "sshpk": { - "version": "1.11.0", - "from": "sshpk@^1.7.0", - "dependencies": { - "asn1": { - "version": "0.2.3", - "from": "asn1@~0.2.3" - }, - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@^1.0.0" - }, - "dashdash": { - "version": "1.14.1", - "from": "dashdash@^1.12.0" - }, - "getpass": { - "version": "0.1.6", - "from": "getpass@^0.1.1" - }, - "jsbn": { - "version": "0.1.1", - "from": "jsbn@~0.1.0" - }, - "tweetnacl": { - "version": "0.14.5", - "from": "tweetnacl@~0.14.0" - }, - "jodid25519": { - "version": "1.0.2", - "from": "jodid25519@^1.0.0" - }, - "ecc-jsbn": { - "version": "0.1.1", - "from": "ecc-jsbn@~0.1.1" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "from": "bcrypt-pbkdf@^1.0.0" - } - } - } - } - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@~1.0.0" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@~0.1.2" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@~5.0.1" - }, - "mime-types": { - "version": "2.1.15", - "from": "mime-types@~2.1.7", - "dependencies": { - "mime-db": { - "version": "1.27.0", - "from": "mime-db@~1.27.0" - } - } - }, - "oauth-sign": { - "version": "0.8.2", - "from": "oauth-sign@~0.8.1" - }, - "performance-now": { - "version": "0.2.0", - "from": "performance-now@^0.2.0" - }, - "qs": { - "version": "6.4.0", - "from": "qs@~6.4.0" - }, - "safe-buffer": { - "version": "5.0.1", - "from": "safe-buffer@^5.0.1" - }, - "stringstream": { - "version": "0.0.5", - "from": "stringstream@~0.0.4" - }, - "tough-cookie": { - "version": "2.3.2", - "from": "tough-cookie@~2.3.0", - "dependencies": { - "punycode": { - "version": "1.4.1", - "from": "punycode@^1.4.1" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@^0.6.0" - }, - "uuid": { - "version": "3.0.1", - "from": "uuid@^3.0.0" - } - } - }, - "rimraf": { - "version": "2.6.1", - "from": "rimraf@^2.6.1", - "dependencies": { - "glob": { - "version": "7.1.1", - "from": "glob@^7.0.5", - "dependencies": { - "fs.realpath": { - "version": "1.0.0", - "from": "fs.realpath@^1.0.0" - }, - "inflight": { - "version": "1.0.6", - "from": "inflight@^1.0.4", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - }, - "minimatch": { - "version": "3.0.3", - "from": "minimatch@^3.0.2", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@^0.4.1" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1" - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@^1.3.0", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "path-is-absolute@^1.0.0" - } - } - } - } - }, - "semver": { - "version": "5.3.0", - "from": "semver@^5.3.0" - }, - "tar": { - "version": "2.2.1", - "from": "tar@^2.2.1", - "dependencies": { - "block-stream": { - "version": "0.0.9", - "from": "block-stream@*" - }, - "fstream": { - "version": "1.0.11", - "from": "fstream@^1.0.2", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "graceful-fs@^4.1.2" - } - } - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - } - } - }, - "tar-pack": { - "version": "3.4.0", - "from": "tar-pack@^3.4.0", - "dependencies": { - "debug": { - "version": "2.6.3", - "from": "debug@^2.2.0", - "dependencies": { - "ms": { - "version": "0.7.2", - "from": "ms@0.7.2" - } - } - }, - "fstream": { - "version": "1.0.11", - "from": "fstream@^1.0.10", - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "from": "graceful-fs@^4.1.2" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - } - } - }, - "fstream-ignore": { - "version": "1.0.5", - "from": "fstream-ignore@^1.0.5", - "dependencies": { - "inherits": { - "version": "2.0.3", - "from": "inherits@2" - }, - "minimatch": { - "version": "3.0.3", - "from": "minimatch@^3.0.0", - "dependencies": { - "brace-expansion": { - "version": "1.1.6", - "from": "brace-expansion@^1.0.0", - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "from": "balanced-match@^0.4.1" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1" - } - } - } - } - } - } - }, - "once": { - "version": "1.4.0", - "from": "once@^1.3.3", - "dependencies": { - "wrappy": { - "version": "1.0.2", - "from": "wrappy@1" - } - } - }, - "readable-stream": { - "version": "2.2.6", - "from": "readable-stream@^2.1.4", - "dependencies": { - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@^1.0.0" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@~1.0.0" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@~1.0.0" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@~2.0.1" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@~1.0.6" - }, - "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@~0.10.x" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@~1.0.1" - } - } - }, - "uid-number": { - "version": "0.0.6", - "from": "uid-number@^0.0.6" - } - } - } - } - } - } - }, - "aws-sdk": { - "version": "2.37.0", - "from": "aws-sdk@^2.1.34", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.37.0.tgz", - "dependencies": { - "buffer": { - "version": "4.9.1", - "from": "buffer@4.9.1", - "dependencies": { - "base64-js": { - "version": "1.2.0", - "from": "base64-js@^1.0.2" - }, - "ieee754": { - "version": "1.1.8", - "from": "ieee754@^1.1.4" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@^1.0.0" - } - } - }, - "crypto-browserify": { - "version": "1.0.9", - "from": "crypto-browserify@1.0.9" - }, - "jmespath": { - "version": "0.15.0", - "from": "jmespath@0.15.0" - }, - "querystring": { - "version": "0.2.0", - "from": "querystring@0.2.0" - }, - "sax": { - "version": "1.1.5", - "from": "sax@1.1.5" - }, - "url": { - "version": "0.10.3", - "from": "url@0.10.3", - "dependencies": { - "punycode": { - "version": "1.3.2", - "from": "punycode@1.3.2" - } - } - }, - "uuid": { - "version": "3.0.0", - "from": "uuid@3.0.0" - }, - "xml2js": { - "version": "0.4.15", - "from": "xml2js@0.4.15" - }, - "xmlbuilder": { - "version": "2.6.2", - "from": "xmlbuilder@2.6.2", - "dependencies": { - "lodash": { - "version": "3.5.0", - "from": "lodash@~3.5.0" - } - } - } - } - } - } -} diff --git a/services/track-changes/package.json b/services/track-changes/package.json index a2656c3840..31f6b2a0ea 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -3,34 +3,35 @@ "version": "0.1.4", "description": "An API for saving and compressing individual document updates into a browsable history", "repository": { - "type": "git", + "type": "git", "url": "https://github.com/sharelatex/track-changes-sharelatex.git" }, "dependencies": { + "JSONStream": "^1.0.4", "async": "~0.2.10", + "aws-sdk": "^2.102.0", "bson": "^0.4.20", "byline": "^4.2.1", "cli": "^0.6.6", "express": "3.3.5", + "heap": "^0.2.6", "line-reader": "^0.2.4", - "mongojs": "^1.4.1", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", + "mongo-uri": "^0.1.2", + "mongojs": "2.4.0", + "redis": "~0.10.1", + "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", "request": "~2.33.0", "requestretry": "^1.12.0", - "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", - "redis": "~0.10.1", - "underscore": "~1.7.0", - "mongo-uri": "^0.1.2", "s3-streams": "^0.3.0", - "JSONStream": "^1.0.4", - "heap": "^0.2.6", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "underscore": "~1.7.0", "v8-profiler": "^5.6.5" }, "devDependencies": { - "chai": "~1.9.0", - "sinon": "~1.8.2", + "chai": "~4.1.1", + "sinon": "~3.2.1", "sandboxed-module": "~0.3.0", "grunt-execute": "~0.1.5", "grunt-contrib-clean": "~0.5.0", @@ -38,7 +39,7 @@ "grunt": "~0.4.2", "grunt-available-tasks": "~0.4.2", "grunt-contrib-coffee": "~0.10.1", - "bunyan": "~0.22.1", + "bunyan": "~2.0.2", "grunt-bunyan": "~0.5.0", "grunt-forever": "~0.4.2", "timekeeper": "0.0.4", diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 2dc289d89a..8c23b82f72 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -30,7 +30,7 @@ describe "MongoAWS", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() - @pack_id = ObjectId() + @pack_id = ObjectId().toString() @update = { v:123 } @callback = sinon.stub() diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee index 2329cf79f5..3339a4242d 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee @@ -31,7 +31,7 @@ describe "DocumentUpdaterManager", -> @request.get.calledWith(url).should.equal true it "should call the callback with the content and version", -> - @callback.calledWith(null, @lines.join("\n"), @version, @ops).should.equal true + @callback.calledWith(null, @lines.join("\n"), @version).should.equal true describe "when the document updater API returns an error", -> beforeEach -> @@ -48,7 +48,7 @@ describe "DocumentUpdaterManager", -> it "should return the callback with an error", -> @callback - .calledWith(new Error("doc updater returned failure status code: 500")) + .calledWith(sinon.match.has('message', "doc updater returned a non-success status code: 500")) .should.equal true describe "setDocument", -> @@ -91,5 +91,5 @@ describe "DocumentUpdaterManager", -> it "should return the callback with an error", -> @callback - .calledWith(new Error("doc updater returned failure status code: 500")) - .should.equal true \ No newline at end of file + .calledWith(sinon.match.has('message', "doc updater returned a non-success status code: 500")) + .should.equal true diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee index ad4df198a9..ed30cb2815 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee @@ -194,5 +194,5 @@ describe "LockManager", -> @LockManager.releaseLock @key, @lockValue, @callback it 'should return an error if the lock has expired', -> - @callback.calledWith(new Error("tried to release timed out lock")).should.equal true + @callback.calledWith(sinon.match.has('message', "tried to release timed out lock")).should.equal true diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee index 283e288315..f6243619d2 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee @@ -316,7 +316,7 @@ describe "PackManager", -> it "should call the callback", -> @callback.called.should.equal true it "should return an error", -> - @callback.calledWith(new Error()).should.equal true + @callback.calledWith(sinon.match.has('message')).should.equal true describe "when an archive is completed", -> beforeEach -> @@ -326,17 +326,17 @@ describe "PackManager", -> it "should call the callback", -> @callback.called.should.equal true it "should return an error", -> - @callback.calledWith(new Error()).should.equal true + @callback.calledWith(sinon.match.has('message')).should.equal true describe "when the archive has not started or completed", -> beforeEach -> @db.docHistoryIndex = findOne: sinon.stub().callsArgWith(2, null, {}) @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback - it "should call the callback", -> + it "should call the callback with no error", -> @callback.called.should.equal true it "should return with no error", -> - @callback.calledWith(undefined).should.equal true + (typeof @callback.lastCall.args[0]).should.equal 'undefined' # describe "setTTLOnArchivedPack", -> # beforeEach -> diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 88f1c7998a..1943116341 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -97,6 +97,7 @@ describe "UpdatesManager", -> @lastCompressedUpdate = {pack: [{ v: 11, op: "compressed-op-11" }], v:11} @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) + # @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback it "should look at the last compressed op", -> @@ -104,9 +105,12 @@ describe "UpdatesManager", -> .calledWith(@doc_id) .should.equal true - it "should defer the compression of raw ops until they are written in a new pack", -> - @UpdateCompressor.compressRawUpdates - .should.not.be.called + # FIXME: Broken test, was hidden by an api mistake + + # it "should defer the compression of raw ops until they are written in a new pack", -> + # console.log @UpdateCompressor.compressRawUpdates.called + # @UpdateCompressor.compressRawUpdates + # .called.should.not.equal true it "should save the new compressed ops into a pack", -> @PackManager.insertCompressedUpdates @@ -134,7 +138,7 @@ describe "UpdatesManager", -> it "should call the callback with an error", -> @callback - .calledWith(new Error("Tried to apply raw op at version 13 to last compressed update with version 11")) + .calledWith(sinon.match.has('message', "Tried to apply raw op at version 13 to last compressed update with version 11 from unknown time")) .should.equal true it "should not insert any update into mongo", -> @@ -147,7 +151,7 @@ describe "UpdatesManager", -> it "should call the callback with an error", -> @callback - .calledWith(new Error) + .calledWith(sinon.match.has('message')) .should.equal true it "should not insert any update into mongo", -> diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee index fe33d08a4a..92c0fcd54a 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee @@ -64,12 +64,12 @@ describe "WebApiManager", -> describe "when the web returns a failure error code", -> beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") + @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42}, "") @WebApiManager.getUserInfo @user_id, @callback it "should return the callback with an error", -> @callback - .calledWith(new Error("web returned failure status code: 500")) + .calledWith(sinon.match.has('message', "web returned a non-success status code: 500 (attempts: 42)")) .should.equal true describe "when the user cannot be found", -> @@ -114,10 +114,10 @@ describe "WebApiManager", -> describe "when the web returns a failure error code", -> beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") + @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42 }, "") @WebApiManager.getProjectDetails @project_id, @callback it "should return the callback with an error", -> @callback - .calledWith(new Error("web returned failure status code: 500")) + .calledWith(sinon.match.has('message', "web returned a non-success status code: 500 (attempts: 42)")) .should.equal true From 7c9ca7ccef0fb999111c6c9168315a3bc3a63b05 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 25 Aug 2017 14:46:47 +0100 Subject: [PATCH 401/549] Revert accidental change --- .../track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 8c23b82f72..2dc289d89a 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -30,7 +30,7 @@ describe "MongoAWS", -> @project_id = ObjectId().toString() @doc_id = ObjectId().toString() - @pack_id = ObjectId().toString() + @pack_id = ObjectId() @update = { v:123 } @callback = sinon.stub() From cd2ed249379b501e1cbc45759a48aec6eecf1927 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 25 Aug 2017 14:47:07 +0100 Subject: [PATCH 402/549] Fix WriteStream API --- .../track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee index 2dc289d89a..3424418935 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee @@ -39,7 +39,8 @@ describe "MongoAWS", -> beforeEach (done) -> @awssdk.config = { update: sinon.stub() } @awssdk.S3 = sinon.stub() - @S3S.WriteStream = MemoryStream.createWriteStream + @S3S.WriteStream = () -> + MemoryStream.createWriteStream() @db.docHistory = {} @db.docHistory.findOne = sinon.stub().callsArgWith(1, null, {"pack":"hello"}) From f6fdc5a8270872d0b73f59f6dbec386e3a529bad Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 25 Aug 2017 14:50:33 +0100 Subject: [PATCH 403/549] Remove stray log --- services/track-changes/app/coffee/WebApiManager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.coffee index a95808deee..aee8d431dd 100644 --- a/services/track-changes/app/coffee/WebApiManager.coffee +++ b/services/track-changes/app/coffee/WebApiManager.coffee @@ -30,7 +30,6 @@ module.exports = WebApiManager = return callback null, body else error = new Error("web returned a non-success status code: #{res.statusCode} (attempts: #{res.attempts})") - console.log error.message callback error getUserInfo: (user_id, callback = (error, userInfo) ->) -> From ad43ddb6c610b5aea2f1b06a164334e9509a41c4 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 25 Aug 2017 14:52:14 +0100 Subject: [PATCH 404/549] Remove commented-out code --- .../test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 1943116341..c669bb1c68 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -97,7 +97,6 @@ describe "UpdatesManager", -> @lastCompressedUpdate = {pack: [{ v: 11, op: "compressed-op-11" }], v:11} @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) - # @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback it "should look at the last compressed op", -> From 1cd80b9dcde52259ee0ccea7bbedac40645cd931 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 25 Aug 2017 14:53:39 +0100 Subject: [PATCH 405/549] Restore a broken test that was commented out temporarily --- .../coffee/UpdatesManager/UpdatesManagerTests.coffee | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index c669bb1c68..dec00765c7 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -105,11 +105,10 @@ describe "UpdatesManager", -> .should.equal true # FIXME: Broken test, was hidden by an api mistake - - # it "should defer the compression of raw ops until they are written in a new pack", -> - # console.log @UpdateCompressor.compressRawUpdates.called - # @UpdateCompressor.compressRawUpdates - # .called.should.not.equal true + it "should defer the compression of raw ops until they are written in a new pack", -> + console.log @UpdateCompressor.compressRawUpdates.called + @UpdateCompressor.compressRawUpdates + .called.should.not.equal true it "should save the new compressed ops into a pack", -> @PackManager.insertCompressedUpdates From ca991c1efd84934a13aee73907b64458e544e3e7 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 29 Aug 2017 15:10:58 +0100 Subject: [PATCH 406/549] fix broken unit test the behaviour of the code changed so that raw ops are always compressed --- .../unit/coffee/UpdatesManager/UpdatesManagerTests.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee index 88f1c7998a..1137947870 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee @@ -104,9 +104,10 @@ describe "UpdatesManager", -> .calledWith(@doc_id) .should.equal true - it "should defer the compression of raw ops until they are written in a new pack", -> + it "should compress the raw ops", -> @UpdateCompressor.compressRawUpdates - .should.not.be.called + .calledWith(null, @rawUpdates) + .should.equal true it "should save the new compressed ops into a pack", -> @PackManager.insertCompressedUpdates From 76cae1acf49663cb9566cf6df1b27515604f75d5 Mon Sep 17 00:00:00 2001 From: Joe Green Date: Mon, 4 Sep 2017 14:57:36 +0100 Subject: [PATCH 407/549] build.txt --- services/track-changes/Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 3bf41fae36..7430dd26a6 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -43,6 +43,7 @@ pipeline { } stage('Package') { steps { + sh 'echo ${BUILD_NUMBER} > build_number.txt' sh 'touch build.tar.gz' // Avoid tar warning about files changing during read sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' } @@ -51,6 +52,8 @@ pipeline { steps { withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") + // The deployment process uses this file to figure out the latest build + s3Upload(file:'build_number.txt', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/latest") } } } From dbef5904be2553cea89ab3f75f1e19f1710a5c71 Mon Sep 17 00:00:00 2001 From: Joe Green Date: Wed, 20 Sep 2017 15:46:48 +0100 Subject: [PATCH 408/549] use correct node version in jenkinsfile --- services/track-changes/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 7430dd26a6..72206c4485 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { agent { docker { - image 'sharelatex/node:0.10.22' + image 'node:6.11.2' args "-v /var/lib/jenkins/.npm:/tmp/.npm" } } From 90f2b850348e3259479c3d93b9b4f1462ed826db Mon Sep 17 00:00:00 2001 From: Joe Green Date: Thu, 12 Oct 2017 16:57:06 +0100 Subject: [PATCH 409/549] only alert on master --- services/track-changes/Jenkinsfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 72206c4485..bf5bb0efb9 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -61,6 +61,10 @@ pipeline { post { failure { + when { + branch 'master' + } + mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", From 67be2feb231011f8d834cce25ebce7008aa811fe Mon Sep 17 00:00:00 2001 From: Joe Green Date: Mon, 16 Oct 2017 14:13:00 +0100 Subject: [PATCH 410/549] Update Jenkinsfile --- services/track-changes/Jenkinsfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index bf5bb0efb9..72206c4485 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -61,10 +61,6 @@ pipeline { post { failure { - when { - branch 'master' - } - mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", From dacc7ebabb4c17b8f0dbd5a6543df094462234de Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 20 Oct 2017 15:24:58 +0100 Subject: [PATCH 411/549] exit if mock servers fail to start --- .../test/acceptance/coffee/helpers/MockDocStoreApi.coffee | 3 +++ .../test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee | 4 ++++ .../test/acceptance/coffee/helpers/MockWebApi.coffee | 3 +++ 3 files changed, 10 insertions(+) diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee index 29864479a4..0a0ddc72e3 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee @@ -19,6 +19,9 @@ module.exports = MockDocUpdaterApi = app.listen 3016, (error) -> throw error if error? + .on "error", (error) -> + console.error "error starting MockDocStoreApi:", error.message + process.exit(1) MockDocUpdaterApi.run() diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee index 5ef3506b9c..2c87fde358 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -31,6 +31,10 @@ module.exports = MockDocUpdaterApi = app.listen 3003, (error) -> throw error if error? + .on "error", (error) -> + console.error "error starting MockDocUpdaterApi:", error.message + process.exit(1) + MockDocUpdaterApi.run() diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee index 7beb67075e..97b4ea33ca 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee @@ -33,6 +33,9 @@ module.exports = MockWebApi = app.listen 3000, (error) -> throw error if error? + .on "error", (error) -> + console.error "error starting MockWebApiServer:", error.message + process.exit(1) MockWebApi.run() From b9adad72be0ba1fddacf695123f70fccc183da53 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 30 Oct 2017 11:09:25 +0000 Subject: [PATCH 412/549] upgrade to ioredis 3 via redis-sharelatex --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 31f6b2a0ea..81363c30f8 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -21,7 +21,7 @@ "mongo-uri": "^0.1.2", "mongojs": "2.4.0", "redis": "~0.10.1", - "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.2", + "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.4", "request": "~2.33.0", "requestretry": "^1.12.0", "s3-streams": "^0.3.0", From dd008e1c7dd7679448e9e40016d840859507b39b Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 29 Dec 2017 08:18:04 +0000 Subject: [PATCH 413/549] Provide hosts as environment settings and add npm run start script --- .../config/settings.defaults.coffee | 16 ++++++++-------- services/track-changes/package.json | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 907d3c0033..6cc3b51c35 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -3,31 +3,31 @@ TMP_DIR = Path.resolve(Path.join(__dirname, "../../", "tmp")) module.exports = mongo: - url: 'mongodb://127.0.0.1/sharelatex' + url: "mongodb://#{process.env["MONGO_HOST"] or "localhost"}/sharelatex" internal: trackchanges: port: 3015 - host: "localhost" + host: process.env["LISTEN_ADDRESS"] or "localhost" apis: documentupdater: - url: "http://localhost:3003" + url: "http://#{process.env["DOCUPDATER_HOST"] or "localhost"}:3003" docstore: - url: "http://localhost:3016" + url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" web: - url: "http://localhost:3000" + url: "http://#{process.env["WEB_HOST"] or "localhost"}:3000" user: "sharelatex" pass: "password" redis: lock: - host: "localhost" + host: process.env["REDIS_HOST"] or "localhost" port: 6379 pass: "" key_schema: historyLock: ({doc_id}) -> "HistoryLock:#{doc_id}" historyIndexLock: ({project_id}) -> "HistoryIndexLock:#{project_id}" history: - port:"6379" - host:"localhost" + port: "6379" + host: process.env["REDIS_HOST"] or "localhost" password:"" key_schema: uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:#{doc_id}" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 81363c30f8..ee3ceddd13 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -6,6 +6,10 @@ "type": "git", "url": "https://github.com/sharelatex/track-changes-sharelatex.git" }, + "scripts": { + "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", + "start": "npm run compile:app && node app.js" + }, "dependencies": { "JSONStream": "^1.0.4", "async": "~0.2.10", From 3de7970e0ba6447efc1914710d0d5907c57aefaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Fern=C3=A1ndez=20Capel?= Date: Tue, 1 May 2018 09:21:20 +0100 Subject: [PATCH 414/549] Make travis read node version from .nvmrc file --- services/track-changes/.travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml index ffa7d7f9f8..ccad90efe3 100644 --- a/services/track-changes/.travis.yml +++ b/services/track-changes/.travis.yml @@ -1,8 +1,5 @@ language: node_js -node_js: - - "0.10" - before_install: - npm install -g grunt-cli From fca95b5b27b836c4b77b8d083f18117785308583 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 24 May 2018 12:02:27 +0100 Subject: [PATCH 415/549] dockerise app - 1.1.3 build scripts - add env vars for configuring s3 - add docker file --- services/track-changes/.dockerignore | 9 +++ services/track-changes/Dockerfile | 22 +++++++ services/track-changes/Jenkinsfile | 57 ++++++++----------- services/track-changes/Makefile | 45 +++++++++++++++ services/track-changes/app.coffee | 14 +++-- .../config/settings.defaults.coffee | 9 ++- services/track-changes/docker-compose.ci.yml | 35 ++++++++++++ services/track-changes/docker-compose.yml | 42 ++++++++++++++ services/track-changes/nodemon.json | 19 +++++++ services/track-changes/package.json | 13 ++++- .../coffee/AppendingUpdatesTests.coffee | 13 ++++- .../coffee/ArchivingUpdatesTests.coffee | 13 +++-- .../coffee/FlushingUpdatesTests.coffee | 6 +- .../coffee/GettingADiffTests.coffee | 18 +++--- .../coffee/GettingUpdatesTests.coffee | 10 ++-- .../acceptance/coffee/LockManagerTests.coffee | 7 ++- .../coffee/RestoringVersions.coffee | 10 ++-- .../coffee/helpers/TrackChangesApp.coffee | 24 ++++++++ 18 files changed, 296 insertions(+), 70 deletions(-) create mode 100644 services/track-changes/.dockerignore create mode 100644 services/track-changes/Dockerfile create mode 100644 services/track-changes/Makefile create mode 100644 services/track-changes/docker-compose.ci.yml create mode 100644 services/track-changes/docker-compose.yml create mode 100644 services/track-changes/nodemon.json create mode 100644 services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee diff --git a/services/track-changes/.dockerignore b/services/track-changes/.dockerignore new file mode 100644 index 0000000000..386f26df30 --- /dev/null +++ b/services/track-changes/.dockerignore @@ -0,0 +1,9 @@ +node_modules/* +gitrev +.git +.gitignore +.npm +.nvmrc +nodemon.json +app.js +**/js/* diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile new file mode 100644 index 0000000000..4fe121ef4c --- /dev/null +++ b/services/track-changes/Dockerfile @@ -0,0 +1,22 @@ +FROM node:6.11.2 as app + +WORKDIR /app + +#wildcard as some files may not be in all repos +COPY package*.json npm-shrink*.json /app/ + +RUN npm install --quiet + +COPY . /app + + +RUN npm run compile:all + +FROM node:6.11.2 + +COPY --from=app /app /app + +WORKDIR /app +USER node + +CMD ["node","app.js"] diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 72206c4485..e8eefc2e9d 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -1,57 +1,44 @@ +String cron_string = BRANCH_NAME == "master" ? "@daily" : "" + pipeline { - - agent { - docker { - image 'node:6.11.2' - args "-v /var/lib/jenkins/.npm:/tmp/.npm" - } - } - - environment { - HOME = "/tmp" - } + agent any triggers { pollSCM('* * * * *') - cron('@daily') + cron(cron_string) } stages { - stage('Set up') { + stage('Build') { steps { - // we need to disable logallrefupdates, else git clones during the npm install will require git to lookup the user id - // which does not exist in the container's /etc/passwd file, causing the clone to fail. - sh 'git config --global core.logallrefupdates false' + sh 'make build' } } - stage('Install') { + + stage('Unit Tests') { steps { - sh 'rm -fr node_modules' - sh 'npm install' - sh 'npm install --quiet grunt-cli' + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_unit' } } - stage('Compile') { + + stage('Acceptance Tests') { steps { - sh 'node_modules/.bin/grunt compile' + withCredentials([usernamePassword(credentialsId: 'S3_DOCSTORE_TEST_AWS_KEYS', passwordVariable: 'AWS_SECRET_ACCESS_KEY', usernameVariable: 'AWS_ACCESS_KEY_ID')]) { + sh 'AWS_BUCKET="sl-doc-archive-testing" AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + } } } - stage('Test') { + + stage('Package and publish build') { steps { - sh 'NODE_ENV=development node_modules/.bin/grunt test:unit' + sh 'make publish' } } - stage('Package') { - steps { - sh 'echo ${BUILD_NUMBER} > build_number.txt' - sh 'touch build.tar.gz' // Avoid tar warning about files changing during read - sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' - } - } - stage('Publish') { + + stage('Publish build number') { steps { + sh 'echo ${BRANCH_NAME}-${BUILD_NUMBER} > build_number.txt' withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { - s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") // The deployment process uses this file to figure out the latest build s3Upload(file:'build_number.txt', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/latest") } @@ -60,6 +47,10 @@ pipeline { } post { + always { + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_clean' + } + failure { mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile new file mode 100644 index 0000000000..b26c530f57 --- /dev/null +++ b/services/track-changes/Makefile @@ -0,0 +1,45 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.1.3 + +BUILD_NUMBER ?= local +BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) +PROJECT_NAME = track-changes +DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml +DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ + BRANCH_NAME=$(BRANCH_NAME) \ + PROJECT_NAME=$(PROJECT_NAME) \ + MOCHA_GREP=${MOCHA_GREP} \ + AWS_BUCKET=${AWS_BUCKET} \ + AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ + AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ + docker-compose ${DOCKER_COMPOSE_FLAGS} + + +clean: + rm -f app.js + rm -rf app/js + rm -rf test/unit/js + rm -rf test/acceptance/js + +test: test_unit test_acceptance + +test_unit: + @[ ! -d test/unit ] && echo "track-changes has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit + +test_acceptance: test_clean test_acceptance_pre_run # clear the database before each acceptance test run + @[ ! -d test/acceptance ] && echo "track-changes has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance + +test_clean: + $(DOCKER_COMPOSE) down -v -t 0 + +test_acceptance_pre_run: + @[ ! -f test/acceptance/scripts/pre-run ] && echo "track-changes has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run +build: + docker build --pull --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . + +publish: + docker push gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + +.PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index a83d78c7f1..513cdb7f50 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -92,9 +92,13 @@ app.use (error, req, res, next) -> port = Settings.internal?.trackchanges?.port or 3015 host = Settings.internal?.trackchanges?.host or "localhost" -app.listen port, host, (error) -> - if error? - logger.error err: error, "could not start track-changes server" - else - logger.info "trackchanges starting up, listening on #{host}:#{port}" + +if !module.parent # Called directly + app.listen port, host, (error) -> + if error? + logger.error err: error, "could not start track-changes server" + else + logger.info "trackchanges starting up, listening on #{host}:#{port}" + +module.exports = app diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 6cc3b51c35..4bc5962d77 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -35,11 +35,10 @@ module.exports = trackchanges: s3: - key: "" - secret: "" + key: process.env['AWS_ACCESS_KEY_ID'] + secret: process.env['AWS_SECRET_ACCESS_KEY'] stores: - doc_history: "" - - + doc_history: process.env['AWS_BUCKET'] + path: dumpFolder: Path.join(TMP_DIR, "dumpFolder") diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml new file mode 100644 index 0000000000..18a454adfd --- /dev/null +++ b/services/track-changes/docker-compose.ci.yml @@ -0,0 +1,35 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.1.3 + +version: "2" + +services: + test_unit: + image: gcr.io/csh-gcdm-test/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + user: node + command: npm run test:unit:_run + + test_acceptance: + build: . + image: gcr.io/csh-gcdm-test/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + POSTGRES_HOST: postgres + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_BUCKET: ${AWS_BUCKET} + depends_on: + - mongo + - redis + user: node + command: npm run test:acceptance:_run + + redis: + image: redis + + mongo: + image: mongo:3.4 + diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml new file mode 100644 index 0000000000..31fe46d3ed --- /dev/null +++ b/services/track-changes/docker-compose.yml @@ -0,0 +1,42 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.1.3 + +version: "2" + +services: + test_unit: + build: . + volumes: + - .:/app + working_dir: /app + environment: + MOCHA_GREP: ${MOCHA_GREP} + command: npm run test:unit + user: node + + test_acceptance: + build: . + volumes: + - .:/app + working_dir: /app + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + POSTGRES_HOST: postgres + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_BUCKET: ${AWS_BUCKET} + MOCHA_GREP: ${MOCHA_GREP} + user: node + depends_on: + - mongo + - redis + command: npm run test:acceptance + redis: + image: redis + + mongo: + image: mongo:3.4 + diff --git a/services/track-changes/nodemon.json b/services/track-changes/nodemon.json new file mode 100644 index 0000000000..98db38d71b --- /dev/null +++ b/services/track-changes/nodemon.json @@ -0,0 +1,19 @@ +{ + "ignore": [ + ".git", + "node_modules/" + ], + "verbose": true, + "legacyWatch": true, + "execMap": { + "js": "npm run start" + }, + + "watch": [ + "app/coffee/", + "app.coffee", + "config/" + ], + "ext": "coffee" + +} diff --git a/services/track-changes/package.json b/services/track-changes/package.json index ee3ceddd13..b8781ed635 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -7,8 +7,16 @@ "url": "https://github.com/sharelatex/track-changes-sharelatex.git" }, "scripts": { - "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", - "start": "npm run compile:app && node app.js" + "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", + "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", + "test:acceptance:_run": "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_BUCKET=$AWS_BUCKET AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", + "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", + "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", + "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", + "nodemon": "nodemon --config nodemon.json" }, "dependencies": { "JSONStream": "^1.0.4", @@ -44,6 +52,7 @@ "grunt-available-tasks": "~0.4.2", "grunt-contrib-coffee": "~0.10.1", "bunyan": "~2.0.2", + "mocha": "^4.0.1", "grunt-bunyan": "~0.5.0", "grunt-forever": "~0.4.2", "timekeeper": "0.0.4", diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 69d378fd9d..e018cd2be0 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -6,12 +6,18 @@ mongojs = require "../../../app/js/mongojs" ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" request = require "request" -rclient = require("redis").createClient() # Only works locally for now +console.log "hiiiiis" +console.log Settings.redis.history +rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +TrackChangesApp = require "./helpers/TrackChangesApp" TrackChangesClient = require "./helpers/TrackChangesClient" MockWebApi = require "./helpers/MockWebApi" describe "Appending doc ops to the history", -> + before (done)-> + TrackChangesApp.ensureRunning done + describe "when the history does not exist yet", -> before (done) -> @project_id = ObjectId().toString() @@ -81,6 +87,7 @@ describe "Appending doc ops to the history", -> describe "when the updates are recent and from the same user", -> beforeEach (done) -> + console.log 1 TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now(), user_id: @user_id } @@ -94,9 +101,13 @@ describe "Appending doc ops to the history", -> meta: { ts: Date.now(), user_id: @user_id } v: 8 }], (error) => + console.log 2, error throw error if error? + console.log 3 TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => + console.log 4, error throw error if error? + console.log 5 done() it "should combine all the updates into one pack", -> diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 398856e4d3..74d5d4031d 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -7,8 +7,9 @@ db = mongojs.db ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" request = require "request" -rclient = require("redis").createClient() # Only works locally for now +rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +TrackChangesApp = require "./helpers/TrackChangesApp" TrackChangesClient = require "./helpers/TrackChangesClient" MockDocStoreApi = require "./helpers/MockDocStoreApi" MockWebApi = require "./helpers/MockWebApi" @@ -53,12 +54,12 @@ describe "Archiving updates", -> meta: { ts: @now + (i-2048) * @hours + 10*@minutes, user_id: @user_id } v: 2 * i + 2 } - - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> + TrackChangesApp.ensureRunning => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? - done() + TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> + throw error if error? + done() after (done) -> MockWebApi.getUserInfo.restore() diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee index 3f82304da1..b3fd843c6b 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -6,12 +6,16 @@ mongojs = require "../../../app/js/mongojs" ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" request = require "request" -rclient = require("redis").createClient() # Only works locally for now +rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +TrackChangesApp = require "./helpers/TrackChangesApp" TrackChangesClient = require "./helpers/TrackChangesClient" MockWebApi = require "./helpers/MockWebApi" describe "Flushing updates", -> + before (done)-> + TrackChangesApp.ensureRunning done + describe "flushing a doc's updates", -> before (done) -> @project_id = ObjectId().toString() diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index f64f42ff87..916702eecf 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -7,12 +7,14 @@ db = mongojs.db ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" +TrackChangesApp = require "./helpers/TrackChangesApp" TrackChangesClient = require "./helpers/TrackChangesClient" MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" MockWebApi = require "./helpers/MockWebApi" describe "Getting a diff", -> - before (done) -> + + beforeEach (done) -> sinon.spy MockDocUpdaterApi, "getDoc" @now = Date.now() @@ -58,15 +60,15 @@ describe "Getting a diff", -> MockDocUpdaterApi.docs[@doc_id] = lines: @lines version: 7 - - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.getDiff @project_id, @doc_id, @fromVersion, @toVersion, (error, diff) => + TrackChangesApp.ensureRunning => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? - @diff = diff.diff - done() + TrackChangesClient.getDiff @project_id, @doc_id, @fromVersion, @toVersion, (error, diff) => + throw error if error? + @diff = diff.diff + done() - after () -> + afterEach () -> MockDocUpdaterApi.getDoc.restore() MockWebApi.getUserInfo.restore() diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index d01797e25f..e8619be06e 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -7,6 +7,7 @@ db = mongojs.db ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" +TrackChangesApp = require "./helpers/TrackChangesApp" TrackChangesClient = require "./helpers/TrackChangesClient" MockWebApi = require "./helpers/MockWebApi" @@ -46,10 +47,11 @@ describe "Getting updates", -> v: 2 * i + 2 } @updates[0].meta.user_id = @deleted_user_id - - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - done() + + TrackChangesApp.ensureRunning => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => + throw error if error? + done() after: () -> MockWebApi.getUserInfo.restore() diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee b/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee index 6de4599a6b..0468a75109 100644 --- a/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee +++ b/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee @@ -6,9 +6,14 @@ mongojs = require "../../../app/js/mongojs" ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" LockManager = require "../../../app/js/LockManager" -rclient = require("redis").createClient() # Only works locally for now +rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +TrackChangesApp = require "./helpers/TrackChangesApp" describe "Locking document", -> + + before (done)-> + TrackChangesApp.ensureRunning done + describe "when the lock has expired in redis", -> before (done) -> LockManager.LOCK_TTL = 1 # second diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index b09328d2ea..62d374c28e 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -7,6 +7,7 @@ db = mongojs.db ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" +TrackChangesApp = require "./helpers/TrackChangesApp" TrackChangesClient = require "./helpers/TrackChangesClient" MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" MockWebApi = require "./helpers/MockWebApi" @@ -54,11 +55,12 @@ describe "Restoring a version", -> lines: @lines version: 7 - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) => + TrackChangesApp.ensureRunning => + TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? - done() + TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) => + throw error if error? + done() after () -> MockDocUpdaterApi.setDoc.restore() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee new file mode 100644 index 0000000000..187427f56d --- /dev/null +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee @@ -0,0 +1,24 @@ +app = require('../../../../app') +require("logger-sharelatex").logger.level("info") +logger = require("logger-sharelatex") +Settings = require("settings-sharelatex") + +module.exports = + running: false + initing: false + callbacks: [] + ensureRunning: (callback = (error) ->) -> + if @running + return callback() + else if @initing + @callbacks.push callback + else + @initing = true + @callbacks.push callback + app.listen Settings.internal?.trackchanges?.port, "localhost", (error) => + throw error if error? + @running = true + logger.log("track changes running in dev mode") + + for callback in @callbacks + callback() \ No newline at end of file From a6ab549d29419714c4d3d141cd5c1fa9514c277b Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 29 May 2018 15:39:52 +0100 Subject: [PATCH 416/549] Update build_scripts to 1.1.4 (draft) --- services/track-changes/Jenkinsfile | 2 +- services/track-changes/Makefile | 4 ++-- services/track-changes/docker-compose.ci.yml | 5 +++-- services/track-changes/docker-compose.yml | 4 ++-- services/track-changes/package.json | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index e8eefc2e9d..a779f94b6f 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -24,7 +24,7 @@ pipeline { stage('Acceptance Tests') { steps { withCredentials([usernamePassword(credentialsId: 'S3_DOCSTORE_TEST_AWS_KEYS', passwordVariable: 'AWS_SECRET_ACCESS_KEY', usernameVariable: 'AWS_ACCESS_KEY_ID')]) { - sh 'AWS_BUCKET="sl-doc-archive-testing" AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + sh 'AWS_BUCKET="sl-acceptance-tests" AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' } } } diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index b26c530f57..bb921ac06f 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.4 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -14,7 +14,7 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ AWS_BUCKET=${AWS_BUCKET} \ AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ - docker-compose ${DOCKER_COMPOSE_FLAGS} + docker-compose ${DOCKER_COMPOSE_FLAGS} clean: diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 18a454adfd..b82ec852ac 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.4 version: "2" @@ -18,9 +18,10 @@ services: REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_BUCKET: ${AWS_BUCKET} + MOCHA_GREP: ${MOCHA_GREP} depends_on: - mongo - redis diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 31fe46d3ed..fd540dae99 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.4 version: "2" @@ -25,8 +25,8 @@ services: REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_BUCKET: ${AWS_BUCKET} MOCHA_GREP: ${MOCHA_GREP} user: node diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b8781ed635..006a3b78a9 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -11,7 +11,7 @@ "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_BUCKET=$AWS_BUCKET AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", - "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", From f26bebca4daa4ac50056fb38bfc3e202847b39d7 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 4 Oct 2018 10:32:21 +0100 Subject: [PATCH 417/549] update build scripts --- .../track-changes/.github/ISSUE_TEMPLATE.md | 38 +++++++++++++ .../.github/PULL_REQUEST_TEMPLATE.md | 45 +++++++++++++++ services/track-changes/Jenkinsfile | 55 +++++++++++++++++-- services/track-changes/Makefile | 16 +++--- services/track-changes/buildscript.txt | 9 +++ services/track-changes/docker-compose.ci.yml | 12 ++-- services/track-changes/docker-compose.yml | 7 +-- services/track-changes/package.json | 9 +-- 8 files changed, 165 insertions(+), 26 deletions(-) create mode 100644 services/track-changes/.github/ISSUE_TEMPLATE.md create mode 100644 services/track-changes/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 services/track-changes/buildscript.txt diff --git a/services/track-changes/.github/ISSUE_TEMPLATE.md b/services/track-changes/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..e0093aa90c --- /dev/null +++ b/services/track-changes/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,38 @@ + + +## Steps to Reproduce + + + +1. +2. +3. + +## Expected Behaviour + + +## Observed Behaviour + + + +## Context + + +## Technical Info + + +* URL: +* Browser Name and version: +* Operating System and version (desktop or mobile): +* Signed in as: +* Project and/or file: + +## Analysis + + +## Who Needs to Know? + + + +- +- diff --git a/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md b/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..ed25ee83c1 --- /dev/null +++ b/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,45 @@ + + +### Description + + + +#### Screenshots + + + +#### Related Issues / PRs + + + +### Review + + + +#### Potential Impact + + + +#### Manual Testing Performed + +- [ ] +- [ ] + +#### Accessibility + + + +### Deployment + + + +#### Deployment Checklist + +- [ ] Update documentation not included in the PR (if any) +- [ ] + +#### Metrics and Monitoring + + + +#### Who Needs to Know? diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index e8eefc2e9d..7d01259ddf 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -3,12 +3,33 @@ String cron_string = BRANCH_NAME == "master" ? "@daily" : "" pipeline { agent any + environment { + GIT_PROJECT = "track-changes-sharelatex" + JENKINS_WORKFLOW = "track-changes-sharelatex" + TARGET_URL = "${env.JENKINS_URL}blue/organizations/jenkins/${JENKINS_WORKFLOW}/detail/$BRANCH_NAME/$BUILD_NUMBER/pipeline" + GIT_API_URL = "https://api.github.com/repos/sharelatex/${GIT_PROJECT}/statuses/$GIT_COMMIT" + } + triggers { pollSCM('* * * * *') cron(cron_string) } stages { + stage('Install') { + steps { + withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { + sh "curl $GIT_API_URL \ + --data '{ \ + \"state\" : \"pending\", \ + \"target_url\": \"$TARGET_URL\", \ + \"description\": \"Your build is underway\", \ + \"context\": \"ci/jenkins\" }' \ + -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" + } + } + } + stage('Build') { steps { sh 'make build' @@ -23,15 +44,19 @@ pipeline { stage('Acceptance Tests') { steps { - withCredentials([usernamePassword(credentialsId: 'S3_DOCSTORE_TEST_AWS_KEYS', passwordVariable: 'AWS_SECRET_ACCESS_KEY', usernameVariable: 'AWS_ACCESS_KEY_ID')]) { - sh 'AWS_BUCKET="sl-doc-archive-testing" AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' - } + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' } } stage('Package and publish build') { steps { - sh 'make publish' + + withCredentials([file(credentialsId: 'gcr.io_overleaf-ops', variable: 'DOCKER_REPO_KEY_PATH')]) { + sh 'docker login -u _json_key --password-stdin https://gcr.io/overleaf-ops < ${DOCKER_REPO_KEY_PATH}' + } + sh 'DOCKER_REPO=gcr.io/overleaf-ops make publish' + sh 'docker logout https://gcr.io/overleaf-ops' + } } @@ -49,6 +74,19 @@ pipeline { post { always { sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_clean' + sh 'make clean' + } + + success { + withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { + sh "curl $GIT_API_URL \ + --data '{ \ + \"state\" : \"success\", \ + \"target_url\": \"$TARGET_URL\", \ + \"description\": \"Your build succeeded!\", \ + \"context\": \"ci/jenkins\" }' \ + -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" + } } failure { @@ -56,6 +94,15 @@ pipeline { to: "${EMAIL_ALERT_TO}", subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", body: "Build: ${BUILD_URL}") + withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { + sh "curl $GIT_API_URL \ + --data '{ \ + \"state\" : \"failure\", \ + \"target_url\": \"$TARGET_URL\", \ + \"description\": \"Your build failed\", \ + \"context\": \"ci/jenkins\" }' \ + -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" + } } } diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index b26c530f57..ab93391a28 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.9 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -11,13 +11,12 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ BRANCH_NAME=$(BRANCH_NAME) \ PROJECT_NAME=$(PROJECT_NAME) \ MOCHA_GREP=${MOCHA_GREP} \ - AWS_BUCKET=${AWS_BUCKET} \ - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ - docker-compose ${DOCKER_COMPOSE_FLAGS} + docker-compose ${DOCKER_COMPOSE_FLAGS} clean: + docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) rm -f app.js rm -rf app/js rm -rf test/unit/js @@ -37,9 +36,12 @@ test_clean: test_acceptance_pre_run: @[ ! -f test/acceptance/scripts/pre-run ] && echo "track-changes has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run build: - docker build --pull --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . + docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + --tag gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + . publish: - docker push gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + + docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt new file mode 100644 index 0000000000..2b3f71d8e9 --- /dev/null +++ b/services/track-changes/buildscript.txt @@ -0,0 +1,9 @@ +--script-version=1.1.9 +track-changes +--node-version=6.11.2 +--acceptance-creds=None +--language=coffeescript +--dependencies=mongo,redis +--docker-repos=gcr.io/overleaf-ops +--kube=false +--build-target=docker diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 18a454adfd..17c4ddd2bf 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,26 +1,25 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.9 version: "2" services: test_unit: - image: gcr.io/csh-gcdm-test/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER user: node command: npm run test:unit:_run test_acceptance: build: . - image: gcr.io/csh-gcdm-test/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER environment: + ELASTIC_SEARCH_DSN: es:9200 REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} - AWS_BUCKET: ${AWS_BUCKET} + MOCHA_GREP: ${MOCHA_GREP} depends_on: - mongo - redis @@ -32,4 +31,3 @@ services: mongo: image: mongo:3.4 - diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 31fe46d3ed..dcbc14e683 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.9 version: "2" @@ -22,18 +22,17 @@ services: - .:/app working_dir: /app environment: + ELASTIC_SEARCH_DSN: es:9200 REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} - AWS_BUCKET: ${AWS_BUCKET} MOCHA_GREP: ${MOCHA_GREP} user: node depends_on: - mongo - redis command: npm run test:acceptance + redis: image: redis diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b8781ed635..6555da700f 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -9,14 +9,15 @@ "scripts": { "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_BUCKET=$AWS_BUCKET AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", - "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", - "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", - "nodemon": "nodemon --config nodemon.json" + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && npm run compile:smoke_tests", + "nodemon": "nodemon --config nodemon.json", + "compile:smoke_tests": "[ ! -e test/smoke/coffee ] && echo 'No smoke tests to compile' || coffee -o test/smoke/js -c test/smoke/coffee" }, "dependencies": { "JSONStream": "^1.0.4", From 0a64983fb4792a404ac23853d9b84fe692def62e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 4 Oct 2018 10:34:44 +0100 Subject: [PATCH 418/549] update config with full mongo conneciton and redis details --- .../track-changes/config/settings.defaults.coffee | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 4bc5962d77..e1c58ba823 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -3,7 +3,8 @@ TMP_DIR = Path.resolve(Path.join(__dirname, "../../", "tmp")) module.exports = mongo: - url: "mongodb://#{process.env["MONGO_HOST"] or "localhost"}/sharelatex" + url: process.env['MONGO_CONNECTION_STRING'] or "mongodb://#{process.env["MONGO_HOST"] or "localhost"}/sharelatex" + internal: trackchanges: port: 3015 @@ -20,15 +21,15 @@ module.exports = redis: lock: host: process.env["REDIS_HOST"] or "localhost" - port: 6379 - pass: "" + port: process.env['REDIS_PORT'] or 6379 + password: process.env["REDIS_PASSWORD"] or "" key_schema: historyLock: ({doc_id}) -> "HistoryLock:#{doc_id}" historyIndexLock: ({project_id}) -> "HistoryIndexLock:#{project_id}" history: - port: "6379" host: process.env["REDIS_HOST"] or "localhost" - password:"" + port: process.env['REDIS_PORT'] or 6379 + password: process.env["REDIS_PASSWORD"] or "" key_schema: uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:#{doc_id}" docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:#{project_id}" From 7893ace13f02b2b81f5dd7c6ee6f9e1e160fd699 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 4 Oct 2018 10:40:34 +0100 Subject: [PATCH 419/549] add aws creds --- services/track-changes/Jenkinsfile | 4 +++- services/track-changes/Makefile | 3 +++ services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.ci.yml | 3 +++ services/track-changes/docker-compose.yml | 3 +++ services/track-changes/package.json | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 7d01259ddf..862abec028 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -44,7 +44,9 @@ pipeline { stage('Acceptance Tests') { steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + withCredentials([usernamePassword(credentialsId: 'S3_DOCSTORE_TEST_AWS_KEYS', passwordVariable: 'AWS_SECRET_ACCESS_KEY', usernameVariable: 'AWS_ACCESS_KEY_ID')]) { + sh 'AWS_BUCKET="sl-acceptance-tests" AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + } } } diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index ab93391a28..9a60fbe0de 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -11,6 +11,9 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ BRANCH_NAME=$(BRANCH_NAME) \ PROJECT_NAME=$(PROJECT_NAME) \ MOCHA_GREP=${MOCHA_GREP} \ + AWS_BUCKET=${AWS_BUCKET} \ + AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ + AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ docker-compose ${DOCKER_COMPOSE_FLAGS} diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 2b3f71d8e9..132c5d7d6b 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,7 +1,7 @@ --script-version=1.1.9 track-changes --node-version=6.11.2 ---acceptance-creds=None +--acceptance-creds=aws --language=coffeescript --dependencies=mongo,redis --docker-repos=gcr.io/overleaf-ops diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 17c4ddd2bf..d93d6270cd 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -19,6 +19,9 @@ services: REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_BUCKET: ${AWS_BUCKET} MOCHA_GREP: ${MOCHA_GREP} depends_on: - mongo diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index dcbc14e683..494b565cc7 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -26,6 +26,9 @@ services: REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_BUCKET: ${AWS_BUCKET} MOCHA_GREP: ${MOCHA_GREP} user: node depends_on: diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 6555da700f..afb3a30f00 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -9,7 +9,7 @@ "scripts": { "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", + "test:acceptance:_run": "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_BUCKET=$AWS_BUCKET AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", From 8d8319460e121df304ce87ce578bc2e1e273c74a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 4 Oct 2018 11:14:15 +0100 Subject: [PATCH 420/549] pass tmp path as env var --- services/track-changes/config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index e1c58ba823..ee5089f516 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -1,5 +1,5 @@ Path = require('path') -TMP_DIR = Path.resolve(Path.join(__dirname, "../../", "tmp")) +TMP_DIR = process.env["TMP_PATH"] or Path.resolve(Path.join(__dirname, "../../", "tmp")) module.exports = mongo: From a2ff9de0dd1186baf776ae043ebef4acb52610ff Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 9 Oct 2018 11:51:18 +0100 Subject: [PATCH 421/549] Use setting instead of hard-coding port --- services/track-changes/config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 6cc3b51c35..db2f44e7e2 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -14,7 +14,7 @@ module.exports = docstore: url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" web: - url: "http://#{process.env["WEB_HOST"] or "localhost"}:3000" + url: "http://#{process.env["WEB_HOST"] or "localhost"}:#{process.env['WEB_PORT'] or 3000}" user: "sharelatex" pass: "password" redis: From c3ec4a534e108783bfba93825455e3cab569b59e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 5 Dec 2018 16:49:15 +0000 Subject: [PATCH 422/549] update config to take DOCUMENT_UPDATER_HOST --- services/track-changes/config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index ee5089f516..cca30496e8 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -11,7 +11,7 @@ module.exports = host: process.env["LISTEN_ADDRESS"] or "localhost" apis: documentupdater: - url: "http://#{process.env["DOCUPDATER_HOST"] or "localhost"}:3003" + url: "http://#{process.env["DOCUMENT_UPDATER_HOST"] or process.env["DOCUPDATER_HOST"] or "localhost"}:3003" docstore: url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" web: From 052949316830f55efc3fc07786d40a0ff68f311a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 5 Dec 2018 16:50:20 +0000 Subject: [PATCH 423/549] bump metrics --- services/track-changes/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index afb3a30f00..b478631caa 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -30,7 +30,7 @@ "heap": "^0.2.6", "line-reader": "^0.2.4", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.7.1", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v2.0.8", "mongo-uri": "^0.1.2", "mongojs": "2.4.0", "redis": "~0.10.1", From 85216fbd3432ac8e6e1f10a67318740a3d7a75e7 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 8 Jan 2019 16:08:40 +0000 Subject: [PATCH 424/549] Update metrics to v2.0.12, logger to v1.5.9 and settings to v1.1.0, add npm-shrinkwrap --- services/track-changes/npm-shrinkwrap.json | 3095 ++++++++++++++++++++ services/track-changes/package.json | 6 +- 2 files changed, 3098 insertions(+), 3 deletions(-) create mode 100644 services/track-changes/npm-shrinkwrap.json diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json new file mode 100644 index 0000000000..facedc10b1 --- /dev/null +++ b/services/track-changes/npm-shrinkwrap.json @@ -0,0 +1,3095 @@ +{ + "name": "history-sharelatex", + "version": "0.1.4", + "dependencies": { + "@google-cloud/common": { + "version": "0.27.0", + "from": "@google-cloud/common@>=0.27.0 <0.28.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.27.0.tgz" + }, + "@google-cloud/debug-agent": { + "version": "3.0.1", + "from": "@google-cloud/debug-agent@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.0.1.tgz", + "dependencies": { + "coffeescript": { + "version": "2.3.2", + "from": "coffeescript@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.3.2.tgz" + } + } + }, + "@google-cloud/profiler": { + "version": "0.2.3", + "from": "@google-cloud/profiler@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-0.2.3.tgz", + "dependencies": { + "@google-cloud/common": { + "version": "0.26.2", + "from": "@google-cloud/common@>=0.26.0 <0.27.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz" + }, + "through2": { + "version": "3.0.0", + "from": "through2@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz" + } + } + }, + "@google-cloud/projectify": { + "version": "0.3.2", + "from": "@google-cloud/projectify@>=0.3.2 <0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.2.tgz" + }, + "@google-cloud/promisify": { + "version": "0.3.1", + "from": "@google-cloud/promisify@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz" + }, + "@google-cloud/trace-agent": { + "version": "3.5.0", + "from": "@google-cloud/trace-agent@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.5.0.tgz", + "dependencies": { + "@google-cloud/common": { + "version": "0.28.0", + "from": "@google-cloud/common@>=0.28.0 <0.29.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.28.0.tgz" + }, + "methods": { + "version": "1.1.2", + "from": "methods@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + } + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "from": "@protobufjs/aspromise@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "from": "@protobufjs/base64@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "from": "@protobufjs/codegen@>=2.0.4 <3.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "from": "@protobufjs/eventemitter@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "from": "@protobufjs/fetch@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" + }, + "@protobufjs/float": { + "version": "1.0.2", + "from": "@protobufjs/float@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "from": "@protobufjs/inquire@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" + }, + "@protobufjs/path": { + "version": "1.1.2", + "from": "@protobufjs/path@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "from": "@protobufjs/pool@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "from": "@protobufjs/utf8@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" + }, + "@sindresorhus/is": { + "version": "0.13.0", + "from": "@sindresorhus/is@>=0.13.0 <0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.13.0.tgz" + }, + "@sinonjs/commons": { + "version": "1.3.0", + "from": "@sinonjs/commons@^1.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.0.tgz" + }, + "@sinonjs/formatio": { + "version": "3.1.0", + "from": "@sinonjs/formatio@^3.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.1.0.tgz" + }, + "@sinonjs/samsam": { + "version": "3.0.2", + "from": "@sinonjs/samsam@^2 || ^3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.0.2.tgz" + }, + "@types/caseless": { + "version": "0.12.1", + "from": "@types/caseless@*", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz" + }, + "@types/console-log-level": { + "version": "1.4.0", + "from": "@types/console-log-level@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz" + }, + "@types/duplexify": { + "version": "3.6.0", + "from": "@types/duplexify@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.0.tgz" + }, + "@types/form-data": { + "version": "2.2.1", + "from": "@types/form-data@*", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz" + }, + "@types/long": { + "version": "4.0.0", + "from": "@types/long@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz" + }, + "@types/node": { + "version": "10.12.18", + "from": "@types/node@*", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz" + }, + "@types/request": { + "version": "2.48.1", + "from": "@types/request@>=2.47.0 <3.0.0", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz" + }, + "@types/semver": { + "version": "5.5.0", + "from": "@types/semver@>=5.5.0 <6.0.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz" + }, + "@types/tough-cookie": { + "version": "2.3.4", + "from": "@types/tough-cookie@*", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.4.tgz" + }, + "abbrev": { + "version": "1.1.1", + "from": "abbrev@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + }, + "acorn": { + "version": "5.7.3", + "from": "acorn@>=5.0.3 <6.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz" + }, + "agent-base": { + "version": "4.2.1", + "from": "agent-base@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz" + }, + "ajv": { + "version": "6.6.2", + "from": "ajv@>=6.5.5 <7.0.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz" + }, + "ansi-regex": { + "version": "0.2.1", + "from": "ansi-regex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" + }, + "ansi-styles": { + "version": "1.0.0", + "from": "ansi-styles@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "from": "aproba@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" + }, + "are-we-there-yet": { + "version": "1.1.5", + "from": "are-we-there-yet@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" + }, + "argparse": { + "version": "0.1.16", + "from": "argparse@>=0.1.11 <0.2.0", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "dependencies": { + "underscore.string": { + "version": "2.4.0", + "from": "underscore.string@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + } + } + }, + "array-from": { + "version": "2.1.1", + "from": "array-from@^2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz" + }, + "arrify": { + "version": "1.0.1", + "from": "arrify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "optional": true + }, + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "optional": true + }, + "assertion-error": { + "version": "1.1.0", + "from": "assertion-error@^1.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + }, + "async": { + "version": "0.2.10", + "from": "async@>=0.2.10 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "async-listener": { + "version": "0.6.10", + "from": "async-listener@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz" + }, + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + }, + "aws-sdk": { + "version": "2.384.0", + "from": "aws-sdk@>=2.102.0 <3.0.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.384.0.tgz" + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "optional": true + }, + "aws4": { + "version": "1.8.0", + "from": "aws4@>=1.8.0 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz" + }, + "axios": { + "version": "0.18.0", + "from": "axios@>=0.18.0 <0.19.0", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz" + }, + "balanced-match": { + "version": "1.0.0", + "from": "balanced-match@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" + }, + "base64-js": { + "version": "1.3.0", + "from": "base64-js@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" + }, + "bignumber.js": { + "version": "7.2.1", + "from": "bignumber.js@>=7.0.0 <8.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz" + }, + "bindings": { + "version": "1.3.1", + "from": "bindings@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz" + }, + "bintrees": { + "version": "1.0.1", + "from": "bintrees@1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" + }, + "block-stream": { + "version": "0.0.9", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" + }, + "bluebird": { + "version": "3.5.3", + "from": "bluebird@>=3.3.4 <4.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "brace-expansion": { + "version": "1.1.11", + "from": "brace-expansion@>=1.1.7 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + }, + "broadway": { + "version": "0.3.6", + "from": "broadway@>=0.3.2 <0.4.0", + "resolved": "https://registry.npmjs.org/broadway/-/broadway-0.3.6.tgz", + "dev": true, + "dependencies": { + "cliff": { + "version": "0.1.9", + "from": "cliff@0.1.9", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.9.tgz", + "dev": true + }, + "winston": { + "version": "0.8.0", + "from": "winston@0.8.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.0.tgz", + "dev": true + } + } + }, + "browser-stdout": { + "version": "1.3.0", + "from": "browser-stdout@1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "dev": true + }, + "bson": { + "version": "0.4.23", + "from": "bson@>=0.4.20 <0.5.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" + }, + "buffer": { + "version": "4.9.1", + "from": "buffer@4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz" + }, + "buffer-crc32": { + "version": "0.2.1", + "from": "buffer-crc32@0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "from": "buffer-equal-constant-time@1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" + }, + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" + }, + "builtin-modules": { + "version": "3.0.0", + "from": "builtin-modules@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz" + }, + "bunyan": { + "version": "2.0.2", + "from": "bunyan@>=2.0.2 <2.1.0", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", + "dev": true + }, + "byline": { + "version": "4.2.2", + "from": "byline@>=4.2.1 <5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz" + }, + "bytes": { + "version": "0.2.0", + "from": "bytes@0.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" + }, + "caseless": { + "version": "0.12.0", + "from": "caseless@>=0.12.0 <0.13.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" + }, + "chai": { + "version": "4.1.2", + "from": "chai@>=4.1.1 <4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "from": "chalk@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "from": "check-error@^1.0.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + }, + "cli": { + "version": "0.6.6", + "from": "cli@>=0.6.6 <0.7.0", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz" + }, + "cliff": { + "version": "0.1.10", + "from": "cliff@>=0.1.9 <0.2.0", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "dev": true, + "dependencies": { + "colors": { + "version": "1.0.3", + "from": "colors@>=1.0.3 <1.1.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "dev": true + } + } + }, + "cluster-key-slot": { + "version": "1.0.12", + "from": "cluster-key-slot@>=1.0.6 <2.0.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz" + }, + "co": { + "version": "4.6.0", + "from": "co@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + }, + "code-point-at": { + "version": "1.1.0", + "from": "code-point-at@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" + }, + "coffee-script": { + "version": "1.12.4", + "from": "coffee-script@1.12.4", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz" + }, + "colors": { + "version": "0.6.2", + "from": "colors@>=0.6.2 <0.7.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" + }, + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "optional": true + }, + "commander": { + "version": "1.2.0", + "from": "commander@1.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + }, + "connect": { + "version": "2.8.5", + "from": "connect@2.8.5", + "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz" + }, + "console-control-strings": { + "version": "1.1.0", + "from": "console-control-strings@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" + }, + "console-log-level": { + "version": "1.4.0", + "from": "console-log-level@>=1.4.0 <2.0.0", + "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.0.tgz" + }, + "continuation-local-storage": { + "version": "3.2.1", + "from": "continuation-local-storage@>=3.2.1 <4.0.0", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz" + }, + "cookie": { + "version": "0.1.0", + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + }, + "cookie-signature": { + "version": "1.0.1", + "from": "cookie-signature@1.0.1", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "optional": true + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "optional": true + }, + "cycle": { + "version": "1.0.3", + "from": "cycle@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "from": "dateformat@1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" + }, + "debug": { + "version": "4.1.1", + "from": "debug@*", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz" + }, + "deep-eql": { + "version": "3.0.1", + "from": "deep-eql@^3.0.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" + }, + "deep-equal": { + "version": "1.0.1", + "from": "deep-equal@*", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "from": "deep-extend@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + }, + "delay": { + "version": "4.1.0", + "from": "delay@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-4.1.0.tgz" + }, + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "optional": true + }, + "delegates": { + "version": "1.0.0", + "from": "delegates@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" + }, + "denque": { + "version": "1.4.0", + "from": "denque@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.0.tgz" + }, + "detect-libc": { + "version": "1.0.3", + "from": "detect-libc@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" + }, + "diff": { + "version": "1.0.7", + "from": "diff@1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" + }, + "director": { + "version": "1.2.7", + "from": "director@1.2.7", + "resolved": "https://registry.npmjs.org/director/-/director-1.2.7.tgz", + "dev": true + }, + "dtrace-provider": { + "version": "0.8.7", + "from": "dtrace-provider@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", + "dev": true, + "optional": true + }, + "duplexify": { + "version": "3.6.1", + "from": "duplexify@>=3.6.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz" + }, + "each-series": { + "version": "1.0.0", + "from": "each-series@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" + }, + "ecc-jsbn": { + "version": "0.1.2", + "from": "ecc-jsbn@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" + }, + "ecdsa-sig-formatter": { + "version": "1.0.10", + "from": "ecdsa-sig-formatter@1.0.10", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz" + }, + "emitter-listener": { + "version": "1.1.2", + "from": "emitter-listener@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz" + }, + "end-of-stream": { + "version": "1.4.1", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz" + }, + "ent": { + "version": "2.2.0", + "from": "ent@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" + }, + "es6-promise": { + "version": "4.2.5", + "from": "es6-promise@>=4.0.3 <5.0.0", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz" + }, + "es6-promisify": { + "version": "5.0.0", + "from": "es6-promisify@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + }, + "esprima": { + "version": "1.0.4", + "from": "esprima@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + }, + "event-stream": { + "version": "0.5.3", + "from": "event-stream@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-0.5.3.tgz", + "dev": true, + "dependencies": { + "optimist": { + "version": "0.2.8", + "from": "optimist@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz", + "dev": true + } + } + }, + "eventemitter2": { + "version": "0.4.14", + "from": "eventemitter2@>=0.4.13 <0.5.0", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "events": { + "version": "1.1.1", + "from": "events@1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" + }, + "exeunt": { + "version": "1.1.0", + "from": "exeunt@1.1.0", + "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", + "dev": true + }, + "exit": { + "version": "0.1.2", + "from": "exit@0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + }, + "express": { + "version": "3.3.5", + "from": "express@3.3.5", + "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz" + }, + "extend": { + "version": "3.0.2", + "from": "extend@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" + }, + "extsprintf": { + "version": "1.3.0", + "from": "extsprintf@1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + }, + "eyes": { + "version": "0.1.8", + "from": "eyes@>=0.1.8 <0.2.0", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "from": "fast-deep-equal@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "from": "fast-json-stable-stringify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz" + }, + "findit2": { + "version": "2.2.3", + "from": "findit2@>=2.2.3 <3.0.0", + "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz" + }, + "findup-sync": { + "version": "0.1.3", + "from": "findup-sync@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "flatiron": { + "version": "0.4.3", + "from": "flatiron@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/flatiron/-/flatiron-0.4.3.tgz", + "dev": true, + "dependencies": { + "optimist": { + "version": "0.6.0", + "from": "optimist@0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", + "dev": true + } + } + }, + "flexbuffer": { + "version": "0.0.6", + "from": "flexbuffer@0.0.6", + "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" + }, + "follow-redirects": { + "version": "1.6.1", + "from": "follow-redirects@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz", + "dependencies": { + "debug": { + "version": "3.1.0", + "from": "debug@3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + } + }, + "forever": { + "version": "0.14.2", + "from": "forever@>=0.14.1 <0.15.0", + "resolved": "https://registry.npmjs.org/forever/-/forever-0.14.2.tgz", + "dev": true + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "forever-monitor": { + "version": "1.5.2", + "from": "forever-monitor@>=1.5.1 <1.6.0", + "resolved": "https://registry.npmjs.org/forever-monitor/-/forever-monitor-1.5.2.tgz", + "dev": true, + "dependencies": { + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dev": true + } + } + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "optional": true, + "dependencies": { + "async": { + "version": "0.9.2", + "from": "async@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "optional": true + } + } + }, + "formatio": { + "version": "1.2.0", + "from": "formatio@1.2.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "dev": true + }, + "formidable": { + "version": "1.0.14", + "from": "formidable@1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" + }, + "fresh": { + "version": "0.2.0", + "from": "fresh@0.2.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" + }, + "fs-extra": { + "version": "0.8.1", + "from": "fs-extra@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.8.1.tgz", + "dev": true, + "dependencies": { + "ncp": { + "version": "0.4.2", + "from": "ncp@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "dev": true + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@>=2.2.0 <2.3.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + }, + "fstream": { + "version": "1.0.11", + "from": "fstream@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "from": "graceful-fs@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 >=0.0.0 <1.0.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "from": "fstream-ignore@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "dependencies": { + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } + } + }, + "gauge": { + "version": "2.7.4", + "from": "gauge@>=2.7.3 <2.8.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } + } + }, + "gaxios": { + "version": "1.0.7", + "from": "gaxios@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.0.7.tgz" + }, + "gcp-metadata": { + "version": "0.9.3", + "from": "gcp-metadata@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz" + }, + "get-func-name": { + "version": "2.0.0", + "from": "get-func-name@^2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + }, + "getobject": { + "version": "0.1.0", + "from": "getobject@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" + }, + "getpass": { + "version": "0.1.7", + "from": "getpass@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "glob": { + "version": "3.2.11", + "from": "glob@>=3.2.1 <3.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz" + }, + "google-auth-library": { + "version": "2.0.2", + "from": "google-auth-library@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", + "dependencies": { + "gcp-metadata": { + "version": "0.7.0", + "from": "gcp-metadata@>=0.7.0 <0.8.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz" + }, + "lru-cache": { + "version": "5.1.1", + "from": "lru-cache@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + } + } + }, + "google-p12-pem": { + "version": "1.0.3", + "from": "google-p12-pem@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.3.tgz" + }, + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "growl": { + "version": "1.7.0", + "from": "growl@1.7.x", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" + }, + "grunt": { + "version": "0.4.5", + "from": "grunt@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "from": "async@>=0.1.22 <0.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + }, + "coffee-script": { + "version": "1.3.3", + "from": "coffee-script@>=1.3.3 <1.4.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" + }, + "glob": { + "version": "3.1.21", + "from": "glob@>=3.1.21 <3.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz" + }, + "inherits": { + "version": "1.0.2", + "from": "inherits@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz" + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.12 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@>=2.2.8 <2.3.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + } + } + }, + "grunt-available-tasks": { + "version": "0.4.5", + "from": "grunt-available-tasks@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/grunt-available-tasks/-/grunt-available-tasks-0.4.5.tgz", + "dev": true, + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + }, + "underscore.string": { + "version": "2.3.3", + "from": "underscore.string@>=2.3.3 <2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "dev": true + } + } + }, + "grunt-bunyan": { + "version": "0.5.0", + "from": "grunt-bunyan@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/grunt-bunyan/-/grunt-bunyan-0.5.0.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "grunt-contrib-clean": { + "version": "0.5.0", + "from": "grunt-contrib-clean@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", + "dev": true, + "dependencies": { + "rimraf": { + "version": "2.2.8", + "from": "rimraf@>=2.2.1 <2.3.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "dev": true + } + } + }, + "grunt-contrib-coffee": { + "version": "0.10.1", + "from": "grunt-contrib-coffee@>=0.10.1 <0.11.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.10.1.tgz", + "dev": true, + "dependencies": { + "coffee-script": { + "version": "1.7.1", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", + "dev": true + }, + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "dev": true + } + } + }, + "grunt-execute": { + "version": "0.1.5", + "from": "grunt-execute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.1.5.tgz", + "dev": true + }, + "grunt-forever": { + "version": "0.4.7", + "from": "grunt-forever@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/grunt-forever/-/grunt-forever-0.4.7.tgz", + "dev": true + }, + "grunt-legacy-log": { + "version": "0.1.3", + "from": "grunt-legacy-log@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "from": "underscore.string@>=2.3.3 <2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-log-utils": { + "version": "0.1.1", + "from": "grunt-legacy-log-utils@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "underscore.string": { + "version": "2.3.3", + "from": "underscore.string@>=2.3.3 <2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "from": "grunt-legacy-util@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "dependencies": { + "async": { + "version": "0.1.22", + "from": "async@>=0.1.22 <0.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" + } + } + }, + "grunt-mocha-test": { + "version": "0.9.4", + "from": "grunt-mocha-test@>=0.9.3 <0.10.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.9.4.tgz", + "dev": true, + "dependencies": { + "commander": { + "version": "2.0.0", + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "dev": true + }, + "glob": { + "version": "3.2.3", + "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "dev": true + }, + "graceful-fs": { + "version": "2.0.3", + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dev": true + }, + "mocha": { + "version": "1.17.1", + "from": "mocha@>=1.17.1 <1.18.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz", + "dev": true + } + } + }, + "gtoken": { + "version": "2.3.0", + "from": "gtoken@>=2.3.0 <3.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.0.tgz", + "dependencies": { + "mime": { + "version": "2.4.0", + "from": "mime@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz" + }, + "pify": { + "version": "3.0.0", + "from": "pify@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" + } + } + }, + "har-schema": { + "version": "2.0.0", + "from": "har-schema@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" + }, + "har-validator": { + "version": "5.1.3", + "from": "har-validator@>=5.1.0 <5.2.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz" + }, + "has-ansi": { + "version": "0.1.0", + "from": "has-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz" + }, + "has-color": { + "version": "0.1.7", + "from": "has-color@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "from": "has-flag@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "from": "has-unicode@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" + }, + "hawk": { + "version": "1.0.0", + "from": "hawk@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "optional": true + }, + "he": { + "version": "1.1.1", + "from": "he@1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "dev": true + }, + "heap": { + "version": "0.2.6", + "from": "heap@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" + }, + "hex2dec": { + "version": "1.1.1", + "from": "hex2dec@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.1.tgz" + }, + "hoek": { + "version": "0.9.1", + "from": "hoek@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "hooker": { + "version": "0.2.3", + "from": "hooker@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "optional": true + }, + "https-proxy-agent": { + "version": "2.2.1", + "from": "https-proxy-agent@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "dependencies": { + "debug": { + "version": "3.2.6", + "from": "debug@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" + } + } + }, + "i": { + "version": "0.3.6", + "from": "i@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", + "dev": true + }, + "iconv-lite": { + "version": "0.2.11", + "from": "iconv-lite@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" + }, + "ieee754": { + "version": "1.1.8", + "from": "ieee754@1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" + }, + "inflight": { + "version": "1.0.6", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + }, + "ini": { + "version": "1.3.5", + "from": "ini@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" + }, + "ioredis": { + "version": "3.2.2", + "from": "ioredis@>=3.2.1 <4.0.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-3.2.2.tgz", + "dependencies": { + "debug": { + "version": "2.6.9", + "from": "debug@>=2.6.9 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + } + }, + "is": { + "version": "3.3.0", + "from": "is@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz" + }, + "is-buffer": { + "version": "1.1.6", + "from": "is-buffer@>=1.1.5 <2.0.0", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" + }, + "jade": { + "version": "0.26.3", + "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "dependencies": { + "commander": { + "version": "0.6.1", + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + }, + "mkdirp": { + "version": "0.3.0", + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + } + } + }, + "jmespath": { + "version": "0.15.0", + "from": "jmespath@0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" + }, + "js-yaml": { + "version": "2.0.5", + "from": "js-yaml@>=2.0.5 <2.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz" + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + }, + "json-bigint": { + "version": "0.3.0", + "from": "json-bigint@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz" + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" + }, + "json-schema-traverse": { + "version": "0.4.1", + "from": "json-schema-traverse@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + }, + "json-stable-stringify": { + "version": "1.0.1", + "from": "json-stable-stringify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + }, + "jsonfile": { + "version": "1.1.1", + "from": "jsonfile@~1.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" + }, + "jsonify": { + "version": "0.0.0", + "from": "jsonify@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + }, + "jsonparse": { + "version": "1.3.1", + "from": "jsonparse@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" + }, + "JSONStream": { + "version": "1.3.5", + "from": "JSONStream@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + }, + "jsprim": { + "version": "1.4.1", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "just-extend": { + "version": "4.0.2", + "from": "just-extend@^4.0.2", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz" + }, + "jwa": { + "version": "1.1.6", + "from": "jwa@>=1.1.5 <2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz" + }, + "jws": { + "version": "3.1.5", + "from": "jws@>=3.1.5 <4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz" + }, + "keypress": { + "version": "0.1.0", + "from": "keypress@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" + }, + "lazy": { + "version": "1.0.11", + "from": "lazy@>=1.0.11 <1.1.0", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "dev": true + }, + "line-reader": { + "version": "0.2.4", + "from": "line-reader@>=0.2.4 <0.3.0", + "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" + }, + "lodash": { + "version": "0.9.2", + "from": "lodash@>=0.9.2 <0.10.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" + }, + "lodash.assign": { + "version": "4.2.0", + "from": "lodash.assign@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz" + }, + "lodash.bind": { + "version": "4.2.1", + "from": "lodash.bind@>=4.2.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz" + }, + "lodash.clone": { + "version": "4.5.0", + "from": "lodash.clone@>=4.5.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "from": "lodash.clonedeep@>=4.5.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" + }, + "lodash.defaults": { + "version": "4.2.0", + "from": "lodash.defaults@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" + }, + "lodash.difference": { + "version": "4.5.0", + "from": "lodash.difference@>=4.5.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz" + }, + "lodash.flatten": { + "version": "4.4.0", + "from": "lodash.flatten@>=4.4.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" + }, + "lodash.foreach": { + "version": "4.5.0", + "from": "lodash.foreach@>=4.5.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz" + }, + "lodash.get": { + "version": "4.4.2", + "from": "lodash.get@^4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" + }, + "lodash.isempty": { + "version": "4.4.0", + "from": "lodash.isempty@>=4.4.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz" + }, + "lodash.keys": { + "version": "4.2.0", + "from": "lodash.keys@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz" + }, + "lodash.noop": { + "version": "3.0.1", + "from": "lodash.noop@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz" + }, + "lodash.partial": { + "version": "4.2.1", + "from": "lodash.partial@>=4.2.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.partial/-/lodash.partial-4.2.1.tgz" + }, + "lodash.pick": { + "version": "4.4.0", + "from": "lodash.pick@>=4.4.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz" + }, + "lodash.pickby": { + "version": "4.6.0", + "from": "lodash.pickby@>=4.6.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz" + }, + "lodash.sample": { + "version": "4.2.1", + "from": "lodash.sample@>=4.2.1 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.sample/-/lodash.sample-4.2.1.tgz" + }, + "lodash.shuffle": { + "version": "4.2.0", + "from": "lodash.shuffle@>=4.2.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz" + }, + "lodash.values": { + "version": "4.3.0", + "from": "lodash.values@>=4.3.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz" + }, + "logger-sharelatex": { + "version": "1.5.9", + "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.9", + "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#e8e1b95052f62e107336053e4a983f81cdbdf589", + "dependencies": { + "ansi-styles": { + "version": "1.1.0", + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" + }, + "bunyan": { + "version": "1.5.1", + "from": "bunyan@1.5.1", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz" + }, + "chai": { + "version": "4.2.0", + "from": "chai@latest", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz" + }, + "chalk": { + "version": "0.5.1", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz" + }, + "commander": { + "version": "2.0.0", + "from": "commander@2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + }, + "dtrace-provider": { + "version": "0.6.0", + "from": "dtrace-provider@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "optional": true + }, + "fs-extra": { + "version": "0.9.1", + "from": "fs-extra@>=0.9.1 <0.10.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + } + } + }, + "glob": { + "version": "3.2.3", + "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" + }, + "graceful-fs": { + "version": "2.0.3", + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + }, + "grunt-contrib-clean": { + "version": "0.6.0", + "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz" + }, + "grunt-contrib-coffee": { + "version": "0.11.1", + "from": "grunt-contrib-coffee@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", + "dependencies": { + "coffee-script": { + "version": "1.7.1", + "from": "coffee-script@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz" + } + } + }, + "grunt-execute": { + "version": "0.2.2", + "from": "grunt-execute@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" + }, + "grunt-mocha-test": { + "version": "0.11.0", + "from": "grunt-mocha-test@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz" + }, + "has-flag": { + "version": "3.0.0", + "from": "has-flag@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + }, + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "lolex": { + "version": "3.0.0", + "from": "lolex@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-3.0.0.tgz" + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + }, + "mocha": { + "version": "1.20.1", + "from": "mocha@>=1.20.0 <1.21.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz" + }, + "ncp": { + "version": "0.5.1", + "from": "ncp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@>=2.2.1 <2.3.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "sandboxed-module": { + "version": "2.0.3", + "from": "sandboxed-module@latest", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz" + }, + "sinon": { + "version": "7.2.2", + "from": "sinon@latest", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.2.tgz", + "dependencies": { + "diff": { + "version": "3.5.0", + "from": "diff@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" + }, + "supports-color": { + "version": "5.5.0", + "from": "supports-color@>=5.5.0 <6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + } + } + }, + "strip-ansi": { + "version": "0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" + }, + "supports-color": { + "version": "0.2.0", + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + }, + "timekeeper": { + "version": "1.0.0", + "from": "timekeeper@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" + } + } + }, + "lolex": { + "version": "2.7.5", + "from": "lolex@>=2.1.2 <3.0.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz" + }, + "long": { + "version": "4.0.0", + "from": "long@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz" + }, + "lru-cache": { + "version": "2.7.3", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + }, + "lsmod": { + "version": "1.0.0", + "from": "lsmod@1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz" + }, + "lynx": { + "version": "0.1.1", + "from": "lynx@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz" + }, + "memorystream": { + "version": "0.3.1", + "from": "memorystream@0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "dev": true + }, + "mersenne": { + "version": "0.0.4", + "from": "mersenne@>=0.0.3 <0.1.0", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz" + }, + "methods": { + "version": "0.0.1", + "from": "methods@0.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" + }, + "metrics-sharelatex": { + "version": "2.0.12", + "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v2.0.12", + "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#3ac1621ef049e2f2d88a83b3a41011333d609662", + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + }, + "underscore": { + "version": "1.6.0", + "from": "underscore@>=1.6.0 <1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + } + } + }, + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.9 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "mime-db": { + "version": "1.37.0", + "from": "mime-db@>=1.37.0 <1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz" + }, + "mime-types": { + "version": "2.1.21", + "from": "mime-types@>=2.1.19 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz" + }, + "minimatch": { + "version": "0.3.0", + "from": "minimatch@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz" + }, + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + }, + "mocha": { + "version": "4.1.0", + "from": "mocha@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "dev": true, + "dependencies": { + "commander": { + "version": "2.11.0", + "from": "commander@2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "dev": true + }, + "debug": { + "version": "3.1.0", + "from": "debug@3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "dev": true + }, + "diff": { + "version": "3.3.1", + "from": "diff@3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "dev": true + }, + "glob": { + "version": "7.1.2", + "from": "glob@7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "dev": true + }, + "growl": { + "version": "1.10.3", + "from": "growl@1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dev": true + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "dev": true + } + } + }, + "module-details-from-path": { + "version": "1.0.3", + "from": "module-details-from-path@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz" + }, + "moment": { + "version": "2.23.0", + "from": "moment@>=2.10.6 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz", + "dev": true, + "optional": true + }, + "mongo-uri": { + "version": "0.1.2", + "from": "mongo-uri@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" + }, + "mongodb": { + "version": "2.2.36", + "from": "mongodb@>=2.0.45 <3.0.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.36.tgz", + "dependencies": { + "es6-promise": { + "version": "3.2.1", + "from": "es6-promise@3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + }, + "readable-stream": { + "version": "2.2.7", + "from": "readable-stream@2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz" + }, + "string_decoder": { + "version": "1.0.3", + "from": "string_decoder@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" + } + } + }, + "mongodb-core": { + "version": "2.1.20", + "from": "mongodb-core@2.1.20", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz", + "dependencies": { + "bson": { + "version": "1.0.9", + "from": "bson@>=1.0.4 <1.1.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz" + } + } + }, + "mongojs": { + "version": "2.4.0", + "from": "mongojs@2.4.0", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.4.0.tgz" + }, + "ms": { + "version": "2.1.1", + "from": "ms@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" + }, + "mute-stream": { + "version": "0.0.8", + "from": "mute-stream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "dev": true + }, + "mv": { + "version": "2.1.1", + "from": "mv@~2", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "optional": true, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "optional": true + } + } + }, + "nan": { + "version": "2.12.1", + "from": "nan@>=2.0.8 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz" + }, + "native-promise-only": { + "version": "0.8.1", + "from": "native-promise-only@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "dev": true + }, + "nconf": { + "version": "0.6.9", + "from": "nconf@>=0.6.9 <0.7.0", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.6.9.tgz", + "dev": true, + "dependencies": { + "async": { + "version": "0.2.9", + "from": "async@0.2.9", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", + "dev": true + }, + "optimist": { + "version": "0.6.0", + "from": "optimist@0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", + "dev": true + } + } + }, + "ncp": { + "version": "2.0.0", + "from": "ncp@~2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "optional": true + }, + "nise": { + "version": "1.4.8", + "from": "nise@^1.0.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.8.tgz" + }, + "node-fetch": { + "version": "2.3.0", + "from": "node-fetch@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz" + }, + "node-forge": { + "version": "0.7.6", + "from": "node-forge@>=0.7.5 <0.8.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz" + }, + "node-pre-gyp": { + "version": "0.6.39", + "from": "node-pre-gyp@>=0.6.34 <0.7.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", + "dependencies": { + "ajv": { + "version": "4.11.8", + "from": "ajv@>=4.9.1 <5.0.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz" + }, + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" + }, + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "combined-stream": { + "version": "1.0.7", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" + }, + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.1.4", + "from": "form-data@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" + }, + "glob": { + "version": "7.1.3", + "from": "glob@>=7.1.3 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" + }, + "har-schema": { + "version": "1.0.5", + "from": "har-schema@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + }, + "har-validator": { + "version": "4.2.1", + "from": "har-validator@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz" + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" + }, + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" + }, + "nopt": { + "version": "4.0.1", + "from": "nopt@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz" + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" + }, + "performance-now": { + "version": "0.2.0", + "from": "performance-now@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" + }, + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + }, + "qs": { + "version": "6.4.0", + "from": "qs@>=6.4.0 <6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" + }, + "request": { + "version": "2.81.0", + "from": "request@2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz" + }, + "rimraf": { + "version": "2.6.3", + "from": "rimraf@>=2.6.1 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + }, + "tough-cookie": { + "version": "2.3.4", + "from": "tough-cookie@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz" + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + } + } + }, + "nopt": { + "version": "1.0.10", + "from": "nopt@>=1.0.10 <1.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz" + }, + "npmlog": { + "version": "4.1.2", + "from": "npmlog@>=4.0.2 <5.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" + }, + "nssocket": { + "version": "0.5.3", + "from": "nssocket@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.5.3.tgz", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "from": "object-assign@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "once": { + "version": "1.4.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "os-tmpdir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + }, + "osenv": { + "version": "0.1.5", + "from": "osenv@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" + }, + "p-limit": { + "version": "2.1.0", + "from": "p-limit@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz" + }, + "p-try": { + "version": "2.0.0", + "from": "p-try@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz" + }, + "parse-duration": { + "version": "0.1.1", + "from": "parse-duration@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.1.tgz" + }, + "parse-mongo-url": { + "version": "1.1.1", + "from": "parse-mongo-url@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" + }, + "parse-ms": { + "version": "2.0.0", + "from": "parse-ms@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + }, + "path-parse": { + "version": "1.0.6", + "from": "path-parse@>=1.0.6 <2.0.0", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" + }, + "path-to-regexp": { + "version": "1.7.0", + "from": "path-to-regexp@^1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "dependencies": { + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + } + } + }, + "pathval": { + "version": "1.1.0", + "from": "pathval@^1.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz" + }, + "pause": { + "version": "0.0.1", + "from": "pause@0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + }, + "performance-now": { + "version": "2.1.0", + "from": "performance-now@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" + }, + "pify": { + "version": "4.0.1", + "from": "pify@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + }, + "pkginfo": { + "version": "0.3.1", + "from": "pkginfo@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "dev": true + }, + "pretty-ms": { + "version": "4.0.0", + "from": "pretty-ms@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz" + }, + "process-nextick-args": { + "version": "2.0.0", + "from": "process-nextick-args@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" + }, + "prom-client": { + "version": "11.2.1", + "from": "prom-client@>=11.1.3 <12.0.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.2.1.tgz" + }, + "prompt": { + "version": "0.2.14", + "from": "prompt@0.2.14", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", + "dev": true + }, + "protobufjs": { + "version": "6.8.8", + "from": "protobufjs@>=6.8.6 <6.9.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz" + }, + "ps-tree": { + "version": "0.0.3", + "from": "ps-tree@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-0.0.3.tgz", + "dev": true + }, + "psl": { + "version": "1.1.31", + "from": "psl@>=1.1.28 <2.0.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz" + }, + "punycode": { + "version": "1.3.2", + "from": "punycode@1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + }, + "q": { + "version": "0.9.2", + "from": "q@0.9.2", + "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" + }, + "qs": { + "version": "0.6.5", + "from": "qs@0.6.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" + }, + "querystring": { + "version": "0.2.0", + "from": "querystring@0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + }, + "range-parser": { + "version": "0.0.4", + "from": "range-parser@0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" + }, + "raven": { + "version": "1.2.1", + "from": "raven@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "dependencies": { + "cookie": { + "version": "0.3.1", + "from": "cookie@0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" + }, + "uuid": { + "version": "3.0.0", + "from": "uuid@3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + } + } + }, + "rc": { + "version": "1.2.8", + "from": "rc@>=1.1.7 <2.0.0", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "dependencies": { + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + } + } + }, + "read": { + "version": "1.0.7", + "from": "read@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" + }, + "redis": { + "version": "0.10.3", + "from": "redis@>=0.10.1 <0.11.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" + }, + "redis-commands": { + "version": "1.4.0", + "from": "redis-commands@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz" + }, + "redis-parser": { + "version": "2.6.0", + "from": "redis-parser@>=2.4.0 <3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz" + }, + "redis-sentinel": { + "version": "0.1.1", + "from": "redis-sentinel@0.1.1", + "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", + "dependencies": { + "redis": { + "version": "0.11.0", + "from": "redis@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" + } + } + }, + "redis-sharelatex": { + "version": "1.0.4", + "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.4", + "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#ca4e906559c1405d132e8edd7db763d64a57be62", + "dependencies": { + "async": { + "version": "2.6.1", + "from": "async@>=2.5.0 <3.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz" + }, + "coffee-script": { + "version": "1.8.0", + "from": "coffee-script@1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz" + }, + "lodash": { + "version": "4.17.11", + "from": "lodash@>=4.17.10 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz" + }, + "redis": { + "version": "0.12.1", + "from": "redis@0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + } + } + }, + "request": { + "version": "2.33.0", + "from": "request@>=2.33.0 <2.34.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", + "dependencies": { + "node-uuid": { + "version": "1.4.8", + "from": "node-uuid@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" + } + } + }, + "requestretry": { + "version": "1.13.0", + "from": "requestretry@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "aws-sign2": { + "version": "0.7.0", + "from": "aws-sign2@>=0.7.0 <0.8.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" + }, + "combined-stream": { + "version": "1.0.7", + "from": "combined-stream@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" + }, + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" + }, + "form-data": { + "version": "2.3.3", + "from": "form-data@>=2.3.2 <2.4.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" + }, + "http-signature": { + "version": "1.2.0", + "from": "http-signature@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" + }, + "lodash": { + "version": "4.17.11", + "from": "lodash@>=4.15.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz" + }, + "oauth-sign": { + "version": "0.9.0", + "from": "oauth-sign@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" + }, + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + }, + "qs": { + "version": "6.5.2", + "from": "qs@>=6.5.2 <6.6.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" + }, + "request": { + "version": "2.88.0", + "from": "request@>=2.74.0 <3.0.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz" + }, + "tough-cookie": { + "version": "2.4.3", + "from": "tough-cookie@>=2.4.3 <2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz" + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.3.2", + "from": "uuid@>=3.3.2 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" + } + } + }, + "require_optional": { + "version": "1.0.1", + "from": "require_optional@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz" + }, + "require-in-the-middle": { + "version": "3.1.0", + "from": "require-in-the-middle@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-3.1.0.tgz" + }, + "require-like": { + "version": "0.1.2", + "from": "require-like@0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" + }, + "resolve": { + "version": "1.9.0", + "from": "resolve@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz" + }, + "resolve-from": { + "version": "2.0.0", + "from": "resolve-from@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" + }, + "retry-axios": { + "version": "0.3.2", + "from": "retry-axios@0.3.2", + "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz" + }, + "retry-request": { + "version": "4.0.0", + "from": "retry-request@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz" + }, + "revalidator": { + "version": "0.1.8", + "from": "revalidator@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", + "dev": true + }, + "rimraf": { + "version": "2.4.5", + "from": "rimraf@>=2.4.0 <2.5.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "dependencies": { + "glob": { + "version": "6.0.4", + "from": "glob@>=6.0.1 <7.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } + } + }, + "s3-streams": { + "version": "0.3.0", + "from": "s3-streams@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", + "dependencies": { + "bluebird": { + "version": "2.11.0", + "from": "bluebird@>=2.9.27 <3.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz" + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.9.3 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "from": "safe-buffer@>=5.1.1 <5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + }, + "safe-json-stringify": { + "version": "1.2.0", + "from": "safe-json-stringify@~1", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "from": "safer-buffer@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + }, + "samsam": { + "version": "1.3.0", + "from": "samsam@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "dev": true + }, + "sandboxed-module": { + "version": "0.3.0", + "from": "sandboxed-module@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", + "dev": true, + "dependencies": { + "stack-trace": { + "version": "0.0.6", + "from": "stack-trace@0.0.6", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "dev": true + } + } + }, + "sax": { + "version": "1.2.1", + "from": "sax@1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz" + }, + "semver": { + "version": "5.6.0", + "from": "semver@>=5.5.0 <6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz" + }, + "send": { + "version": "0.1.4", + "from": "send@0.1.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz" + }, + "set-blocking": { + "version": "2.0.0", + "from": "set-blocking@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + }, + "settings-sharelatex": { + "version": "1.1.0", + "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.1.0", + "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#93f63d029b52fef8825c3a401b2b6a7ba29b4750", + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "from": "coffee-script@1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + } + } + }, + "shimmer": { + "version": "1.2.0", + "from": "shimmer@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + }, + "signal-exit": { + "version": "3.0.2", + "from": "signal-exit@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" + }, + "sinon": { + "version": "3.2.1", + "from": "sinon@>=3.2.1 <3.3.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", + "dev": true, + "dependencies": { + "diff": { + "version": "3.5.0", + "from": "diff@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "dev": true + } + } + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "optional": true + }, + "source-map": { + "version": "0.6.1", + "from": "source-map@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + }, + "split": { + "version": "1.0.1", + "from": "split@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz" + }, + "sshpk": { + "version": "1.16.0", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "dependencies": { + "asn1": { + "version": "0.2.4", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" + }, + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "stack-trace": { + "version": "0.0.9", + "from": "stack-trace@0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" + }, + "statsd-parser": { + "version": "0.0.4", + "from": "statsd-parser@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + }, + "stream-shift": { + "version": "1.0.0", + "from": "stream-shift@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz" + }, + "string_decoder": { + "version": "1.1.1", + "from": "string_decoder@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + }, + "string-width": { + "version": "1.0.2", + "from": "string-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } + } + }, + "stringstream": { + "version": "0.0.6", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz" + }, + "strip-ansi": { + "version": "0.1.1", + "from": "strip-ansi@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "from": "strip-json-comments@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + }, + "supports-color": { + "version": "4.4.0", + "from": "supports-color@4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "dev": true + }, + "symbol-observable": { + "version": "1.2.0", + "from": "symbol-observable@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" + }, + "tar": { + "version": "2.2.1", + "from": "tar@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz" + }, + "tar-pack": { + "version": "3.4.1", + "from": "tar-pack@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", + "dependencies": { + "debug": { + "version": "2.6.9", + "from": "debug@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + }, + "glob": { + "version": "7.1.3", + "from": "glob@>=7.1.3 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + }, + "rimraf": { + "version": "2.6.3", + "from": "rimraf@>=2.5.1 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" + } + } + }, + "tdigest": { + "version": "0.1.1", + "from": "tdigest@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz" + }, + "teeny-request": { + "version": "3.11.3", + "from": "teeny-request@>=3.11.1 <4.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "dependencies": { + "uuid": { + "version": "3.3.2", + "from": "uuid@>=3.3.2 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" + } + } + }, + "text-encoding": { + "version": "0.6.4", + "from": "text-encoding@0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.2.7 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + }, + "through2": { + "version": "2.0.5", + "from": "through2@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" + }, + "thunky": { + "version": "0.1.0", + "from": "thunky@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" + }, + "timekeeper": { + "version": "0.0.4", + "from": "timekeeper@0.0.4", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", + "dev": true + }, + "timespan": { + "version": "2.3.0", + "from": "timespan@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", + "dev": true + }, + "to-mongodb-core": { + "version": "2.0.0", + "from": "to-mongodb-core@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" + }, + "tough-cookie": { + "version": "2.5.0", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "optional": true, + "dependencies": { + "punycode": { + "version": "2.1.1", + "from": "punycode@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "optional": true + } + } + }, + "tunnel-agent": { + "version": "0.3.0", + "from": "tunnel-agent@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@>=0.14.0 <0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + }, + "type-detect": { + "version": "4.0.8", + "from": "type-detect@^4.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + }, + "uid-number": { + "version": "0.0.6", + "from": "uid-number@>=0.0.6 <0.0.7", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + }, + "uid2": { + "version": "0.0.2", + "from": "uid2@0.0.2", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" + }, + "underscore": { + "version": "1.7.0", + "from": "underscore@>=1.7.0 <1.8.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "underscore.string": { + "version": "2.2.1", + "from": "underscore.string@>=2.2.1 <2.3.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" + }, + "uri-js": { + "version": "4.2.2", + "from": "uri-js@>=4.2.2 <5.0.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "dependencies": { + "punycode": { + "version": "2.1.1", + "from": "punycode@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" + } + } + }, + "url": { + "version": "0.10.3", + "from": "url@0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz" + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, + "utile": { + "version": "0.2.1", + "from": "utile@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", + "dev": true, + "dependencies": { + "ncp": { + "version": "0.4.2", + "from": "ncp@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "dev": true + } + } + }, + "uuid": { + "version": "3.1.0", + "from": "uuid@3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + }, + "v8-profiler": { + "version": "5.7.0", + "from": "v8-profiler@>=5.6.5 <6.0.0", + "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz" + }, + "verror": { + "version": "1.10.0", + "from": "verror@1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + } + }, + "watch": { + "version": "0.13.0", + "from": "watch@>=0.13.0 <0.14.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.13.0.tgz", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "dev": true + } + } + }, + "when": { + "version": "3.7.8", + "from": "when@>=3.7.7 <4.0.0", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" + }, + "which": { + "version": "1.0.9", + "from": "which@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + }, + "wide-align": { + "version": "1.1.3", + "from": "wide-align@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" + }, + "winston": { + "version": "0.8.3", + "from": "winston@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + }, + "xml2js": { + "version": "0.4.19", + "from": "xml2js@0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz" + }, + "xmlbuilder": { + "version": "9.0.7", + "from": "xmlbuilder@>=9.0.1 <9.1.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz" + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.1 <4.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" + }, + "yallist": { + "version": "3.0.3", + "from": "yallist@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz" + } + } +} diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b478631caa..6ceb7a5704 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -29,8 +29,8 @@ "express": "3.3.5", "heap": "^0.2.6", "line-reader": "^0.2.4", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.6", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v2.0.8", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.9", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v2.0.12", "mongo-uri": "^0.1.2", "mongojs": "2.4.0", "redis": "~0.10.1", @@ -38,7 +38,7 @@ "request": "~2.33.0", "requestretry": "^1.12.0", "s3-streams": "^0.3.0", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.1.0", "underscore": "~1.7.0", "v8-profiler": "^5.6.5" }, From 65e1aba2659ce4f8cf69cf1803ee4c7b0119597c Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 8 Jan 2019 16:11:56 +0000 Subject: [PATCH 425/549] Update app.coffee to work with Metrics v2 --- services/track-changes/app.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 513cdb7f50..846c7e8db1 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -1,3 +1,5 @@ +Metrics = require "metrics-sharelatex" +Metrics.initialize("track-changes") Settings = require "settings-sharelatex" logger = require "logger-sharelatex" TrackChangesLogger = logger.initialize("track-changes").logger @@ -23,8 +25,7 @@ TrackChangesLogger.addSerializers { } Path = require "path" -Metrics = require "metrics-sharelatex" -Metrics.initialize("track-changes") + Metrics.memory.monitor(logger) child_process = require "child_process" @@ -35,6 +36,8 @@ app = express() app.use Metrics.http.monitor(logger) +Metrics.injectMetricsRoute(app) + app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff From 1c701a2d3d2c6a70810f288465a534c072e32cbe Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 8 Jan 2019 16:15:04 +0000 Subject: [PATCH 426/549] Bump buildscript to v1.1.10 --- services/track-changes/Dockerfile | 2 +- services/track-changes/Makefile | 2 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.ci.yml | 2 +- services/track-changes/docker-compose.yml | 2 +- services/track-changes/package.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 4fe121ef4c..187f37be2c 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -19,4 +19,4 @@ COPY --from=app /app /app WORKDIR /app USER node -CMD ["node","app.js"] +CMD ["node", "--expose-gc", "app.js"] diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 9a60fbe0de..5fd166ab47 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.9 +# Version: 1.1.10 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 132c5d7d6b..adb6c282df 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,4 +1,4 @@ ---script-version=1.1.9 +--script-version=1.1.10 track-changes --node-version=6.11.2 --acceptance-creds=aws diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index d93d6270cd..4e17f15673 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.9 +# Version: 1.1.10 version: "2" diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 494b565cc7..a556a008fa 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.9 +# Version: 1.1.10 version: "2" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 6ceb7a5704..6024296ce3 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/sharelatex/track-changes-sharelatex.git" }, "scripts": { - "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", + "compile:app": "([ -e app/coffee ] && coffee -m $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee -m $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_BUCKET=$AWS_BUCKET AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", From fe4c75a2ab71b289206f7787c0f19adab3be208b Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 8 Jan 2019 16:39:46 +0000 Subject: [PATCH 427/549] Remove grunt --- services/track-changes/Gruntfile.coffee | 117 ----- services/track-changes/npm-shrinkwrap.json | 500 +++------------------ services/track-changes/package.json | 18 +- 3 files changed, 67 insertions(+), 568 deletions(-) delete mode 100644 services/track-changes/Gruntfile.coffee diff --git a/services/track-changes/Gruntfile.coffee b/services/track-changes/Gruntfile.coffee deleted file mode 100644 index ccd1654e2d..0000000000 --- a/services/track-changes/Gruntfile.coffee +++ /dev/null @@ -1,117 +0,0 @@ -module.exports = (grunt) -> - grunt.loadNpmTasks 'grunt-contrib-coffee' - grunt.loadNpmTasks 'grunt-contrib-clean' - grunt.loadNpmTasks 'grunt-mocha-test' - grunt.loadNpmTasks 'grunt-available-tasks' - grunt.loadNpmTasks 'grunt-execute' - grunt.loadNpmTasks 'grunt-bunyan' - grunt.loadNpmTasks 'grunt-forever' - - grunt.initConfig - forever: - app: - options: - index: "app.js" - - execute: - app: - src: "app.js" - - bunyan: - strict: false - - coffee: - app_dir: - expand: true, - flatten: false, - cwd: 'app/coffee', - src: ['**/*.coffee'], - dest: 'app/js/', - ext: '.js' - - app: - src: 'app.coffee' - dest: 'app.js' - - acceptance_tests: - expand: true, - flatten: false, - cwd: 'test/acceptance/coffee', - src: ['**/*.coffee'], - dest: 'test/acceptance/js/', - ext: '.js' - - unit_tests: - expand: true, - flatten: false, - cwd: 'test/unit/coffee', - src: ['**/*.coffee'], - dest: 'test/unit/js/', - ext: '.js' - - clean: - app: ["app/js"] - acceptance_tests: ["test/acceptance/js"] - - mochaTest: - unit: - src: ["test/unit/js/#{grunt.option("feature") or "**"}/*.js"] - options: - reporter: grunt.option('reporter') or 'spec' - grep: grunt.option("grep") - acceptance: - src: ["test/acceptance/js/**/*.js"] - options: - reporter: grunt.option('reporter') or 'spec' - grep: grunt.option("grep") - timeout: 10000 - - availabletasks: - tasks: - options: - filter: 'exclude', - tasks: [ - 'coffee' - 'clean' - 'mochaTest' - 'availabletasks' - 'execute' - 'bunyan' - ] - groups: - "Compile tasks": [ - "compile:server" - "compile:tests" - "compile" - "compile:unit_tests" - "compile:acceptance_tests" - "install" - ] - "Test tasks": [ - "test:unit" - "test:acceptance" - ] - "Run tasks": [ - "run" - "default" - ] - "Misc": [ - "help" - ] - - grunt.registerTask 'help', 'Display this help list', 'availabletasks' - - grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir'] - grunt.registerTask 'compile:unit_tests', 'Compile the unit tests', ['coffee:unit_tests'] - grunt.registerTask 'compile:acceptance_tests', 'Compile the acceptance tests', ['clean:acceptance_tests', 'coffee:acceptance_tests'] - grunt.registerTask 'compile:tests', 'Compile all the tests', ['compile:acceptance_tests', 'compile:unit_tests'] - grunt.registerTask 'compile', 'Compiles everything need to run track-changes-sharelatex', ['compile:server'] - - grunt.registerTask 'install', "Compile everything when installing as an npm module", ['compile'] - - grunt.registerTask 'test:unit', 'Run the unit tests (use --grep= for individual tests)', ['compile:server', 'compile:unit_tests', 'mochaTest:unit'] - grunt.registerTask 'test:acceptance', 'Run the acceptance tests (use --grep= for individual tests)', ['compile:acceptance_tests', 'mochaTest:acceptance'] - - grunt.registerTask 'run', "Compile and run the track-changes-sharelatex server", ['compile', 'bunyan', 'execute'] - grunt.registerTask 'default', 'run' - diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index facedc10b1..fe97eb3c94 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -204,10 +204,9 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" }, "ansi-styles": { - "version": "1.0.0", - "from": "ansi-styles@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", - "dev": true + "version": "1.1.0", + "from": "ansi-styles@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" }, "aproba": { "version": "1.2.0", @@ -344,26 +343,6 @@ "from": "brace-expansion@>=1.1.7 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" }, - "broadway": { - "version": "0.3.6", - "from": "broadway@>=0.3.2 <0.4.0", - "resolved": "https://registry.npmjs.org/broadway/-/broadway-0.3.6.tgz", - "dev": true, - "dependencies": { - "cliff": { - "version": "0.1.9", - "from": "cliff@0.1.9", - "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.9.tgz", - "dev": true - }, - "winston": { - "version": "0.8.0", - "from": "winston@0.8.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.0.tgz", - "dev": true - } - } - }, "browser-stdout": { "version": "1.3.0", "from": "browser-stdout@1.3.0", @@ -428,10 +407,16 @@ "dev": true }, "chalk": { - "version": "0.4.0", - "from": "chalk@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "dev": true + "version": "0.5.1", + "from": "chalk@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "dependencies": { + "supports-color": { + "version": "0.2.0", + "from": "supports-color@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + } + } }, "check-error": { "version": "1.0.2", @@ -443,20 +428,6 @@ "from": "cli@>=0.6.6 <0.7.0", "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz" }, - "cliff": { - "version": "0.1.10", - "from": "cliff@>=0.1.9 <0.2.0", - "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", - "dev": true, - "dependencies": { - "colors": { - "version": "1.0.3", - "from": "colors@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "dev": true - } - } - }, "cluster-key-slot": { "version": "1.0.12", "from": "cluster-key-slot@>=1.0.6 <2.0.0", @@ -545,12 +516,6 @@ "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", "optional": true }, - "cycle": { - "version": "1.0.3", - "from": "cycle@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "dev": true - }, "dashdash": { "version": "1.14.1", "from": "dashdash@>=1.12.0 <2.0.0", @@ -578,12 +543,6 @@ "from": "deep-eql@^3.0.0", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" }, - "deep-equal": { - "version": "1.0.1", - "from": "deep-equal@*", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "dev": true - }, "deep-extend": { "version": "0.6.0", "from": "deep-extend@>=0.6.0 <0.7.0", @@ -620,12 +579,6 @@ "from": "diff@1.0.7", "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" }, - "director": { - "version": "1.2.7", - "from": "director@1.2.7", - "resolved": "https://registry.npmjs.org/director/-/director-1.2.7.tgz", - "dev": true - }, "dtrace-provider": { "version": "0.8.7", "from": "dtrace-provider@>=0.8.0 <0.9.0", @@ -688,20 +641,6 @@ "from": "esprima@>=1.0.2 <1.1.0", "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" }, - "event-stream": { - "version": "0.5.3", - "from": "event-stream@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-0.5.3.tgz", - "dev": true, - "dependencies": { - "optimist": { - "version": "0.2.8", - "from": "optimist@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.2.8.tgz", - "dev": true - } - } - }, "eventemitter2": { "version": "0.4.14", "from": "eventemitter2@>=0.4.13 <0.5.0", @@ -738,12 +677,6 @@ "from": "extsprintf@1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" }, - "eyes": { - "version": "0.1.8", - "from": "eyes@>=0.1.8 <0.2.0", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "dev": true - }, "fast-deep-equal": { "version": "2.0.1", "from": "fast-deep-equal@>=2.0.1 <3.0.0", @@ -771,20 +704,6 @@ } } }, - "flatiron": { - "version": "0.4.3", - "from": "flatiron@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/flatiron/-/flatiron-0.4.3.tgz", - "dev": true, - "dependencies": { - "optimist": { - "version": "0.6.0", - "from": "optimist@0.6.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", - "dev": true - } - } - }, "flexbuffer": { "version": "0.0.6", "from": "flexbuffer@0.0.6", @@ -807,31 +726,11 @@ } } }, - "forever": { - "version": "0.14.2", - "from": "forever@>=0.14.1 <0.15.0", - "resolved": "https://registry.npmjs.org/forever/-/forever-0.14.2.tgz", - "dev": true - }, "forever-agent": { "version": "0.5.2", "from": "forever-agent@>=0.5.0 <0.6.0", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" }, - "forever-monitor": { - "version": "1.5.2", - "from": "forever-monitor@>=1.5.1 <1.6.0", - "resolved": "https://registry.npmjs.org/forever-monitor/-/forever-monitor-1.5.2.tgz", - "dev": true, - "dependencies": { - "minimatch": { - "version": "1.0.0", - "from": "minimatch@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", - "dev": true - } - } - }, "form-data": { "version": "0.1.4", "from": "form-data@>=0.1.0 <0.2.0", @@ -863,22 +762,19 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" }, "fs-extra": { - "version": "0.8.1", - "from": "fs-extra@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.8.1.tgz", - "dev": true, + "version": "0.9.1", + "from": "fs-extra@>=0.9.1 <0.10.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", "dependencies": { - "ncp": { - "version": "0.4.2", - "from": "ncp@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "dev": true + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" }, - "rimraf": { - "version": "2.2.8", - "from": "rimraf@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "dev": true + "ncp": { + "version": "0.5.1", + "from": "ncp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" } } }, @@ -999,7 +895,7 @@ }, "growl": { "version": "1.7.0", - "from": "growl@1.7.x", + "from": "growl@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" }, "grunt": { @@ -1039,26 +935,6 @@ } } }, - "grunt-available-tasks": { - "version": "0.4.5", - "from": "grunt-available-tasks@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/grunt-available-tasks/-/grunt-available-tasks-0.4.5.tgz", - "dev": true, - "dependencies": { - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "dev": true - }, - "underscore.string": { - "version": "2.3.3", - "from": "underscore.string@>=2.3.3 <2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "dev": true - } - } - }, "grunt-bunyan": { "version": "0.5.0", "from": "grunt-bunyan@>=0.5.0 <0.6.0", @@ -1072,50 +948,38 @@ } }, "grunt-contrib-clean": { - "version": "0.5.0", - "from": "grunt-contrib-clean@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", - "dev": true, + "version": "0.6.0", + "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", "dependencies": { "rimraf": { "version": "2.2.8", - "from": "rimraf@>=2.2.1 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "dev": true + "from": "rimraf@~2.2.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" } } }, "grunt-contrib-coffee": { - "version": "0.10.1", - "from": "grunt-contrib-coffee@>=0.10.1 <0.11.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.10.1.tgz", - "dev": true, + "version": "0.11.1", + "from": "grunt-contrib-coffee@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", "dependencies": { "coffee-script": { "version": "1.7.1", "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz" }, "lodash": { "version": "2.4.2", "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" } } }, "grunt-execute": { - "version": "0.1.5", - "from": "grunt-execute@>=0.1.5 <0.2.0", - "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.1.5.tgz", - "dev": true - }, - "grunt-forever": { - "version": "0.4.7", - "from": "grunt-forever@>=0.4.2 <0.5.0", - "resolved": "https://registry.npmjs.org/grunt-forever/-/grunt-forever-0.4.7.tgz", - "dev": true + "version": "0.2.2", + "from": "grunt-execute@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" }, "grunt-legacy-log": { "version": "0.1.3", @@ -1158,46 +1022,40 @@ "dependencies": { "async": { "version": "0.1.22", - "from": "async@>=0.1.22 <0.2.0", + "from": "async@~0.1.22", "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" } } }, "grunt-mocha-test": { - "version": "0.9.4", - "from": "grunt-mocha-test@>=0.9.3 <0.10.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.9.4.tgz", - "dev": true, + "version": "0.11.0", + "from": "grunt-mocha-test@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz", "dependencies": { "commander": { "version": "2.0.0", "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" }, "graceful-fs": { "version": "2.0.3", "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "minimatch": { "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "dev": true + "from": "minimatch@~0.2.11", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" }, "mocha": { - "version": "1.17.1", - "from": "mocha@>=1.17.1 <1.18.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz", - "dev": true + "version": "1.20.1", + "from": "mocha@>=1.20.0 <1.21.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz" } } }, @@ -1233,12 +1091,6 @@ "from": "has-ansi@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz" }, - "has-color": { - "version": "0.1.7", - "from": "has-color@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "dev": true - }, "has-flag": { "version": "2.0.0", "from": "has-flag@>=2.0.0 <3.0.0", @@ -1300,12 +1152,6 @@ } } }, - "i": { - "version": "0.3.6", - "from": "i@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", - "dev": true - }, "iconv-lite": { "version": "0.2.11", "from": "iconv-lite@>=0.2.11 <0.3.0", @@ -1437,7 +1283,7 @@ }, "jsonfile": { "version": "1.1.1", - "from": "jsonfile@~1.1.0", + "from": "jsonfile@>=1.1.0 <1.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" }, "jsonify": { @@ -1487,12 +1333,6 @@ "from": "keypress@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" }, - "lazy": { - "version": "1.0.11", - "from": "lazy@>=1.0.11 <1.1.0", - "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", - "dev": true - }, "line-reader": { "version": "0.2.4", "from": "line-reader@>=0.2.4 <0.3.0", @@ -1598,11 +1438,6 @@ "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.9", "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#e8e1b95052f62e107336053e4a983f81cdbdf589", "dependencies": { - "ansi-styles": { - "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" - }, "bunyan": { "version": "1.5.1", "from": "bunyan@1.5.1", @@ -1613,15 +1448,10 @@ "from": "chai@latest", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz" }, - "chalk": { - "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz" - }, - "commander": { - "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" + "diff": { + "version": "3.5.0", + "from": "diff@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" }, "dtrace-provider": { "version": "0.6.0", @@ -1629,90 +1459,16 @@ "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "optional": true }, - "fs-extra": { - "version": "0.9.1", - "from": "fs-extra@>=0.9.1 <0.10.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - } - } - }, - "glob": { - "version": "3.2.3", - "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" - }, - "graceful-fs": { - "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" - }, - "grunt-contrib-clean": { - "version": "0.6.0", - "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz" - }, - "grunt-contrib-coffee": { - "version": "0.11.1", - "from": "grunt-contrib-coffee@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", - "dependencies": { - "coffee-script": { - "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz" - } - } - }, - "grunt-execute": { - "version": "0.2.2", - "from": "grunt-execute@>=0.2.2 <0.3.0", - "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" - }, - "grunt-mocha-test": { - "version": "0.11.0", - "from": "grunt-mocha-test@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz" - }, "has-flag": { "version": "3.0.0", "from": "has-flag@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" }, - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - }, "lolex": { "version": "3.0.0", "from": "lolex@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-3.0.0.tgz" }, - "minimatch": { - "version": "0.2.14", - "from": "minimatch@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" - }, - "mocha": { - "version": "1.20.1", - "from": "mocha@>=1.20.0 <1.21.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz" - }, - "ncp": { - "version": "0.5.1", - "from": "ncp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" - }, - "rimraf": { - "version": "2.2.8", - "from": "rimraf@>=2.2.1 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" - }, "sandboxed-module": { "version": "2.0.3", "from": "sandboxed-module@latest", @@ -1721,29 +1477,12 @@ "sinon": { "version": "7.2.2", "from": "sinon@latest", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.2.tgz", - "dependencies": { - "diff": { - "version": "3.5.0", - "from": "diff@>=3.5.0 <4.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" - }, - "supports-color": { - "version": "5.5.0", - "from": "supports-color@>=5.5.0 <6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - } - } - }, - "strip-ansi": { - "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" + "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.2.tgz" }, "supports-color": { - "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" + "version": "5.5.0", + "from": "supports-color@>=5.5.0 <6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" }, "timekeeper": { "version": "1.0.0", @@ -1962,12 +1701,6 @@ "from": "ms@>=2.1.1 <3.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" }, - "mute-stream": { - "version": "0.0.8", - "from": "mute-stream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "dev": true - }, "mv": { "version": "2.1.1", "from": "mv@~2", @@ -1993,26 +1726,6 @@ "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", "dev": true }, - "nconf": { - "version": "0.6.9", - "from": "nconf@>=0.6.9 <0.7.0", - "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.6.9.tgz", - "dev": true, - "dependencies": { - "async": { - "version": "0.2.9", - "from": "async@0.2.9", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", - "dev": true - }, - "optimist": { - "version": "0.6.0", - "from": "optimist@0.6.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.0.tgz", - "dev": true - } - } - }, "ncp": { "version": "2.0.0", "from": "ncp@~2.0.0", @@ -2186,12 +1899,6 @@ "from": "npmlog@>=4.0.2 <5.0.0", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" }, - "nssocket": { - "version": "0.5.3", - "from": "nssocket@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.5.3.tgz", - "dev": true - }, "number-is-nan": { "version": "1.0.1", "from": "number-is-nan@>=1.0.0 <2.0.0", @@ -2213,12 +1920,6 @@ "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" }, - "optimist": { - "version": "0.6.1", - "from": "optimist@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "dev": true - }, "os-homedir": { "version": "1.0.2", "from": "os-homedir@>=1.0.0 <2.0.0", @@ -2301,12 +2002,6 @@ "from": "pify@>=4.0.1 <5.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" }, - "pkginfo": { - "version": "0.3.1", - "from": "pkginfo@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "dev": true - }, "pretty-ms": { "version": "4.0.0", "from": "pretty-ms@>=4.0.0 <5.0.0", @@ -2322,23 +2017,11 @@ "from": "prom-client@>=11.1.3 <12.0.0", "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.2.1.tgz" }, - "prompt": { - "version": "0.2.14", - "from": "prompt@0.2.14", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", - "dev": true - }, "protobufjs": { "version": "6.8.8", "from": "protobufjs@>=6.8.6 <6.9.0", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz" }, - "ps-tree": { - "version": "0.0.3", - "from": "ps-tree@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-0.0.3.tgz", - "dev": true - }, "psl": { "version": "1.1.31", "from": "psl@>=1.1.28 <2.0.0", @@ -2398,12 +2081,6 @@ } } }, - "read": { - "version": "1.0.7", - "from": "read@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "dev": true - }, "readable-stream": { "version": "2.3.6", "from": "readable-stream@>=2.0.0 <3.0.0", @@ -2592,12 +2269,6 @@ "from": "retry-request@>=4.0.0 <5.0.0", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz" }, - "revalidator": { - "version": "0.1.8", - "from": "revalidator@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", - "dev": true - }, "rimraf": { "version": "2.4.5", "from": "rimraf@>=2.4.0 <2.5.0", @@ -2805,10 +2476,9 @@ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz" }, "strip-ansi": { - "version": "0.1.1", - "from": "strip-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", - "dev": true + "version": "0.3.0", + "from": "strip-ansi@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" }, "strip-json-comments": { "version": "2.0.1", @@ -2906,12 +2576,6 @@ "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", "dev": true }, - "timespan": { - "version": "2.3.0", - "from": "timespan@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", - "dev": true - }, "to-mongodb-core": { "version": "2.0.0", "from": "to-mongodb-core@>=2.0.0 <3.0.0", @@ -2989,20 +2653,6 @@ "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, - "utile": { - "version": "0.2.1", - "from": "utile@>=0.2.1 <0.3.0", - "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", - "dev": true, - "dependencies": { - "ncp": { - "version": "0.4.2", - "from": "ncp@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "dev": true - } - } - }, "uuid": { "version": "3.1.0", "from": "uuid@3.1.0", @@ -3025,20 +2675,6 @@ } } }, - "watch": { - "version": "0.13.0", - "from": "watch@>=0.13.0 <0.14.0", - "resolved": "https://registry.npmjs.org/watch/-/watch-0.13.0.tgz", - "dev": true, - "dependencies": { - "minimist": { - "version": "1.2.0", - "from": "minimist@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "dev": true - } - } - }, "when": { "version": "3.7.8", "from": "when@>=3.7.7 <4.0.0", @@ -3054,18 +2690,6 @@ "from": "wide-align@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" }, - "winston": { - "version": "0.8.3", - "from": "winston@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "from": "wordwrap@>=0.0.2 <0.1.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "dev": true - }, "wrappy": { "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 6024296ce3..3a6142609f 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -43,20 +43,12 @@ "v8-profiler": "^5.6.5" }, "devDependencies": { - "chai": "~4.1.1", - "sinon": "~3.2.1", - "sandboxed-module": "~0.3.0", - "grunt-execute": "~0.1.5", - "grunt-contrib-clean": "~0.5.0", - "grunt-mocha-test": "~0.9.3", - "grunt": "~0.4.2", - "grunt-available-tasks": "~0.4.2", - "grunt-contrib-coffee": "~0.10.1", "bunyan": "~2.0.2", + "chai": "~4.1.1", + "memorystream": "0.3.1", "mocha": "^4.0.1", - "grunt-bunyan": "~0.5.0", - "grunt-forever": "~0.4.2", - "timekeeper": "0.0.4", - "memorystream": "0.3.1" + "sandboxed-module": "~0.3.0", + "sinon": "~3.2.1", + "timekeeper": "0.0.4" } } From def7a0eeaa31a9ff86dd8ae691dcf2fa5e1899c2 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 11 Jan 2019 15:17:18 +0000 Subject: [PATCH 428/549] remove unnecessary modules --- services/track-changes/app/coffee/ProjectIterator.coffee | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/track-changes/app/coffee/ProjectIterator.coffee b/services/track-changes/app/coffee/ProjectIterator.coffee index b0bed523df..d64fc260e8 100644 --- a/services/track-changes/app/coffee/ProjectIterator.coffee +++ b/services/track-changes/app/coffee/ProjectIterator.coffee @@ -1,7 +1,3 @@ -async = require "async" -_ = require "underscore" -{db, ObjectId, BSON} = require "./mongojs" -logger = require "logger-sharelatex" Heap = require "heap" module.exports = ProjectIterator = From 5e1d51d5d732fbeb52899d5f2bddd163a083b47a Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Fri, 11 Jan 2019 19:04:35 +0000 Subject: [PATCH 429/549] Add note on acceptance tests to README.md --- services/track-changes/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/track-changes/README.md b/services/track-changes/README.md index 65cf6fe2ff..cc9c204e7c 100644 --- a/services/track-changes/README.md +++ b/services/track-changes/README.md @@ -5,6 +5,15 @@ An API for converting raw editor updates into a compressed and browseable histor [![Build Status](https://travis-ci.org/sharelatex/track-changes-sharelatex.png?branch=master)](https://travis-ci.org/sharelatex/track-changes-sharelatex) +Acceptance tests can be run with the command +``` +AWS_BUCKET= AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= make test +``` +where `bucket-name`, `aws-access-key` and `aws-secret-access-key` are the credentials for an AWS S3 bucket. + + + + License ------- From cc4d9ffcbc8e988a6773d8e3805bdc3fae73ae81 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Fri, 11 Jan 2019 19:07:06 +0000 Subject: [PATCH 430/549] Bump buildscript to 1.1.11 --- services/track-changes/Makefile | 2 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.ci.yml | 3 ++- services/track-changes/docker-compose.yml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 5fd166ab47..8c3793e584 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.10 +# Version: 1.1.11 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index adb6c282df..6ceaf90b35 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,4 +1,4 @@ ---script-version=1.1.10 +--script-version=1.1.11 track-changes --node-version=6.11.2 --acceptance-creds=aws diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 4e17f15673..564198efed 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.10 +# Version: 1.1.11 version: "2" @@ -11,6 +11,7 @@ services: user: node command: npm run test:unit:_run + test_acceptance: build: . image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index a556a008fa..6c19913608 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.10 +# Version: 1.1.11 version: "2" From 44762ef498bc17ba87fba8589894aea5dcd61e8e Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Fri, 11 Jan 2019 19:09:39 +0000 Subject: [PATCH 431/549] Add **/*.map to .gitignore --- services/track-changes/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index 5178f6093f..9df7f254ec 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -2,6 +2,7 @@ node_modules/ app/js app.js +**/*.map test/unit/js test/acceptance/js forever/ From d4a22a65ac1dc7a98300eba02da0aa6ae5b6f5fa Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 11 Jan 2019 15:01:03 +0000 Subject: [PATCH 432/549] add missing coffeescript to package.json using the same version as in web --- services/track-changes/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/package.json b/services/track-changes/package.json index ee3ceddd13..07a050887e 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -35,6 +35,7 @@ }, "devDependencies": { "chai": "~4.1.1", + "coffee-script": "^1.7.1", "sinon": "~3.2.1", "sandboxed-module": "~0.3.0", "grunt-execute": "~0.1.5", From 38dbea4b0e0cc49373bcdd821870ac755637b75a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 31 Jan 2019 16:56:10 +0000 Subject: [PATCH 433/549] bulk upgrade to 1.1.12, moved to npm rather than git --- services/track-changes/Jenkinsfile | 10 +- services/track-changes/Makefile | 6 +- services/track-changes/buildscript.txt | 5 +- services/track-changes/docker-compose.ci.yml | 12 +- services/track-changes/docker-compose.yml | 12 +- services/track-changes/npm-shrinkwrap.json | 834 +++---------------- services/track-changes/package.json | 8 +- 7 files changed, 137 insertions(+), 750 deletions(-) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 862abec028..804252ef26 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -50,8 +50,11 @@ pipeline { } } - stage('Package and publish build') { + stage('Package and docker push') { steps { + sh 'echo ${BUILD_NUMBER} > build_number.txt' + sh 'touch build.tar.gz' // Avoid tar warning about files changing during read + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make tar' withCredentials([file(credentialsId: 'gcr.io_overleaf-ops', variable: 'DOCKER_REPO_KEY_PATH')]) { sh 'docker login -u _json_key --password-stdin https://gcr.io/overleaf-ops < ${DOCKER_REPO_KEY_PATH}' @@ -62,9 +65,12 @@ pipeline { } } - stage('Publish build number') { + stage('Publish to s3') { steps { sh 'echo ${BRANCH_NAME}-${BUILD_NUMBER} > build_number.txt' + withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { + s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") + } withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { // The deployment process uses this file to figure out the latest build s3Upload(file:'build_number.txt', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/latest") diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 8c3793e584..b28f960b01 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.11 +# Version: 1.1.12 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -16,7 +16,6 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ docker-compose ${DOCKER_COMPOSE_FLAGS} - clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) @@ -43,6 +42,9 @@ build: --tag gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ . +tar: + $(DOCKER_COMPOSE) up tar + publish: docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 6ceaf90b35..0822059ef0 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,9 +1,8 @@ ---script-version=1.1.11 track-changes +--language=coffeescript --node-version=6.11.2 --acceptance-creds=aws ---language=coffeescript --dependencies=mongo,redis --docker-repos=gcr.io/overleaf-ops ---kube=false --build-target=docker +--script-version=1.1.12 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 564198efed..e05433ac79 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.11 +# Version: 1.1.12 version: "2" @@ -30,6 +30,16 @@ services: user: node command: npm run test:acceptance:_run + + + tar: + build: . + image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + volumes: + - ./:/tmp/build/ + command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . + user: root + redis: image: redis diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 6c19913608..e7b4d4e6d4 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.11 +# Version: 1.1.12 version: "2" @@ -36,6 +36,16 @@ services: - redis command: npm run test:acceptance + + + tar: + build: . + image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + volumes: + - ./:/tmp/build/ + command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . + user: root + redis: image: redis diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index fe97eb3c94..0454eae6b4 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -118,21 +118,6 @@ "from": "@sindresorhus/is@>=0.13.0 <0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.13.0.tgz" }, - "@sinonjs/commons": { - "version": "1.3.0", - "from": "@sinonjs/commons@^1.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.0.tgz" - }, - "@sinonjs/formatio": { - "version": "3.1.0", - "from": "@sinonjs/formatio@^3.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.1.0.tgz" - }, - "@sinonjs/samsam": { - "version": "3.0.2", - "from": "@sinonjs/samsam@^2 || ^3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.0.2.tgz" - }, "@types/caseless": { "version": "0.12.1", "from": "@types/caseless@*", @@ -198,16 +183,6 @@ "from": "ajv@>=6.5.5 <7.0.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz" }, - "ansi-regex": { - "version": "0.2.1", - "from": "ansi-regex@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" - }, - "ansi-styles": { - "version": "1.1.0", - "from": "ansi-styles@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" - }, "aproba": { "version": "1.2.0", "from": "aproba@>=1.0.3 <2.0.0", @@ -218,23 +193,6 @@ "from": "are-we-there-yet@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" }, - "argparse": { - "version": "0.1.16", - "from": "argparse@>=0.1.11 <0.2.0", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", - "dependencies": { - "underscore.string": { - "version": "2.4.0", - "from": "underscore.string@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" - } - } - }, - "array-from": { - "version": "2.1.1", - "from": "array-from@^2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz" - }, "arrify": { "version": "1.0.1", "from": "arrify@>=1.0.1 <2.0.0", @@ -252,11 +210,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", "optional": true }, - "assertion-error": { - "version": "1.1.0", - "from": "assertion-error@^1.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" - }, "async": { "version": "0.2.10", "from": "async@>=0.2.10 <0.3.0", @@ -328,11 +281,6 @@ "from": "block-stream@*", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" }, - "bluebird": { - "version": "3.5.3", - "from": "bluebird@>=3.3.4 <4.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz" - }, "boom": { "version": "0.4.2", "from": "boom@>=0.4.0 <0.5.0", @@ -343,12 +291,6 @@ "from": "brace-expansion@>=1.1.7 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" }, - "browser-stdout": { - "version": "1.3.0", - "from": "browser-stdout@1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "dev": true - }, "bson": { "version": "0.4.23", "from": "bson@>=0.4.20 <0.5.0", @@ -379,12 +321,6 @@ "from": "builtin-modules@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz" }, - "bunyan": { - "version": "2.0.2", - "from": "bunyan@>=2.0.2 <2.1.0", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", - "dev": true - }, "byline": { "version": "4.2.2", "from": "byline@>=4.2.1 <5.0.0", @@ -400,29 +336,6 @@ "from": "caseless@>=0.12.0 <0.13.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" }, - "chai": { - "version": "4.1.2", - "from": "chai@>=4.1.1 <4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "dev": true - }, - "chalk": { - "version": "0.5.1", - "from": "chalk@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "dependencies": { - "supports-color": { - "version": "0.2.0", - "from": "supports-color@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" - } - } - }, - "check-error": { - "version": "1.0.2", - "from": "check-error@^1.0.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" - }, "cli": { "version": "0.6.6", "from": "cli@>=0.6.6 <0.7.0", @@ -448,11 +361,6 @@ "from": "coffee-script@1.12.4", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz" }, - "colors": { - "version": "0.6.2", - "from": "colors@>=0.6.2 <0.7.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" - }, "combined-stream": { "version": "0.0.7", "from": "combined-stream@>=0.0.4 <0.1.0", @@ -528,21 +436,11 @@ } } }, - "dateformat": { - "version": "1.0.2-1.2.3", - "from": "dateformat@1.0.2-1.2.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz" - }, "debug": { "version": "4.1.1", "from": "debug@*", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz" }, - "deep-eql": { - "version": "3.0.1", - "from": "deep-eql@^3.0.0", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" - }, "deep-extend": { "version": "0.6.0", "from": "deep-extend@>=0.6.0 <0.7.0", @@ -574,18 +472,6 @@ "from": "detect-libc@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" }, - "diff": { - "version": "1.0.7", - "from": "diff@1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz" - }, - "dtrace-provider": { - "version": "0.8.7", - "from": "dtrace-provider@>=0.8.0 <0.9.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", - "dev": true, - "optional": true - }, "duplexify": { "version": "3.6.1", "from": "duplexify@>=3.6.0 <4.0.0", @@ -631,32 +517,11 @@ "from": "es6-promisify@>=5.0.0 <6.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "esprima": { - "version": "1.0.4", - "from": "esprima@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" - }, - "eventemitter2": { - "version": "0.4.14", - "from": "eventemitter2@>=0.4.13 <0.5.0", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" - }, "events": { "version": "1.1.1", "from": "events@1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" }, - "exeunt": { - "version": "1.1.0", - "from": "exeunt@1.1.0", - "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", - "dev": true - }, "exit": { "version": "0.1.2", "from": "exit@0.1.2", @@ -692,18 +557,6 @@ "from": "findit2@>=2.2.3 <3.0.0", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz" }, - "findup-sync": { - "version": "0.1.3", - "from": "findup-sync@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", - "dependencies": { - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - } - } - }, "flexbuffer": { "version": "0.0.6", "from": "flexbuffer@0.0.6", @@ -745,12 +598,6 @@ } } }, - "formatio": { - "version": "1.2.0", - "from": "formatio@1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "dev": true - }, "formidable": { "version": "1.0.14", "from": "formidable@1.0.14", @@ -761,23 +608,6 @@ "from": "fresh@0.2.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" }, - "fs-extra": { - "version": "0.9.1", - "from": "fs-extra@>=0.9.1 <0.10.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.9.1.tgz", - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - }, - "ncp": { - "version": "0.5.1", - "from": "ncp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz" - } - } - }, "fs.realpath": { "version": "1.0.0", "from": "fs.realpath@>=1.0.0 <2.0.0", @@ -839,16 +669,6 @@ "from": "gcp-metadata@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz" }, - "get-func-name": { - "version": "2.0.0", - "from": "get-func-name@^2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" - }, - "getobject": { - "version": "0.1.0", - "from": "getobject@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz" - }, "getpass": { "version": "0.1.7", "from": "getpass@>=0.1.1 <0.2.0", @@ -888,177 +708,6 @@ "from": "google-p12-pem@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.3.tgz" }, - "graceful-fs": { - "version": "1.2.3", - "from": "graceful-fs@>=1.2.0 <1.3.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" - }, - "growl": { - "version": "1.7.0", - "from": "growl@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz" - }, - "grunt": { - "version": "0.4.5", - "from": "grunt@>=0.4.5 <0.5.0", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", - "dependencies": { - "async": { - "version": "0.1.22", - "from": "async@>=0.1.22 <0.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" - }, - "coffee-script": { - "version": "1.3.3", - "from": "coffee-script@>=1.3.3 <1.4.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz" - }, - "glob": { - "version": "3.1.21", - "from": "glob@>=3.1.21 <3.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz" - }, - "inherits": { - "version": "1.0.2", - "from": "inherits@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz" - }, - "minimatch": { - "version": "0.2.14", - "from": "minimatch@>=0.2.12 <0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" - }, - "rimraf": { - "version": "2.2.8", - "from": "rimraf@>=2.2.8 <2.3.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" - } - } - }, - "grunt-bunyan": { - "version": "0.5.0", - "from": "grunt-bunyan@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/grunt-bunyan/-/grunt-bunyan-0.5.0.tgz", - "dependencies": { - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - } - } - }, - "grunt-contrib-clean": { - "version": "0.6.0", - "from": "grunt-contrib-clean@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz", - "dependencies": { - "rimraf": { - "version": "2.2.8", - "from": "rimraf@~2.2.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" - } - } - }, - "grunt-contrib-coffee": { - "version": "0.11.1", - "from": "grunt-contrib-coffee@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.11.1.tgz", - "dependencies": { - "coffee-script": { - "version": "1.7.1", - "from": "coffee-script@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.7.1.tgz" - }, - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - } - } - }, - "grunt-execute": { - "version": "0.2.2", - "from": "grunt-execute@>=0.2.2 <0.3.0", - "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.2.2.tgz" - }, - "grunt-legacy-log": { - "version": "0.1.3", - "from": "grunt-legacy-log@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", - "dependencies": { - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - }, - "underscore.string": { - "version": "2.3.3", - "from": "underscore.string@>=2.3.3 <2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" - } - } - }, - "grunt-legacy-log-utils": { - "version": "0.1.1", - "from": "grunt-legacy-log-utils@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", - "dependencies": { - "lodash": { - "version": "2.4.2", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - }, - "underscore.string": { - "version": "2.3.3", - "from": "underscore.string@>=2.3.3 <2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" - } - } - }, - "grunt-legacy-util": { - "version": "0.2.0", - "from": "grunt-legacy-util@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", - "dependencies": { - "async": { - "version": "0.1.22", - "from": "async@~0.1.22", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz" - } - } - }, - "grunt-mocha-test": { - "version": "0.11.0", - "from": "grunt-mocha-test@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.11.0.tgz", - "dependencies": { - "commander": { - "version": "2.0.0", - "from": "commander@2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz" - }, - "glob": { - "version": "3.2.3", - "from": "glob@3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz" - }, - "graceful-fs": { - "version": "2.0.3", - "from": "graceful-fs@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" - }, - "minimatch": { - "version": "0.2.14", - "from": "minimatch@~0.2.11", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" - }, - "mocha": { - "version": "1.20.1", - "from": "mocha@>=1.20.0 <1.21.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.20.1.tgz" - } - } - }, "gtoken": { "version": "2.3.0", "from": "gtoken@>=2.3.0 <3.0.0", @@ -1086,17 +735,6 @@ "from": "har-validator@>=5.1.0 <5.2.0", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz" }, - "has-ansi": { - "version": "0.1.0", - "from": "has-ansi@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz" - }, - "has-flag": { - "version": "2.0.0", - "from": "has-flag@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "dev": true - }, "has-unicode": { "version": "2.0.1", "from": "has-unicode@>=2.0.0 <3.0.0", @@ -1108,12 +746,6 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "optional": true }, - "he": { - "version": "1.1.1", - "from": "he@1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "dev": true - }, "heap": { "version": "0.2.6", "from": "heap@>=0.2.6 <0.3.0", @@ -1129,11 +761,6 @@ "from": "hoek@>=0.9.0 <0.10.0", "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" }, - "hooker": { - "version": "0.2.3", - "from": "hooker@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" - }, "http-signature": { "version": "0.10.1", "from": "http-signature@>=0.10.0 <0.11.0", @@ -1152,11 +779,6 @@ } } }, - "iconv-lite": { - "version": "0.2.11", - "from": "iconv-lite@>=0.2.11 <0.3.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz" - }, "ieee754": { "version": "1.1.8", "from": "ieee754@1.1.8", @@ -1177,23 +799,6 @@ "from": "ini@>=1.3.0 <1.4.0", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" }, - "ioredis": { - "version": "3.2.2", - "from": "ioredis@>=3.2.1 <4.0.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-3.2.2.tgz", - "dependencies": { - "debug": { - "version": "2.6.9", - "from": "debug@>=2.6.9 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - }, - "ms": { - "version": "2.0.0", - "from": "ms@2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - } - } - }, "is": { "version": "3.3.0", "from": "is@>=3.2.0 <4.0.0", @@ -1224,33 +829,11 @@ "from": "isstream@>=0.1.2 <0.2.0", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" }, - "jade": { - "version": "0.26.3", - "from": "jade@0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "dependencies": { - "commander": { - "version": "0.6.1", - "from": "commander@0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" - }, - "mkdirp": { - "version": "0.3.0", - "from": "mkdirp@0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" - } - } - }, "jmespath": { "version": "0.15.0", "from": "jmespath@0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" }, - "js-yaml": { - "version": "2.0.5", - "from": "js-yaml@>=2.0.5 <2.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz" - }, "jsbn": { "version": "0.1.1", "from": "jsbn@>=0.1.0 <0.2.0", @@ -1281,11 +864,6 @@ "from": "json-stringify-safe@5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, - "jsonfile": { - "version": "1.1.1", - "from": "jsonfile@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-1.1.1.tgz" - }, "jsonify": { "version": "0.0.0", "from": "jsonify@>=0.0.0 <0.1.0", @@ -1313,11 +891,6 @@ } } }, - "just-extend": { - "version": "4.0.2", - "from": "just-extend@^4.0.2", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz" - }, "jwa": { "version": "1.1.6", "from": "jwa@>=1.1.5 <2.0.0", @@ -1338,120 +911,50 @@ "from": "line-reader@>=0.2.4 <0.3.0", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, - "lodash": { - "version": "0.9.2", - "from": "lodash@>=0.9.2 <0.10.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz" - }, - "lodash.assign": { - "version": "4.2.0", - "from": "lodash.assign@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz" - }, - "lodash.bind": { - "version": "4.2.1", - "from": "lodash.bind@>=4.2.1 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz" - }, - "lodash.clone": { - "version": "4.5.0", - "from": "lodash.clone@>=4.5.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "from": "lodash.clonedeep@>=4.5.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" - }, "lodash.defaults": { "version": "4.2.0", "from": "lodash.defaults@>=4.2.0 <5.0.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" }, - "lodash.difference": { - "version": "4.5.0", - "from": "lodash.difference@>=4.5.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz" - }, "lodash.flatten": { "version": "4.4.0", "from": "lodash.flatten@>=4.4.0 <5.0.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" }, - "lodash.foreach": { - "version": "4.5.0", - "from": "lodash.foreach@>=4.5.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz" - }, - "lodash.get": { - "version": "4.4.2", - "from": "lodash.get@^4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" - }, - "lodash.isempty": { - "version": "4.4.0", - "from": "lodash.isempty@>=4.4.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz" - }, - "lodash.keys": { - "version": "4.2.0", - "from": "lodash.keys@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz" - }, - "lodash.noop": { - "version": "3.0.1", - "from": "lodash.noop@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz" - }, - "lodash.partial": { - "version": "4.2.1", - "from": "lodash.partial@>=4.2.1 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.partial/-/lodash.partial-4.2.1.tgz" - }, - "lodash.pick": { - "version": "4.4.0", - "from": "lodash.pick@>=4.4.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz" - }, "lodash.pickby": { "version": "4.6.0", "from": "lodash.pickby@>=4.6.0 <5.0.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz" }, - "lodash.sample": { - "version": "4.2.1", - "from": "lodash.sample@>=4.2.1 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.sample/-/lodash.sample-4.2.1.tgz" - }, - "lodash.shuffle": { - "version": "4.2.0", - "from": "lodash.shuffle@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz" - }, - "lodash.values": { - "version": "4.3.0", - "from": "lodash.values@>=4.3.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz" - }, "logger-sharelatex": { - "version": "1.5.9", - "from": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.9", - "resolved": "git+https://github.com/sharelatex/logger-sharelatex.git#e8e1b95052f62e107336053e4a983f81cdbdf589", + "version": "1.6.0", + "from": "logger-sharelatex@>=1.6.0 <2.0.0", + "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.6.0.tgz", "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + }, + "aws-sign2": { + "version": "0.7.0", + "from": "aws-sign2@>=0.7.0 <0.8.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" + }, "bunyan": { "version": "1.5.1", "from": "bunyan@1.5.1", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz" }, - "chai": { - "version": "4.2.0", - "from": "chai@latest", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz" + "combined-stream": { + "version": "1.0.7", + "from": "combined-stream@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" }, - "diff": { - "version": "3.5.0", - "from": "diff@>=3.5.0 <4.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" }, "dtrace-provider": { "version": "0.6.0", @@ -1459,43 +962,58 @@ "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", "optional": true }, - "has-flag": { - "version": "3.0.0", - "from": "has-flag@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" }, - "lolex": { - "version": "3.0.0", - "from": "lolex@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-3.0.0.tgz" + "form-data": { + "version": "2.3.3", + "from": "form-data@>=2.3.2 <2.4.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" }, - "sandboxed-module": { - "version": "2.0.3", - "from": "sandboxed-module@latest", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz" + "http-signature": { + "version": "1.2.0", + "from": "http-signature@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" }, - "sinon": { - "version": "7.2.2", - "from": "sinon@latest", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.2.tgz" + "oauth-sign": { + "version": "0.9.0", + "from": "oauth-sign@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" }, - "supports-color": { - "version": "5.5.0", - "from": "supports-color@>=5.5.0 <6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" }, - "timekeeper": { - "version": "1.0.0", - "from": "timekeeper@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-1.0.0.tgz" + "qs": { + "version": "6.5.2", + "from": "qs@>=6.5.2 <6.6.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" + }, + "request": { + "version": "2.88.0", + "from": "request@>=2.88.0 <3.0.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz" + }, + "tough-cookie": { + "version": "2.4.3", + "from": "tough-cookie@>=2.4.3 <2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz" + }, + "tunnel-agent": { + "version": "0.6.0", + "from": "tunnel-agent@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" + }, + "uuid": { + "version": "3.3.2", + "from": "uuid@>=3.3.2 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" } } }, - "lolex": { - "version": "2.7.5", - "from": "lolex@>=2.1.2 <3.0.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz" - }, "long": { "version": "4.0.0", "from": "long@>=4.0.0 <5.0.0", @@ -1516,12 +1034,6 @@ "from": "lynx@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz" }, - "memorystream": { - "version": "0.3.1", - "from": "memorystream@0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "dev": true - }, "mersenne": { "version": "0.0.4", "from": "mersenne@>=0.0.3 <0.1.0", @@ -1533,9 +1045,9 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" }, "metrics-sharelatex": { - "version": "2.0.12", - "from": "git+https://github.com/sharelatex/metrics-sharelatex.git#v2.0.12", - "resolved": "git+https://github.com/sharelatex/metrics-sharelatex.git#3ac1621ef049e2f2d88a83b3a41011333d609662", + "version": "2.1.1", + "from": "metrics-sharelatex@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.1.1.tgz", "dependencies": { "coffee-script": { "version": "1.6.0", @@ -1579,74 +1091,11 @@ "from": "mkdirp@0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, - "mocha": { - "version": "4.1.0", - "from": "mocha@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "dev": true, - "dependencies": { - "commander": { - "version": "2.11.0", - "from": "commander@2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "dev": true - }, - "debug": { - "version": "3.1.0", - "from": "debug@3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "dev": true - }, - "diff": { - "version": "3.3.1", - "from": "diff@3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "dev": true - }, - "glob": { - "version": "7.1.2", - "from": "glob@7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "dev": true - }, - "growl": { - "version": "1.10.3", - "from": "growl@1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dev": true - }, - "ms": { - "version": "2.0.0", - "from": "ms@2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "dev": true - } - } - }, "module-details-from-path": { "version": "1.0.3", "from": "module-details-from-path@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz" }, - "moment": { - "version": "2.23.0", - "from": "moment@>=2.10.6 <3.0.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz", - "dev": true, - "optional": true - }, "mongo-uri": { "version": "0.1.2", "from": "mongo-uri@>=0.1.2 <0.2.0", @@ -1720,23 +1169,12 @@ "from": "nan@>=2.0.8 <3.0.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz" }, - "native-promise-only": { - "version": "0.8.1", - "from": "native-promise-only@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "dev": true - }, "ncp": { "version": "2.0.0", "from": "ncp@~2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "optional": true }, - "nise": { - "version": "1.4.8", - "from": "nise@^1.0.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.8.tgz" - }, "node-fetch": { "version": "2.3.0", "from": "node-fetch@>=2.2.0 <3.0.0", @@ -1889,11 +1327,6 @@ } } }, - "nopt": { - "version": "1.0.10", - "from": "nopt@>=1.0.10 <1.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz" - }, "npmlog": { "version": "4.1.2", "from": "npmlog@>=4.0.2 <5.0.0", @@ -1970,23 +1403,6 @@ "from": "path-parse@>=1.0.6 <2.0.0", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" }, - "path-to-regexp": { - "version": "1.7.0", - "from": "path-to-regexp@^1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "dependencies": { - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - } - } - }, - "pathval": { - "version": "1.1.0", - "from": "pathval@^1.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz" - }, "pause": { "version": "0.0.1", "from": "pause@0.0.1", @@ -2096,10 +1512,10 @@ "from": "redis-commands@>=1.2.0 <2.0.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz" }, - "redis-parser": { - "version": "2.6.0", - "from": "redis-parser@>=2.4.0 <3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz" + "redis-errors": { + "version": "1.2.0", + "from": "redis-errors@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" }, "redis-sentinel": { "version": "0.1.1", @@ -2114,9 +1530,9 @@ } }, "redis-sharelatex": { - "version": "1.0.4", - "from": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.4", - "resolved": "git+https://github.com/sharelatex/redis-sharelatex.git#ca4e906559c1405d132e8edd7db763d64a57be62", + "version": "1.0.5", + "from": "redis-sharelatex@>=1.0.5 <2.0.0", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.5.tgz", "dependencies": { "async": { "version": "2.6.1", @@ -2128,6 +1544,16 @@ "from": "coffee-script@1.8.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz" }, + "debug": { + "version": "3.2.6", + "from": "debug@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" + }, + "ioredis": { + "version": "4.6.0", + "from": "ioredis@4.6.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.6.0.tgz" + }, "lodash": { "version": "4.17.11", "from": "lodash@>=4.17.10 <5.0.0", @@ -2137,6 +1563,11 @@ "version": "0.12.1", "from": "redis@0.12.1", "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + }, + "redis-parser": { + "version": "3.0.0", + "from": "redis-parser@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" } } }, @@ -2244,11 +1675,6 @@ "from": "require-in-the-middle@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-3.1.0.tgz" }, - "require-like": { - "version": "0.1.2", - "from": "require-like@0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" - }, "resolve": { "version": "1.9.0", "from": "resolve@>=1.5.0 <2.0.0", @@ -2319,26 +1745,6 @@ "from": "safer-buffer@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" }, - "samsam": { - "version": "1.3.0", - "from": "samsam@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "dev": true - }, - "sandboxed-module": { - "version": "0.3.0", - "from": "sandboxed-module@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", - "dev": true, - "dependencies": { - "stack-trace": { - "version": "0.0.6", - "from": "stack-trace@0.0.6", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", - "dev": true - } - } - }, "sax": { "version": "1.2.1", "from": "sax@1.2.1", @@ -2361,8 +1767,8 @@ }, "settings-sharelatex": { "version": "1.1.0", - "from": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.1.0", - "resolved": "git+https://github.com/sharelatex/settings-sharelatex.git#93f63d029b52fef8825c3a401b2b6a7ba29b4750", + "from": "settings-sharelatex@latest", + "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", "dependencies": { "coffee-script": { "version": "1.6.0", @@ -2386,20 +1792,6 @@ "from": "signal-exit@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" }, - "sinon": { - "version": "3.2.1", - "from": "sinon@>=3.2.1 <3.3.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", - "dev": true, - "dependencies": { - "diff": { - "version": "3.5.0", - "from": "diff@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "dev": true - } - } - }, "sntp": { "version": "0.2.4", "from": "sntp@>=0.2.0 <0.3.0", @@ -2438,6 +1830,11 @@ "from": "stack-trace@0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" }, + "standard-as-callback": { + "version": "1.0.1", + "from": "standard-as-callback@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-1.0.1.tgz" + }, "statsd-parser": { "version": "0.0.4", "from": "statsd-parser@>=0.0.4 <0.1.0", @@ -2475,22 +1872,11 @@ "from": "stringstream@>=0.0.4 <0.1.0", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz" }, - "strip-ansi": { - "version": "0.3.0", - "from": "strip-ansi@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" - }, "strip-json-comments": { "version": "2.0.1", "from": "strip-json-comments@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" }, - "supports-color": { - "version": "4.4.0", - "from": "supports-color@4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "dev": true - }, "symbol-observable": { "version": "1.2.0", "from": "symbol-observable@>=1.2.0 <2.0.0", @@ -2550,11 +1936,6 @@ } } }, - "text-encoding": { - "version": "0.6.4", - "from": "text-encoding@0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz" - }, "through": { "version": "2.3.8", "from": "through@>=2.2.7 <3.0.0", @@ -2570,12 +1951,6 @@ "from": "thunky@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" }, - "timekeeper": { - "version": "0.0.4", - "from": "timekeeper@0.0.4", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", - "dev": true - }, "to-mongodb-core": { "version": "2.0.0", "from": "to-mongodb-core@>=2.0.0 <3.0.0", @@ -2606,11 +1981,6 @@ "from": "tweetnacl@>=0.14.0 <0.15.0", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" }, - "type-detect": { - "version": "4.0.8", - "from": "type-detect@^4.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" - }, "uid-number": { "version": "0.0.6", "from": "uid-number@>=0.0.6 <0.0.7", @@ -2626,11 +1996,6 @@ "from": "underscore@>=1.7.0 <1.8.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" }, - "underscore.string": { - "version": "2.2.1", - "from": "underscore.string@>=2.2.1 <2.3.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz" - }, "uri-js": { "version": "4.2.2", "from": "uri-js@>=4.2.2 <5.0.0", @@ -2680,11 +2045,6 @@ "from": "when@>=3.7.7 <4.0.0", "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" }, - "which": { - "version": "1.0.9", - "from": "which@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" - }, "wide-align": { "version": "1.1.3", "from": "wide-align@>=1.1.0 <2.0.0", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 3a6142609f..a1ccc67276 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -29,16 +29,16 @@ "express": "3.3.5", "heap": "^0.2.6", "line-reader": "^0.2.4", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.9", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v2.0.12", + "logger-sharelatex": "^1.6.0", + "metrics-sharelatex": "^2.1.1", "mongo-uri": "^0.1.2", "mongojs": "2.4.0", "redis": "~0.10.1", - "redis-sharelatex": "git+https://github.com/sharelatex/redis-sharelatex.git#v1.0.4", + "redis-sharelatex": "^1.0.5", "request": "~2.33.0", "requestretry": "^1.12.0", "s3-streams": "^0.3.0", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.1.0", + "settings-sharelatex": "^1.1.0", "underscore": "~1.7.0", "v8-profiler": "^5.6.5" }, From 6a3506aed9dd73e4f2472833bd391c917390d309 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 1 Feb 2019 19:42:14 +0000 Subject: [PATCH 434/549] update redis key schema for cluster --- services/track-changes/config/settings.defaults.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 3c17ee1f93..4a3ee21644 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -24,15 +24,15 @@ module.exports = port: process.env['REDIS_PORT'] or 6379 password: process.env["REDIS_PASSWORD"] or "" key_schema: - historyLock: ({doc_id}) -> "HistoryLock:#{doc_id}" - historyIndexLock: ({project_id}) -> "HistoryIndexLock:#{project_id}" + historyLock: ({doc_id}) -> "HistoryLock:{#{doc_id}}" + historyIndexLock: ({project_id}) -> "HistoryIndexLock:{#{project_id}}" history: host: process.env["REDIS_HOST"] or "localhost" port: process.env['REDIS_PORT'] or 6379 password: process.env["REDIS_PASSWORD"] or "" key_schema: - uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:#{doc_id}" - docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:#{project_id}" + uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:{#{doc_id}}" + docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:{#{project_id}}" trackchanges: s3: From 7caf3d4935cedeee9173e6dd886185908be54616 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 6 Feb 2019 11:29:31 +0000 Subject: [PATCH 435/549] remove debugging --- .../test/acceptance/coffee/AppendingUpdatesTests.coffee | 7 ------- 1 file changed, 7 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index e018cd2be0..45dcec23bf 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -6,8 +6,6 @@ mongojs = require "../../../app/js/mongojs" ObjectId = mongojs.ObjectId Settings = require "settings-sharelatex" request = require "request" -console.log "hiiiiis" -console.log Settings.redis.history rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now TrackChangesApp = require "./helpers/TrackChangesApp" @@ -87,7 +85,6 @@ describe "Appending doc ops to the history", -> describe "when the updates are recent and from the same user", -> beforeEach (done) -> - console.log 1 TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ op: [{ i: "b", p: 6 }] meta: { ts: Date.now(), user_id: @user_id } @@ -101,13 +98,9 @@ describe "Appending doc ops to the history", -> meta: { ts: Date.now(), user_id: @user_id } v: 8 }], (error) => - console.log 2, error throw error if error? - console.log 3 TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - console.log 4, error throw error if error? - console.log 5 done() it "should combine all the updates into one pack", -> From 0151826eaff76acf5142ba544dd540481aef20f2 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 6 Feb 2019 12:23:15 +0000 Subject: [PATCH 436/549] clear error if s3 keys are not set for tests --- .../test/acceptance/coffee/ArchivingUpdatesTests.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 74d5d4031d..06d339795f 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -16,6 +16,10 @@ MockWebApi = require "./helpers/MockWebApi" describe "Archiving updates", -> before (done) -> + if Settings?.trackchanges?.s3?.key.length < 1 + message = "s3 keys not setup, this test setup will fail" + return done(message) + @now = Date.now() @to = @now @user_id = ObjectId().toString() From 79c1dc5c1aaa83a73c50d16e2033796c1b16cf07 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 7 Feb 2019 15:54:13 +0000 Subject: [PATCH 437/549] use explicit json content-type to avoid security issues with text/html --- services/track-changes/app/coffee/HttpController.coffee | 5 +++-- .../unit/coffee/HttpController/HttpControllerTests.coffee | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.coffee index 9ede86f3ee..b931c41026 100644 --- a/services/track-changes/app/coffee/HttpController.coffee +++ b/services/track-changes/app/coffee/HttpController.coffee @@ -79,7 +79,7 @@ module.exports = HttpController = logger.log {project_id, doc_id, from, to}, "getting diff" DiffManager.getDiff project_id, doc_id, from, to, (error, diff) -> return next(error) if error? - res.send JSON.stringify(diff: diff) + res.json {diff: diff} getUpdates: (req, res, next = (error) ->) -> project_id = req.params.project_id @@ -91,9 +91,10 @@ module.exports = HttpController = UpdatesManager.getSummarizedProjectUpdates project_id, before: before, min_count: min_count, (error, updates, nextBeforeTimestamp) -> return next(error) if error? - res.send JSON.stringify + res.json { updates: updates nextBeforeTimestamp: nextBeforeTimestamp + } restore: (req, res, next = (error) ->) -> {doc_id, project_id, version} = req.params diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee index ec33748536..8eded7ed7d 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee @@ -71,7 +71,7 @@ describe "HttpController", -> from: @from.toString() to: @to.toString() @res = - send: sinon.stub() + json: sinon.stub() @diff = [ u: "mock-diff" ] @DiffManager.getDiff = sinon.stub().callsArgWith(4, null, @diff) @HttpController.getDiff @req, @res, @next @@ -82,7 +82,7 @@ describe "HttpController", -> .should.equal true it "should return the diff", -> - @res.send.calledWith(JSON.stringify(diff: @diff)).should.equal true + @res.json.calledWith({diff: @diff}).should.equal true describe "getUpdates", -> beforeEach -> @@ -96,7 +96,7 @@ describe "HttpController", -> before: @before.toString() min_count: @min_count.toString() @res = - send: sinon.stub() + json: sinon.stub() @updates = ["mock-summarized-updates"] @UpdatesManager.getSummarizedProjectUpdates = sinon.stub().callsArgWith(2, null, @updates, @nextBeforeTimestamp) @HttpController.getUpdates @req, @res, @next @@ -107,7 +107,7 @@ describe "HttpController", -> .should.equal true it "should return the formatted updates", -> - @res.send.calledWith(JSON.stringify(updates: @updates, nextBeforeTimestamp: @nextBeforeTimestamp)).should.equal true + @res.json.calledWith({updates: @updates, nextBeforeTimestamp: @nextBeforeTimestamp}).should.equal true describe "RestoreManager", -> beforeEach -> From d47dcca0d60ae3114058f843e8a3bc85881a807f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 5 Mar 2019 17:10:18 +0000 Subject: [PATCH 438/549] add sentry dsn to config --- services/track-changes/config/settings.defaults.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 4a3ee21644..1b76b35e0f 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -43,3 +43,6 @@ module.exports = path: dumpFolder: Path.join(TMP_DIR, "dumpFolder") + + sentry: + dsn: process.env.SENTRY_DSN From 266c6c368e48b4724b6056b190eb836af09eb67b Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 8 Mar 2019 15:32:58 +0000 Subject: [PATCH 439/549] have track changes make calls to web_api not web --- services/track-changes/config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 1b76b35e0f..779b3fbdfd 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -15,7 +15,7 @@ module.exports = docstore: url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" web: - url: "http://#{process.env["WEB_HOST"] or "localhost"}:#{process.env['WEB_PORT'] or 3000}" + url: "http://#{process.env["WEB_API_HOST"] or "localhost"}:#{process.env['WEB_API_PORT'] or 3000}" user: "sharelatex" pass: "password" redis: From 63920ce33c2dcf2594aa6e862f61cab838c36955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Alby?= Date: Tue, 7 May 2019 16:21:52 +0100 Subject: [PATCH 440/549] Update README.md --- services/track-changes/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/services/track-changes/README.md b/services/track-changes/README.md index cc9c204e7c..6a0f1d79ee 100644 --- a/services/track-changes/README.md +++ b/services/track-changes/README.md @@ -1,10 +1,8 @@ -track-changes-sharelatex +overleaf/track-changes ======================== An API for converting raw editor updates into a compressed and browseable history. -[![Build Status](https://travis-ci.org/sharelatex/track-changes-sharelatex.png?branch=master)](https://travis-ci.org/sharelatex/track-changes-sharelatex) - Acceptance tests can be run with the command ``` AWS_BUCKET= AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= make test @@ -19,4 +17,4 @@ License The code in this repository is released under the GNU AFFERO GENERAL PUBLIC LICENSE, version 3. A copy can be found in the `LICENSE` file. -Copyright (c) ShareLaTeX, 2014. +Copyright (c) Overleaf, 2014-2019. From 6ff1ca3967a0e62bf612c01f93374ee5670336c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Alby?= Date: Tue, 7 May 2019 17:48:59 +0100 Subject: [PATCH 441/549] update Git URL in Jenkinsfile --- services/track-changes/Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 804252ef26..4b85bb04e6 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -4,10 +4,10 @@ pipeline { agent any environment { - GIT_PROJECT = "track-changes-sharelatex" + GIT_PROJECT = "track-changes" JENKINS_WORKFLOW = "track-changes-sharelatex" TARGET_URL = "${env.JENKINS_URL}blue/organizations/jenkins/${JENKINS_WORKFLOW}/detail/$BRANCH_NAME/$BUILD_NUMBER/pipeline" - GIT_API_URL = "https://api.github.com/repos/sharelatex/${GIT_PROJECT}/statuses/$GIT_COMMIT" + GIT_API_URL = "https://api.github.com/repos/overleaf/${GIT_PROJECT}/statuses/$GIT_COMMIT" } triggers { From 61450bb125195042f000aa796741984362f10ded Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Fri, 21 Jun 2019 15:32:47 +0100 Subject: [PATCH 442/549] update logger and metrics --- services/track-changes/Makefile | 6 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.ci.yml | 5 +- services/track-changes/docker-compose.yml | 8 +- services/track-changes/npm-shrinkwrap.json | 688 +++++++++++++----- services/track-changes/package.json | 6 +- .../coffee/AppendingUpdatesTests.coffee | 11 + .../coffee/ArchivingUpdatesTests.coffee | 3 + .../coffee/FlushingUpdatesTests.coffee | 11 +- .../coffee/GettingADiffTests.coffee | 3 + .../coffee/GettingUpdatesTests.coffee | 6 +- .../acceptance/coffee/LockManagerTests.coffee | 3 + .../coffee/RestoringVersions.coffee | 3 + 13 files changed, 560 insertions(+), 195 deletions(-) diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index b28f960b01..6fb3b47b4f 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.12 +# Version: 1.1.21 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -29,7 +29,9 @@ test: test_unit test_acceptance test_unit: @[ ! -d test/unit ] && echo "track-changes has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit -test_acceptance: test_clean test_acceptance_pre_run # clear the database before each acceptance test run +test_acceptance: test_clean test_acceptance_pre_run test_acceptance_run + +test_acceptance_run: @[ ! -d test/acceptance ] && echo "track-changes has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance test_clean: diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 0822059ef0..80e4a36482 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -5,4 +5,4 @@ track-changes --dependencies=mongo,redis --docker-repos=gcr.io/overleaf-ops --build-target=docker ---script-version=1.1.12 +--script-version=1.1.21 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index e05433ac79..baac7bccae 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.12 +# Version: 1.1.21 version: "2" @@ -10,6 +10,8 @@ services: image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER user: node command: npm run test:unit:_run + environment: + NODE_ENV: test test_acceptance: @@ -24,6 +26,7 @@ services: AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_BUCKET: ${AWS_BUCKET} MOCHA_GREP: ${MOCHA_GREP} + NODE_ENV: test depends_on: - mongo - redis diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index e7b4d4e6d4..18fa7aed5e 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,18 +1,19 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.12 +# Version: 1.1.21 version: "2" services: test_unit: - build: . + image: node:6.11.2 volumes: - .:/app working_dir: /app environment: MOCHA_GREP: ${MOCHA_GREP} + NODE_ENV: test command: npm run test:unit user: node @@ -30,6 +31,8 @@ services: AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_BUCKET: ${AWS_BUCKET} MOCHA_GREP: ${MOCHA_GREP} + LOG_LEVEL: ERROR + NODE_ENV: test user: node depends_on: - mongo @@ -52,3 +55,4 @@ services: mongo: image: mongo:3.4 + diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 0454eae6b4..348128696c 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -3,19 +3,24 @@ "version": "0.1.4", "dependencies": { "@google-cloud/common": { - "version": "0.27.0", - "from": "@google-cloud/common@>=0.27.0 <0.28.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.27.0.tgz" + "version": "0.32.1", + "from": "@google-cloud/common@>=0.32.0 <0.33.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz" }, "@google-cloud/debug-agent": { - "version": "3.0.1", + "version": "3.2.0", "from": "@google-cloud/debug-agent@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.2.0.tgz", "dependencies": { "coffeescript": { - "version": "2.3.2", + "version": "2.4.1", "from": "coffeescript@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.3.2.tgz" + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.4.1.tgz" + }, + "semver": { + "version": "6.1.1", + "from": "semver@>=6.0.0 <7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz" } } }, @@ -29,37 +34,69 @@ "from": "@google-cloud/common@>=0.26.0 <0.27.0", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz" }, + "@google-cloud/promisify": { + "version": "0.3.1", + "from": "@google-cloud/promisify@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz" + }, + "arrify": { + "version": "1.0.1", + "from": "arrify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + }, + "gcp-metadata": { + "version": "0.9.3", + "from": "gcp-metadata@>=0.9.0 <0.10.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz" + }, + "google-auth-library": { + "version": "2.0.2", + "from": "google-auth-library@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", + "dependencies": { + "gcp-metadata": { + "version": "0.7.0", + "from": "gcp-metadata@>=0.7.0 <0.8.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz" + } + } + }, + "lru-cache": { + "version": "5.1.1", + "from": "lru-cache@^5.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + }, "through2": { - "version": "3.0.0", + "version": "3.0.1", "from": "through2@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz" } } }, "@google-cloud/projectify": { - "version": "0.3.2", - "from": "@google-cloud/projectify@>=0.3.2 <0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.2.tgz" + "version": "0.3.3", + "from": "@google-cloud/projectify@>=0.3.3 <0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz" }, "@google-cloud/promisify": { - "version": "0.3.1", - "from": "@google-cloud/promisify@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz" + "version": "0.4.0", + "from": "@google-cloud/promisify@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz" }, "@google-cloud/trace-agent": { - "version": "3.5.0", + "version": "3.6.1", "from": "@google-cloud/trace-agent@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.5.0.tgz", + "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.6.1.tgz", "dependencies": { - "@google-cloud/common": { - "version": "0.28.0", - "from": "@google-cloud/common@>=0.28.0 <0.29.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.28.0.tgz" - }, "methods": { "version": "1.1.2", "from": "methods@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + }, + "semver": { + "version": "6.1.1", + "from": "semver@^6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz" } } }, @@ -114,14 +151,38 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" }, "@sindresorhus/is": { - "version": "0.13.0", - "from": "@sindresorhus/is@>=0.13.0 <0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.13.0.tgz" + "version": "0.15.0", + "from": "@sindresorhus/is@>=0.15.0 <0.16.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.15.0.tgz" + }, + "@sinonjs/commons": { + "version": "1.4.0", + "from": "@sinonjs/commons@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", + "dev": true + }, + "@sinonjs/formatio": { + "version": "3.2.1", + "from": "@sinonjs/formatio@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", + "dev": true + }, + "@sinonjs/samsam": { + "version": "3.3.2", + "from": "@sinonjs/samsam@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", + "dev": true + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "from": "@sinonjs/text-encoding@>=0.7.1 <0.8.0", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "dev": true }, "@types/caseless": { - "version": "0.12.1", + "version": "0.12.2", "from": "@types/caseless@*", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz" + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz" }, "@types/console-log-level": { "version": "1.4.0", @@ -144,9 +205,9 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz" }, "@types/node": { - "version": "10.12.18", + "version": "12.0.8", "from": "@types/node@*", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz" + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz" }, "@types/request": { "version": "2.48.1", @@ -159,24 +220,29 @@ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz" }, "@types/tough-cookie": { - "version": "2.3.4", + "version": "2.3.5", "from": "@types/tough-cookie@*", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.4.tgz" + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz" }, "abbrev": { "version": "1.1.1", "from": "abbrev@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" }, + "abort-controller": { + "version": "3.0.0", + "from": "abort-controller@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" + }, "acorn": { - "version": "5.7.3", - "from": "acorn@>=5.0.3 <6.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz" + "version": "6.1.1", + "from": "acorn@>=6.0.0 <7.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz" }, "agent-base": { - "version": "4.2.1", + "version": "4.3.0", "from": "agent-base@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz" + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz" }, "ajv": { "version": "6.6.2", @@ -193,10 +259,16 @@ "from": "are-we-there-yet@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" }, + "array-from": { + "version": "2.1.1", + "from": "array-from@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "dev": true + }, "arrify": { - "version": "1.0.1", - "from": "arrify@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + "version": "2.0.1", + "from": "arrify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" }, "asn1": { "version": "0.1.11", @@ -210,6 +282,12 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", "optional": true }, + "assertion-error": { + "version": "1.1.0", + "from": "assertion-error@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "dev": true + }, "async": { "version": "0.2.10", "from": "async@>=0.2.10 <0.3.0", @@ -242,9 +320,9 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz" }, "axios": { - "version": "0.18.0", + "version": "0.18.1", "from": "axios@>=0.18.0 <0.19.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz" + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz" }, "balanced-match": { "version": "1.0.0", @@ -267,9 +345,9 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz" }, "bindings": { - "version": "1.3.1", + "version": "1.5.0", "from": "bindings@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz" + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" }, "bintrees": { "version": "1.0.1", @@ -291,6 +369,12 @@ "from": "brace-expansion@>=1.1.7 <2.0.0", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" }, + "browser-stdout": { + "version": "1.3.0", + "from": "browser-stdout@1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "dev": true + }, "bson": { "version": "0.4.23", "from": "bson@>=0.4.20 <0.5.0", @@ -317,9 +401,15 @@ "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" }, "builtin-modules": { - "version": "3.0.0", + "version": "3.1.0", "from": "builtin-modules@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz" + }, + "bunyan": { + "version": "2.0.2", + "from": "bunyan@>=2.0.2 <2.1.0", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", + "dev": true }, "byline": { "version": "4.2.2", @@ -336,6 +426,18 @@ "from": "caseless@>=0.12.0 <0.13.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" }, + "chai": { + "version": "4.1.2", + "from": "chai@>=4.1.1 <4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "from": "check-error@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "dev": true + }, "cli": { "version": "0.6.6", "from": "cli@>=0.6.6 <0.7.0", @@ -359,7 +461,8 @@ "coffee-script": { "version": "1.12.4", "from": "coffee-script@1.12.4", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz" + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz", + "dev": true }, "combined-stream": { "version": "0.0.7", @@ -388,9 +491,9 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" }, "console-log-level": { - "version": "1.4.0", + "version": "1.4.1", "from": "console-log-level@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.0.tgz" + "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz" }, "continuation-local-storage": { "version": "3.2.1", @@ -441,15 +544,21 @@ "from": "debug@*", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz" }, + "deep-eql": { + "version": "3.0.1", + "from": "deep-eql@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "dev": true + }, "deep-extend": { "version": "0.6.0", "from": "deep-extend@>=0.6.0 <0.7.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" }, "delay": { - "version": "4.1.0", + "version": "4.3.0", "from": "delay@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/delay/-/delay-4.1.0.tgz" + "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz" }, "delayed-stream": { "version": "0.0.5", @@ -463,19 +572,31 @@ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" }, "denque": { - "version": "1.4.0", + "version": "1.4.1", "from": "denque@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.0.tgz" + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz" }, "detect-libc": { "version": "1.0.3", "from": "detect-libc@>=1.0.2 <2.0.0", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" }, + "diff": { + "version": "3.3.1", + "from": "diff@3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "dev": true + }, + "dtrace-provider": { + "version": "0.8.7", + "from": "dtrace-provider@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", + "optional": true + }, "duplexify": { - "version": "3.6.1", + "version": "3.7.1", "from": "duplexify@>=3.6.0 <4.0.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz" + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz" }, "each-series": { "version": "1.0.0", @@ -488,9 +609,9 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" }, "ecdsa-sig-formatter": { - "version": "1.0.10", - "from": "ecdsa-sig-formatter@1.0.10", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz" + "version": "1.0.11", + "from": "ecdsa-sig-formatter@1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" }, "emitter-listener": { "version": "1.1.2", @@ -508,20 +629,37 @@ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" }, "es6-promise": { - "version": "4.2.5", + "version": "4.2.8", "from": "es6-promise@>=4.0.3 <5.0.0", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz" + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" }, "es6-promisify": { "version": "5.0.0", "from": "es6-promisify@>=5.0.0 <6.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "dev": true + }, + "event-target-shim": { + "version": "5.0.1", + "from": "event-target-shim@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" + }, "events": { "version": "1.1.1", "from": "events@1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" }, + "exeunt": { + "version": "1.1.0", + "from": "exeunt@1.1.0", + "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", + "dev": true + }, "exit": { "version": "0.1.2", "from": "exit@0.1.2", @@ -552,20 +690,25 @@ "from": "fast-json-stable-stringify@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz" }, + "fast-text-encoding": { + "version": "1.0.0", + "from": "fast-text-encoding@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz" + }, + "file-uri-to-path": { + "version": "1.0.0", + "from": "file-uri-to-path@1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" + }, "findit2": { "version": "2.2.3", "from": "findit2@>=2.2.3 <3.0.0", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz" }, - "flexbuffer": { - "version": "0.0.6", - "from": "flexbuffer@0.0.6", - "resolved": "https://registry.npmjs.org/flexbuffer/-/flexbuffer-0.0.6.tgz" - }, "follow-redirects": { - "version": "1.6.1", - "from": "follow-redirects@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz", + "version": "1.5.10", + "from": "follow-redirects@1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", "dependencies": { "debug": { "version": "3.1.0", @@ -598,6 +741,12 @@ } } }, + "formatio": { + "version": "1.2.0", + "from": "formatio@1.2.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "dev": true + }, "formidable": { "version": "1.0.14", "from": "formidable@1.0.14", @@ -660,14 +809,20 @@ } }, "gaxios": { - "version": "1.0.7", - "from": "gaxios@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.0.7.tgz" + "version": "1.8.4", + "from": "gaxios@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz" }, "gcp-metadata": { - "version": "0.9.3", - "from": "gcp-metadata@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz" + "version": "1.0.0", + "from": "gcp-metadata@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz" + }, + "get-func-name": { + "version": "2.0.0", + "from": "get-func-name@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "dev": true }, "getpass": { "version": "0.1.7", @@ -687,15 +842,10 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz" }, "google-auth-library": { - "version": "2.0.2", - "from": "google-auth-library@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", + "version": "3.1.2", + "from": "google-auth-library@>=3.1.1 <4.0.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", "dependencies": { - "gcp-metadata": { - "version": "0.7.0", - "from": "gcp-metadata@>=0.7.0 <0.8.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz" - }, "lru-cache": { "version": "5.1.1", "from": "lru-cache@>=5.0.0 <6.0.0", @@ -704,24 +854,25 @@ } }, "google-p12-pem": { - "version": "1.0.3", + "version": "1.0.4", "from": "google-p12-pem@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.3.tgz" + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz" + }, + "growl": { + "version": "1.10.3", + "from": "growl@1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "dev": true }, "gtoken": { - "version": "2.3.0", - "from": "gtoken@>=2.3.0 <3.0.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.0.tgz", + "version": "2.3.3", + "from": "gtoken@>=2.3.2 <3.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", "dependencies": { "mime": { - "version": "2.4.0", + "version": "2.4.4", "from": "mime@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz" - }, - "pify": { - "version": "3.0.0", - "from": "pify@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz" } } }, @@ -735,6 +886,12 @@ "from": "har-validator@>=5.1.0 <5.2.0", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz" }, + "has-flag": { + "version": "2.0.0", + "from": "has-flag@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "dev": true + }, "has-unicode": { "version": "2.0.1", "from": "has-unicode@>=2.0.0 <3.0.0", @@ -746,15 +903,21 @@ "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "optional": true }, + "he": { + "version": "1.1.1", + "from": "he@1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "dev": true + }, "heap": { "version": "0.2.6", "from": "heap@>=0.2.6 <0.3.0", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" }, "hex2dec": { - "version": "1.1.1", + "version": "1.1.2", "from": "hex2dec@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.1.tgz" + "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz" }, "hoek": { "version": "0.9.1", @@ -799,15 +962,27 @@ "from": "ini@>=1.3.0 <1.4.0", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" }, + "ioredis": { + "version": "4.9.5", + "from": "ioredis@>=4.9.1 <4.10.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.9.5.tgz", + "dependencies": { + "debug": { + "version": "3.2.6", + "from": "debug@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" + } + } + }, "is": { "version": "3.3.0", "from": "is@>=3.2.0 <4.0.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz" }, "is-buffer": { - "version": "1.1.6", - "from": "is-buffer@>=1.1.5 <2.0.0", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" + "version": "2.0.3", + "from": "is-buffer@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz" }, "is-fullwidth-code-point": { "version": "1.0.0", @@ -891,15 +1066,21 @@ } } }, + "just-extend": { + "version": "4.0.2", + "from": "just-extend@>=4.0.2 <5.0.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", + "dev": true + }, "jwa": { - "version": "1.1.6", - "from": "jwa@>=1.1.5 <2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz" + "version": "1.4.1", + "from": "jwa@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" }, "jws": { - "version": "3.1.5", + "version": "3.2.2", "from": "jws@>=3.1.5 <4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz" + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" }, "keypress": { "version": "0.1.0", @@ -911,6 +1092,11 @@ "from": "line-reader@>=0.2.4 <0.3.0", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" }, + "lodash": { + "version": "4.17.11", + "from": "lodash@>=4.17.11 <5.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz" + }, "lodash.defaults": { "version": "4.2.0", "from": "lodash.defaults@>=4.2.0 <5.0.0", @@ -927,9 +1113,9 @@ "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz" }, "logger-sharelatex": { - "version": "1.6.0", - "from": "logger-sharelatex@>=1.6.0 <2.0.0", - "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.6.0.tgz", + "version": "1.7.0", + "from": "logger-sharelatex@1.7.0", + "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.7.0.tgz", "dependencies": { "assert-plus": { "version": "1.0.0", @@ -942,26 +1128,20 @@ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" }, "bunyan": { - "version": "1.5.1", - "from": "bunyan@1.5.1", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz" + "version": "1.8.12", + "from": "bunyan@1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz" }, "combined-stream": { - "version": "1.0.7", + "version": "1.0.8", "from": "combined-stream@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" }, "delayed-stream": { "version": "1.0.0", "from": "delayed-stream@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" }, - "dtrace-provider": { - "version": "0.6.0", - "from": "dtrace-provider@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", - "optional": true - }, "forever-agent": { "version": "0.6.1", "from": "forever-agent@>=0.6.1 <0.7.0", @@ -1014,6 +1194,12 @@ } } }, + "lolex": { + "version": "2.7.5", + "from": "lolex@>=2.1.2 <3.0.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "dev": true + }, "long": { "version": "4.0.0", "from": "long@>=4.0.0 <5.0.0", @@ -1034,6 +1220,12 @@ "from": "lynx@>=0.1.1 <0.2.0", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz" }, + "memorystream": { + "version": "0.3.1", + "from": "memorystream@0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "dev": true + }, "mersenne": { "version": "0.0.4", "from": "mersenne@>=0.0.3 <0.1.0", @@ -1045,9 +1237,9 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" }, "metrics-sharelatex": { - "version": "2.1.1", - "from": "metrics-sharelatex@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.1.1.tgz", + "version": "2.2.0", + "from": "metrics-sharelatex@2.2.0", + "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.2.0.tgz", "dependencies": { "coffee-script": { "version": "1.6.0", @@ -1091,11 +1283,61 @@ "from": "mkdirp@0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" }, + "mocha": { + "version": "4.1.0", + "from": "mocha@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "dev": true, + "dependencies": { + "commander": { + "version": "2.11.0", + "from": "commander@2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "dev": true + }, + "debug": { + "version": "3.1.0", + "from": "debug@3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "dev": true + }, + "glob": { + "version": "7.1.2", + "from": "glob@7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "from": "minimatch@>=3.0.4 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dev": true + }, + "ms": { + "version": "2.0.0", + "from": "ms@2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "dev": true + } + } + }, "module-details-from-path": { "version": "1.0.3", "from": "module-details-from-path@>=1.0.3 <2.0.0", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz" }, + "moment": { + "version": "2.24.0", + "from": "moment@>=2.10.6 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "optional": true + }, "mongo-uri": { "version": "0.1.2", "from": "mongo-uri@>=0.1.2 <0.2.0", @@ -1169,21 +1411,41 @@ "from": "nan@>=2.0.8 <3.0.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz" }, + "native-promise-only": { + "version": "0.8.1", + "from": "native-promise-only@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "dev": true + }, "ncp": { "version": "2.0.0", "from": "ncp@~2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "optional": true }, + "nise": { + "version": "1.5.0", + "from": "nise@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", + "dev": true, + "dependencies": { + "lolex": { + "version": "4.1.0", + "from": "lolex@>=4.1.0 <5.0.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", + "dev": true + } + } + }, "node-fetch": { - "version": "2.3.0", - "from": "node-fetch@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz" + "version": "2.6.0", + "from": "node-fetch@>=2.3.0 <3.0.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz" }, "node-forge": { - "version": "0.7.6", - "from": "node-forge@>=0.7.5 <0.8.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz" + "version": "0.8.4", + "from": "node-forge@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.4.tgz" }, "node-pre-gyp": { "version": "0.6.39", @@ -1369,14 +1631,14 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" }, "p-limit": { - "version": "2.1.0", - "from": "p-limit@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz" + "version": "2.2.0", + "from": "p-limit@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz" }, "p-try": { - "version": "2.0.0", + "version": "2.2.0", "from": "p-try@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" }, "parse-duration": { "version": "0.1.1", @@ -1389,9 +1651,9 @@ "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" }, "parse-ms": { - "version": "2.0.0", + "version": "2.1.0", "from": "parse-ms@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.0.0.tgz" + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz" }, "path-is-absolute": { "version": "1.0.1", @@ -1403,6 +1665,26 @@ "from": "path-parse@>=1.0.6 <2.0.0", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" }, + "path-to-regexp": { + "version": "1.7.0", + "from": "path-to-regexp@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "dev": true, + "dependencies": { + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "dev": true + } + } + }, + "pathval": { + "version": "1.1.0", + "from": "pathval@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "dev": true + }, "pause": { "version": "0.0.1", "from": "pause@0.0.1", @@ -1429,14 +1711,21 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" }, "prom-client": { - "version": "11.2.1", + "version": "11.5.1", "from": "prom-client@>=11.1.3 <12.0.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.2.1.tgz" + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.1.tgz" }, "protobufjs": { "version": "6.8.8", "from": "protobufjs@>=6.8.6 <6.9.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz" + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", + "dependencies": { + "@types/node": { + "version": "10.14.9", + "from": "@types/node@>=10.1.0 <11.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz" + } + } }, "psl": { "version": "1.1.31", @@ -1469,9 +1758,9 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" }, "raven": { - "version": "1.2.1", - "from": "raven@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "version": "1.1.3", + "from": "raven@1.1.3", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.1.3.tgz", "dependencies": { "cookie": { "version": "0.3.1", @@ -1517,6 +1806,11 @@ "from": "redis-errors@>=1.2.0 <2.0.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" }, + "redis-parser": { + "version": "3.0.0", + "from": "redis-parser@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" + }, "redis-sentinel": { "version": "0.1.1", "from": "redis-sentinel@0.1.1", @@ -1530,44 +1824,19 @@ } }, "redis-sharelatex": { - "version": "1.0.5", - "from": "redis-sharelatex@>=1.0.5 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.5.tgz", + "version": "1.0.8", + "from": "redis-sharelatex@1.0.8", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.8.tgz", "dependencies": { "async": { - "version": "2.6.1", + "version": "2.6.2", "from": "async@>=2.5.0 <3.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz" + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz" }, "coffee-script": { "version": "1.8.0", "from": "coffee-script@1.8.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz" - }, - "debug": { - "version": "3.2.6", - "from": "debug@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" - }, - "ioredis": { - "version": "4.6.0", - "from": "ioredis@4.6.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.6.0.tgz" - }, - "lodash": { - "version": "4.17.11", - "from": "lodash@>=4.17.10 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz" - }, - "redis": { - "version": "0.12.1", - "from": "redis@0.12.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" - }, - "redis-parser": { - "version": "3.0.0", - "from": "redis-parser@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" } } }, @@ -1671,14 +1940,20 @@ "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz" }, "require-in-the-middle": { - "version": "3.1.0", - "from": "require-in-the-middle@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-3.1.0.tgz" + "version": "4.0.0", + "from": "require-in-the-middle@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.0.tgz" + }, + "require-like": { + "version": "0.1.2", + "from": "require-like@0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "dev": true }, "resolve": { - "version": "1.9.0", - "from": "resolve@>=1.5.0 <2.0.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz" + "version": "1.11.0", + "from": "resolve@>=1.10.0 <2.0.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz" }, "resolve-from": { "version": "2.0.0", @@ -1745,6 +2020,26 @@ "from": "safer-buffer@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" }, + "samsam": { + "version": "1.3.0", + "from": "samsam@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "dev": true + }, + "sandboxed-module": { + "version": "0.3.0", + "from": "sandboxed-module@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", + "dev": true, + "dependencies": { + "stack-trace": { + "version": "0.0.6", + "from": "stack-trace@0.0.6", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "dev": true + } + } + }, "sax": { "version": "1.2.1", "from": "sax@1.2.1", @@ -1778,9 +2073,9 @@ } }, "shimmer": { - "version": "1.2.0", + "version": "1.2.1", "from": "shimmer@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz" + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz" }, "sigmund": { "version": "1.0.1", @@ -1792,6 +2087,12 @@ "from": "signal-exit@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" }, + "sinon": { + "version": "3.2.1", + "from": "sinon@>=3.2.1 <3.3.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", + "dev": true + }, "sntp": { "version": "0.2.4", "from": "sntp@>=0.2.0 <0.3.0", @@ -1831,9 +2132,9 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" }, "standard-as-callback": { - "version": "1.0.1", - "from": "standard-as-callback@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-1.0.1.tgz" + "version": "2.0.1", + "from": "standard-as-callback@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz" }, "statsd-parser": { "version": "0.0.4", @@ -1877,10 +2178,11 @@ "from": "strip-json-comments@>=2.0.1 <2.1.0", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" }, - "symbol-observable": { - "version": "1.2.0", - "from": "symbol-observable@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" + "supports-color": { + "version": "4.4.0", + "from": "supports-color@4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "dev": true }, "tar": { "version": "2.2.1", @@ -1936,6 +2238,12 @@ } } }, + "text-encoding": { + "version": "0.6.4", + "from": "text-encoding@0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "dev": true + }, "through": { "version": "2.3.8", "from": "through@>=2.2.7 <3.0.0", @@ -1951,6 +2259,12 @@ "from": "thunky@>=0.1.0 <0.2.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" }, + "timekeeper": { + "version": "0.0.4", + "from": "timekeeper@0.0.4", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", + "dev": true + }, "to-mongodb-core": { "version": "2.0.0", "from": "to-mongodb-core@>=2.0.0 <3.0.0", @@ -1981,6 +2295,12 @@ "from": "tweetnacl@>=0.14.0 <0.15.0", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" }, + "type-detect": { + "version": "4.0.8", + "from": "type-detect@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "dev": true + }, "uid-number": { "version": "0.0.6", "from": "uid-number@>=0.0.6 <0.0.7", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 31265d6972..8c632c7be4 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -29,12 +29,12 @@ "express": "3.3.5", "heap": "^0.2.6", "line-reader": "^0.2.4", - "logger-sharelatex": "^1.6.0", - "metrics-sharelatex": "^2.1.1", + "logger-sharelatex": "^1.7.0", + "metrics-sharelatex": "^2.2.0", "mongo-uri": "^0.1.2", "mongojs": "2.4.0", "redis": "~0.10.1", - "redis-sharelatex": "^1.0.5", + "redis-sharelatex": "^1.0.8", "request": "~2.33.0", "requestretry": "^1.12.0", "s3-streams": "^0.3.0", diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee index 45dcec23bf..303f71660b 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee @@ -39,6 +39,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should insert the compressed op into mongo", -> expect(@updates[0].pack[0].op).to.deep.equal [{ @@ -58,6 +59,7 @@ describe "Appending doc ops to the history", -> rclient.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, member) -> member.should.equal 0 done() + return null describe "when the history has already been started", -> beforeEach (done) -> @@ -82,6 +84,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, updates) => throw error if error? done() + return null describe "when the updates are recent and from the same user", -> beforeEach (done) -> @@ -102,6 +105,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should combine all the updates into one pack", -> expect(@updates[0].pack[1].op).to.deep.equal [{ @@ -132,6 +136,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should combine the updates into one pack", -> expect(@updates[0].pack[0].op).to.deep.equal [{ @@ -162,6 +167,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should concat the compressed op into mongo", -> expect(@updates[0].pack.length).to.deep.equal 3 # batch size is 100 @@ -190,6 +196,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should insert the compressed ops into mongo", -> expect(@updates[0].pack[0].op).to.deep.equal [{ @@ -223,6 +230,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should insert the compressed no-op into mongo", -> expect(@updates[0].pack[0].op).to.deep.equal [] @@ -252,6 +260,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should ignore the comment op", -> expect(@updates[0].pack[0].op).to.deep.equal [{d: "bar", p: 6}] @@ -275,6 +284,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should not add a expiresAt entry in the update in mongo", -> expect(@updates[0].expiresAt).to.be.undefined @@ -295,6 +305,7 @@ describe "Appending doc ops to the history", -> TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => throw error if error? done() + return null it "should add a expiresAt entry in the update in mongo", -> expect(@updates[0].expiresAt).to.exist diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 06d339795f..0ff62d70d9 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -64,6 +64,7 @@ describe "Archiving updates", -> TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> throw error if error? done() + return null after (done) -> MockWebApi.getUserInfo.restore() @@ -76,6 +77,7 @@ describe "Archiving updates", -> TrackChangesClient.pushDocHistory @project_id, @doc_id, (error) -> throw error if error? done() + return null it "should have one cached pack", (done) -> db.docHistory.count { doc_id: ObjectId(@doc_id), expiresAt:{$exists:true}}, (error, count) -> @@ -120,6 +122,7 @@ describe "Archiving updates", -> TrackChangesClient.pullDocHistory @project_id, @doc_id, (error) -> throw error if error? done() + return null it "should restore both packs", (done) -> db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee index b3fd843c6b..aaf9710d60 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee @@ -15,7 +15,7 @@ MockWebApi = require "./helpers/MockWebApi" describe "Flushing updates", -> before (done)-> TrackChangesApp.ensureRunning done - + describe "flushing a doc's updates", -> before (done) -> @project_id = ObjectId().toString() @@ -32,6 +32,7 @@ describe "Flushing updates", -> TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> throw error if error? done() + return null it "should flush the op into mongo", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> @@ -39,6 +40,7 @@ describe "Flushing updates", -> p: 3, i: "f" }] done() + return null describe "flushing a project's updates", -> describe "with versioning enabled", -> @@ -66,16 +68,19 @@ describe "Flushing updates", -> TrackChangesClient.flushProject @project_id, (error) -> throw error if error? done() + return null it "should not mark the updates for deletion", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> expect(updates[0].expiresAt).to.not.exist done() + return null it "should preserve history forever", (done) -> TrackChangesClient.getProjectMetaData @project_id, (error, project) -> expect(project.preserveHistory).to.equal true done() + return null describe "without versioning enabled", -> before (done) -> @@ -102,11 +107,13 @@ describe "Flushing updates", -> TrackChangesClient.flushProject @project_id, (error) -> throw error if error? done() + return null it "should mark the updates for deletion", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> expect(updates[0].expiresAt).to.exist done() + return null describe "without versioning enabled but with preserveHistory set to true", -> before (done) -> @@ -135,8 +142,10 @@ describe "Flushing updates", -> TrackChangesClient.flushProject @project_id, (error) -> throw error if error? done() + return null it "should not mark the updates for deletion", (done) -> TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> expect(updates[0].expiresAt).to.not.exist done() + return null diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee index 916702eecf..1e9bed197b 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee @@ -67,10 +67,12 @@ describe "Getting a diff", -> throw error if error? @diff = diff.diff done() + return null afterEach () -> MockDocUpdaterApi.getDoc.restore() MockWebApi.getUserInfo.restore() + return null it "should return the diff", -> expect(@diff).to.deep.equal @expected_diff @@ -79,3 +81,4 @@ describe "Getting a diff", -> MockDocUpdaterApi.getDoc .calledWith(@project_id, @doc_id) .should.equal true + return null diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee index e8619be06e..4a9e9586d6 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee @@ -47,14 +47,16 @@ describe "Getting updates", -> v: 2 * i + 2 } @updates[0].meta.user_id = @deleted_user_id - + TrackChangesApp.ensureRunning => TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => throw error if error? done() + return null after: () -> MockWebApi.getUserInfo.restore() + return null describe "getting updates up to the limit", -> before (done) -> @@ -62,6 +64,7 @@ describe "Getting updates", -> throw error if error? @updates = body.updates done() + return null it "should fetch the user details from the web api", -> MockWebApi.getUserInfo @@ -101,6 +104,7 @@ describe "Getting updates", -> throw error if error? @updates = body.updates done() + return null it "should return as many updates as it can", -> docs1 = {} diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee b/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee index 0468a75109..5bf01e4d9c 100644 --- a/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee +++ b/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee @@ -13,6 +13,7 @@ describe "Locking document", -> before (done)-> TrackChangesApp.ensureRunning done + return null describe "when the lock has expired in redis", -> before (done) -> @@ -29,8 +30,10 @@ describe "Locking document", -> , (error) -> # we get here after trying to release lock A done() + return null it "the new lock should not be removed by the expired locker", (done) -> LockManager.checkLock "doc123", (err, isFree) -> expect(isFree).to.equal false done() + return null diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee index 62d374c28e..647db4952b 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee @@ -61,11 +61,14 @@ describe "Restoring a version", -> TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) => throw error if error? done() + return null after () -> MockDocUpdaterApi.setDoc.restore() + return null it "should set the doc in the doc updater", -> MockDocUpdaterApi.setDoc .calledWith(@project_id, @doc_id, @restored_lines, @user_id, true) .should.equal true + return null From e84ee6ebeb21b58ca218ef868701f19868dc43f4 Mon Sep 17 00:00:00 2001 From: mserranom Date: Thu, 18 Jul 2019 09:00:47 +0000 Subject: [PATCH 443/549] removed profiler --- services/track-changes/app.coffee | 9 - services/track-changes/npm-shrinkwrap.json | 404 +-------------------- services/track-changes/package.json | 3 +- 3 files changed, 8 insertions(+), 408 deletions(-) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.coffee index 846c7e8db1..972b38b7a7 100644 --- a/services/track-changes/app.coffee +++ b/services/track-changes/app.coffee @@ -80,15 +80,6 @@ app.get "/check_lock", HttpController.checkLock app.get "/health_check", HttpController.healthCheck -profiler = require "v8-profiler" -app.get "/profile", (req, res) -> - time = parseInt(req.query.time || "1000") - profiler.startProfiling("test") - setTimeout () -> - profile = profiler.stopProfiling("test") - res.json(profile) - , time - app.use (error, req, res, next) -> logger.error err: error, req: req, "an internal error occured" res.send 500 diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json index 348128696c..fcfd97e71f 100644 --- a/services/track-changes/npm-shrinkwrap.json +++ b/services/track-changes/npm-shrinkwrap.json @@ -224,11 +224,6 @@ "from": "@types/tough-cookie@*", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz" }, - "abbrev": { - "version": "1.1.1", - "from": "abbrev@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" - }, "abort-controller": { "version": "3.0.0", "from": "abort-controller@>=3.0.0 <4.0.0", @@ -249,16 +244,6 @@ "from": "ajv@>=6.5.5 <7.0.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz" }, - "aproba": { - "version": "1.2.0", - "from": "aproba@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" - }, - "are-we-there-yet": { - "version": "1.1.5", - "from": "are-we-there-yet@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" - }, "array-from": { "version": "2.1.1", "from": "array-from@>=2.1.1 <3.0.0", @@ -354,11 +339,6 @@ "from": "bintrees@1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" }, - "block-stream": { - "version": "0.0.9", - "from": "block-stream@*", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" - }, "boom": { "version": "0.4.2", "from": "boom@>=0.4.0 <0.5.0", @@ -448,16 +428,6 @@ "from": "cluster-key-slot@>=1.0.6 <2.0.0", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz" }, - "co": { - "version": "4.6.0", - "from": "co@>=4.6.0 <5.0.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" - }, - "code-point-at": { - "version": "1.1.0", - "from": "code-point-at@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - }, "coffee-script": { "version": "1.12.4", "from": "coffee-script@1.12.4", @@ -485,11 +455,6 @@ "from": "connect@2.8.5", "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz" }, - "console-control-strings": { - "version": "1.1.0", - "from": "console-control-strings@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - }, "console-log-level": { "version": "1.4.1", "from": "console-log-level@>=1.4.0 <2.0.0", @@ -550,11 +515,6 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", "dev": true }, - "deep-extend": { - "version": "0.6.0", - "from": "deep-extend@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" - }, "delay": { "version": "4.3.0", "from": "delay@>=4.0.1 <5.0.0", @@ -566,21 +526,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", "optional": true }, - "delegates": { - "version": "1.0.0", - "from": "delegates@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - }, "denque": { "version": "1.4.1", "from": "denque@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz" }, - "detect-libc": { - "version": "1.0.3", - "from": "detect-libc@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" - }, "diff": { "version": "3.3.1", "from": "diff@3.3.1", @@ -760,53 +710,8 @@ "fs.realpath": { "version": "1.0.0", "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "fstream": { - "version": "1.0.11", - "from": "fstream@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "dependencies": { - "graceful-fs": { - "version": "4.1.15", - "from": "graceful-fs@>=4.1.2 <5.0.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.0 >=0.0.0 <1.0.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - } - } - }, - "fstream-ignore": { - "version": "1.0.5", - "from": "fstream-ignore@>=1.0.5 <2.0.0", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "dependencies": { - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - } - } - }, - "gauge": { - "version": "2.7.4", - "from": "gauge@>=2.7.3 <2.8.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - } - } + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "dev": true }, "gaxios": { "version": "1.8.4", @@ -892,11 +797,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", "dev": true }, - "has-unicode": { - "version": "2.0.1", - "from": "has-unicode@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - }, "hawk": { "version": "1.0.0", "from": "hawk@>=1.0.0 <1.1.0", @@ -957,11 +857,6 @@ "from": "inherits@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" }, - "ini": { - "version": "1.3.5", - "from": "ini@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" - }, "ioredis": { "version": "4.9.5", "from": "ioredis@>=4.9.1 <4.10.0", @@ -984,11 +879,6 @@ "from": "is-buffer@>=2.0.2 <3.0.0", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz" }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - }, "is-typedarray": { "version": "1.0.0", "from": "is-typedarray@>=1.0.0 <1.1.0", @@ -1029,21 +919,11 @@ "from": "json-schema-traverse@>=0.4.1 <0.5.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" }, - "json-stable-stringify": { - "version": "1.0.1", - "from": "json-stable-stringify@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" - }, "json-stringify-safe": { "version": "5.0.1", "from": "json-stringify-safe@5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" }, - "jsonify": { - "version": "0.0.0", - "from": "jsonify@>=0.0.0 <0.1.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - }, "jsonparse": { "version": "1.3.1", "from": "jsonparse@>=1.2.0 <2.0.0", @@ -1447,189 +1327,17 @@ "from": "node-forge@>=0.8.0 <0.9.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.4.tgz" }, - "node-pre-gyp": { - "version": "0.6.39", - "from": "node-pre-gyp@>=0.6.34 <0.7.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", - "dependencies": { - "ajv": { - "version": "4.11.8", - "from": "ajv@>=4.9.1 <5.0.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz" - }, - "assert-plus": { - "version": "0.2.0", - "from": "assert-plus@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "aws-sign2": { - "version": "0.6.0", - "from": "aws-sign2@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "boom": { - "version": "2.10.1", - "from": "boom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "combined-stream": { - "version": "1.0.7", - "from": "combined-stream@>=1.0.5 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "from": "cryptiles@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "2.1.4", - "from": "form-data@>=2.1.1 <2.2.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz" - }, - "glob": { - "version": "7.1.3", - "from": "glob@>=7.1.3 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" - }, - "har-schema": { - "version": "1.0.5", - "from": "har-schema@>=1.0.5 <2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" - }, - "har-validator": { - "version": "4.2.1", - "from": "har-validator@>=4.2.1 <4.3.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz" - }, - "hawk": { - "version": "3.1.3", - "from": "hawk@3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" - }, - "hoek": { - "version": "2.16.3", - "from": "hoek@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "http-signature": { - "version": "1.1.1", - "from": "http-signature@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" - }, - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - }, - "nopt": { - "version": "4.0.1", - "from": "nopt@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz" - }, - "oauth-sign": { - "version": "0.8.2", - "from": "oauth-sign@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" - }, - "performance-now": { - "version": "0.2.0", - "from": "performance-now@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz" - }, - "punycode": { - "version": "1.4.1", - "from": "punycode@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - }, - "qs": { - "version": "6.4.0", - "from": "qs@>=6.4.0 <6.5.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz" - }, - "request": { - "version": "2.81.0", - "from": "request@2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz" - }, - "rimraf": { - "version": "2.6.3", - "from": "rimraf@>=2.6.1 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" - }, - "sntp": { - "version": "1.0.9", - "from": "sntp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - }, - "tough-cookie": { - "version": "2.3.4", - "from": "tough-cookie@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz" - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - } - } - }, - "npmlog": { - "version": "4.1.2", - "from": "npmlog@>=4.0.2 <5.0.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" - }, - "number-is-nan": { - "version": "1.0.1", - "from": "number-is-nan@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - }, "oauth-sign": { "version": "0.3.0", "from": "oauth-sign@>=0.3.0 <0.4.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", "optional": true }, - "object-assign": { - "version": "4.1.1", - "from": "object-assign@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - }, "once": { "version": "1.4.0", "from": "once@>=1.3.0 <2.0.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" }, - "os-homedir": { - "version": "1.0.2", - "from": "os-homedir@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - }, - "os-tmpdir": { - "version": "1.0.2", - "from": "os-tmpdir@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - }, - "osenv": { - "version": "0.1.5", - "from": "osenv@>=0.1.4 <0.2.0", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" - }, "p-limit": { "version": "2.2.0", "from": "p-limit@>=2.2.0 <3.0.0", @@ -1774,18 +1482,6 @@ } } }, - "rc": { - "version": "1.2.8", - "from": "rc@>=1.1.7 <2.0.0", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "dependencies": { - "minimist": { - "version": "1.2.0", - "from": "minimist@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - } - } - }, "readable-stream": { "version": "2.3.6", "from": "readable-stream@>=2.0.0 <3.0.0", @@ -1974,16 +1670,19 @@ "version": "2.4.5", "from": "rimraf@>=2.4.0 <2.5.0", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "optional": true, "dependencies": { "glob": { "version": "6.0.4", "from": "glob@>=6.0.1 <7.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "optional": true }, "minimatch": { "version": "3.0.4", "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "optional": true } } }, @@ -2055,11 +1754,6 @@ "from": "send@0.1.4", "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz" }, - "set-blocking": { - "version": "2.0.0", - "from": "set-blocking@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - }, "settings-sharelatex": { "version": "1.1.0", "from": "settings-sharelatex@latest", @@ -2082,11 +1776,6 @@ "from": "sigmund@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" }, - "signal-exit": { - "version": "3.0.2", - "from": "signal-exit@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" - }, "sinon": { "version": "3.2.1", "from": "sinon@>=3.2.1 <3.3.0", @@ -2151,76 +1840,12 @@ "from": "string_decoder@>=1.1.1 <1.2.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" }, - "string-width": { - "version": "1.0.2", - "from": "string-width@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "from": "ansi-regex@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - }, - "strip-ansi": { - "version": "3.0.1", - "from": "strip-ansi@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - } - } - }, - "stringstream": { - "version": "0.0.6", - "from": "stringstream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz" - }, - "strip-json-comments": { - "version": "2.0.1", - "from": "strip-json-comments@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - }, "supports-color": { "version": "4.4.0", "from": "supports-color@4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", "dev": true }, - "tar": { - "version": "2.2.1", - "from": "tar@>=2.2.1 <3.0.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz" - }, - "tar-pack": { - "version": "3.4.1", - "from": "tar-pack@>=3.4.0 <4.0.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", - "dependencies": { - "debug": { - "version": "2.6.9", - "from": "debug@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - }, - "glob": { - "version": "7.1.3", - "from": "glob@>=7.1.3 <8.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" - }, - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - }, - "ms": { - "version": "2.0.0", - "from": "ms@2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - }, - "rimraf": { - "version": "2.6.3", - "from": "rimraf@>=2.5.1 <3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" - } - } - }, "tdigest": { "version": "0.1.1", "from": "tdigest@>=0.1.1 <0.2.0", @@ -2301,11 +1926,6 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "dev": true }, - "uid-number": { - "version": "0.0.6", - "from": "uid-number@>=0.0.6 <0.0.7", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" - }, "uid2": { "version": "0.0.2", "from": "uid2@0.0.2", @@ -2343,11 +1963,6 @@ "from": "uuid@3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" }, - "v8-profiler": { - "version": "5.7.0", - "from": "v8-profiler@>=5.6.5 <6.0.0", - "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz" - }, "verror": { "version": "1.10.0", "from": "verror@1.10.0", @@ -2365,11 +1980,6 @@ "from": "when@>=3.7.7 <4.0.0", "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" }, - "wide-align": { - "version": "1.1.3", - "from": "wide-align@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" - }, "wrappy": { "version": "1.0.2", "from": "wrappy@>=1.0.0 <2.0.0", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 8c632c7be4..b5320f4f5d 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -39,8 +39,7 @@ "requestretry": "^1.12.0", "s3-streams": "^0.3.0", "settings-sharelatex": "^1.1.0", - "underscore": "~1.7.0", - "v8-profiler": "^5.6.5" + "underscore": "~1.7.0" }, "devDependencies": { "bunyan": "~2.0.2", From d29ea9f527250b77c5adf22bc1aed19380027edc Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 29 Jul 2019 10:28:27 +0000 Subject: [PATCH 444/549] Fixed WEB_HOST lookup in settings --- services/track-changes/config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 779b3fbdfd..75c7e6cd9f 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -15,7 +15,7 @@ module.exports = docstore: url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" web: - url: "http://#{process.env["WEB_API_HOST"] or "localhost"}:#{process.env['WEB_API_PORT'] or 3000}" + url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}" user: "sharelatex" pass: "password" redis: From c5d9bf1093d69f37c2244a74af0b717aa50e8e2c Mon Sep 17 00:00:00 2001 From: John Lees-Miller Date: Sat, 28 Sep 2019 11:08:32 +0100 Subject: [PATCH 445/549] Update config --- services/track-changes/config/settings.defaults.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 75c7e6cd9f..95f394906b 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -16,8 +16,8 @@ module.exports = url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" web: url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}" - user: "sharelatex" - pass: "password" + user: process.env['WEB_API_USER'] or "sharelatex" + pass: process.env['WEB_API_PASSWORD'] or "password" redis: lock: host: process.env["REDIS_HOST"] or "localhost" From 4eab16d6f426731684550d6321c6ad3a250d337d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 1 Oct 2019 13:25:02 +0100 Subject: [PATCH 446/549] add continueOnError in settings --- services/track-changes/config/settings.defaults.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index 95f394906b..fa5d730f33 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -40,6 +40,7 @@ module.exports = secret: process.env['AWS_SECRET_ACCESS_KEY'] stores: doc_history: process.env['AWS_BUCKET'] + continueOnError: process.env['TRACK_CHANGES_CONTINUE_ON_ERROR'] or false path: dumpFolder: Path.join(TMP_DIR, "dumpFolder") From ebbe728ec1ba7410901fdfd91a714c9af577e1a6 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 9 Oct 2019 11:11:15 +0100 Subject: [PATCH 447/549] fix performance of update removal --- services/track-changes/app/coffee/RedisManager.coffee | 2 +- .../test/unit/coffee/RedisManager/RedisManagerTests.coffee | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.coffee index 56dcfd4372..b23a308fc5 100644 --- a/services/track-changes/app/coffee/RedisManager.coffee +++ b/services/track-changes/app/coffee/RedisManager.coffee @@ -21,7 +21,7 @@ module.exports = RedisManager = multi = rclient.multi() # Delete all the updates which have been applied (exact match) for update in docUpdates or [] - multi.lrem Keys.uncompressedHistoryOps({doc_id}), 0, update + multi.lrem Keys.uncompressedHistoryOps({doc_id}), 1, update multi.exec (error, results) -> return callback(error) if error? # It's ok to delete the doc_id from the set here. Even though the list diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee index be20b38d21..b7009f5d74 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee @@ -56,12 +56,12 @@ describe "RedisManager", -> it "should delete the first update from redis", -> @rclient.lrem - .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @jsonUpdates[0]) + .calledWith("UncompressedHistoryOps:#{@doc_id}", 1, @jsonUpdates[0]) .should.equal true it "should delete the second update from redis", -> @rclient.lrem - .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @jsonUpdates[1]) + .calledWith("UncompressedHistoryOps:#{@doc_id}", 1, @jsonUpdates[1]) .should.equal true it "should delete the doc from the set of docs with history ops", -> From e3b3492f444af47ca3e8dfe0c36528561a521ebc Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Wed, 30 Oct 2019 10:26:18 -0400 Subject: [PATCH 448/549] add public link to contributing docs --- services/track-changes/.github/PULL_REQUEST_TEMPLATE.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md b/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md index ed25ee83c1..12bb2eeb3f 100644 --- a/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md +++ b/services/track-changes/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,7 @@ - + + + + ### Description From 4d67c846b981fe909b630a5bdfd18dcec748cfcc Mon Sep 17 00:00:00 2001 From: Nate Stemen Date: Wed, 30 Oct 2019 10:26:38 -0400 Subject: [PATCH 449/549] bump build script to 1.1.24 --- services/track-changes/Makefile | 4 ++-- services/track-changes/buildscript.txt | 4 +++- services/track-changes/docker-compose.ci.yml | 2 +- services/track-changes/docker-compose.yml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 6fb3b47b4f..3103e21778 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.21 +# Version: 1.1.24 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -38,7 +38,7 @@ test_clean: $(DOCKER_COMPOSE) down -v -t 0 test_acceptance_pre_run: - @[ ! -f test/acceptance/scripts/pre-run ] && echo "track-changes has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run + @[ ! -f test/acceptance/js/scripts/pre-run ] && echo "track-changes has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/js/scripts/pre-run build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ --tag gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 80e4a36482..3b302838c3 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -5,4 +5,6 @@ track-changes --dependencies=mongo,redis --docker-repos=gcr.io/overleaf-ops --build-target=docker ---script-version=1.1.21 +--script-version=1.1.24 +--env-pass-through= +--public-repo=True diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index baac7bccae..05d153e982 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.21 +# Version: 1.1.24 version: "2" diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 18fa7aed5e..a9e1132aa6 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.21 +# Version: 1.1.24 version: "2" From 9e28ceb9dbdf0dbb2858d4c6f0a19217460ae0c4 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Fri, 24 Jan 2020 18:30:03 +0100 Subject: [PATCH 450/549] [misc] test/acceptance: do not hard code the logging level There is an environment variable LOG_LEVEL to set it. --- .../test/acceptance/coffee/helpers/TrackChangesApp.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee index 187427f56d..e46d35768a 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee @@ -1,5 +1,5 @@ app = require('../../../../app') -require("logger-sharelatex").logger.level("info") +require("logger-sharelatex") logger = require("logger-sharelatex") Settings = require("settings-sharelatex") From 265e30df3d1d8279a4cca4f6595a8cc7b419c4ec Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 27 Jan 2020 12:17:52 +0100 Subject: [PATCH 451/549] [misc] update build scripts to version 1.3.3 --- services/track-changes/Dockerfile | 5 ++++ services/track-changes/Jenkinsfile | 1 + services/track-changes/Makefile | 9 ++++++- services/track-changes/buildscript.txt | 6 ++--- services/track-changes/docker-compose.ci.yml | 14 +++++------ services/track-changes/docker-compose.yml | 25 +++++++------------- 6 files changed, 32 insertions(+), 28 deletions(-) diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 187f37be2c..5f69a4721f 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -1,3 +1,8 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.3.3 + FROM node:6.11.2 as app WORKDIR /app diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 4b85bb04e6..b1ea80110c 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -16,6 +16,7 @@ pipeline { } stages { + stage('Install') { steps { withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 3103e21778..becf4c28f9 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.24 +# Version: 1.3.3 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -31,14 +31,20 @@ test_unit: test_acceptance: test_clean test_acceptance_pre_run test_acceptance_run +test_acceptance_debug: test_clean test_acceptance_pre_run test_acceptance_run_debug + test_acceptance_run: @[ ! -d test/acceptance ] && echo "track-changes has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance +test_acceptance_run_debug: + @[ ! -d test/acceptance ] && echo "track-changes has no acceptance tests" || $(DOCKER_COMPOSE) run -p 127.0.0.9:19999:19999 --rm test_acceptance npm run test:acceptance -- --inspect=0.0.0.0:19999 --inspect-brk + test_clean: $(DOCKER_COMPOSE) down -v -t 0 test_acceptance_pre_run: @[ ! -f test/acceptance/js/scripts/pre-run ] && echo "track-changes has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/js/scripts/pre-run + build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ --tag gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ @@ -51,4 +57,5 @@ publish: docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 3b302838c3..98b6e9a4a4 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,10 +1,10 @@ track-changes +--public-repo=True --language=coffeescript +--env-add= --node-version=6.11.2 --acceptance-creds=aws --dependencies=mongo,redis --docker-repos=gcr.io/overleaf-ops ---build-target=docker ---script-version=1.1.24 --env-pass-through= ---public-repo=True +--script-version=1.3.3 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 05d153e982..d3fd49dfae 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,9 +1,9 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.24 +# Version: 1.3.3 -version: "2" +version: "2.1" services: test_unit: @@ -28,13 +28,14 @@ services: MOCHA_GREP: ${MOCHA_GREP} NODE_ENV: test depends_on: - - mongo - - redis + mongo: + condition: service_healthy + redis: + condition: service_healthy user: node command: npm run test:acceptance:_run - tar: build: . image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER @@ -42,9 +43,8 @@ services: - ./:/tmp/build/ command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . user: root - redis: image: redis mongo: - image: mongo:3.4 + image: mongo:3.6 diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index a9e1132aa6..450979f592 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,9 +1,9 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.24 +# Version: 1.3.3 -version: "2" +version: "2.1" services: test_unit: @@ -18,7 +18,7 @@ services: user: node test_acceptance: - build: . + image: node:6.11.2 volumes: - .:/app working_dir: /app @@ -35,24 +35,15 @@ services: NODE_ENV: test user: node depends_on: - - mongo - - redis + mongo: + condition: service_healthy + redis: + condition: service_healthy command: npm run test:acceptance - - - tar: - build: . - image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER - volumes: - - ./:/tmp/build/ - command: tar -czf /tmp/build/build.tar.gz --exclude=build.tar.gz --exclude-vcs . - user: root - redis: image: redis mongo: - image: mongo:3.4 - + image: mongo:3.6 From b0b96ee014b90e665e89cda372458fe3a3ee6b08 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Fri, 24 Jan 2020 18:31:56 +0100 Subject: [PATCH 452/549] [misc] add support for offline s3 tests update the build scripts to 1.3.4 --- services/track-changes/Dockerfile | 2 +- services/track-changes/Jenkinsfile | 4 +- services/track-changes/Makefile | 5 +- .../track-changes/app/coffee/MongoAWS.coffee | 2 + services/track-changes/buildscript.txt | 8 +-- .../config/settings.defaults.coffee | 2 + services/track-changes/docker-compose.ci.yml | 18 +++++-- services/track-changes/docker-compose.yml | 18 +++++-- services/track-changes/package.json | 2 +- .../coffee/ArchivingUpdatesTests.coffee | 2 +- .../coffee/helpers/TrackChangesClient.coffee | 49 +++++++++++-------- 11 files changed, 69 insertions(+), 43 deletions(-) diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 5f69a4721f..3066d5d478 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.3 +# Version: 1.3.4 FROM node:6.11.2 as app diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index b1ea80110c..23be6d5d33 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -45,9 +45,7 @@ pipeline { stage('Acceptance Tests') { steps { - withCredentials([usernamePassword(credentialsId: 'S3_DOCSTORE_TEST_AWS_KEYS', passwordVariable: 'AWS_SECRET_ACCESS_KEY', usernameVariable: 'AWS_ACCESS_KEY_ID')]) { - sh 'AWS_BUCKET="sl-acceptance-tests" AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' - } + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' } } diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index becf4c28f9..6be6b7acb2 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.3 +# Version: 1.3.4 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -11,9 +11,6 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ BRANCH_NAME=$(BRANCH_NAME) \ PROJECT_NAME=$(PROJECT_NAME) \ MOCHA_GREP=${MOCHA_GREP} \ - AWS_BUCKET=${AWS_BUCKET} \ - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ docker-compose ${DOCKER_COMPOSE_FLAGS} clean: diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.coffee index 4308763a7a..0223a89981 100644 --- a/services/track-changes/app/coffee/MongoAWS.coffee +++ b/services/track-changes/app/coffee/MongoAWS.coffee @@ -14,6 +14,8 @@ createStream = (streamConstructor, project_id, doc_id, pack_id) -> AWS_CONFIG = accessKeyId: settings.trackchanges.s3.key secretAccessKey: settings.trackchanges.s3.secret + endpoint: settings.trackchanges.s3.endpoint + s3ForcePathStyle: settings.trackchanges.s3.pathStyle return streamConstructor new AWS.S3(AWS_CONFIG), { "Bucket": settings.trackchanges.stores.doc_history, diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 98b6e9a4a4..83d66e52e5 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,10 +1,10 @@ track-changes --public-repo=True --language=coffeescript ---env-add= +--env-add=AWS_BUCKET=bucket --node-version=6.11.2 ---acceptance-creds=aws ---dependencies=mongo,redis +--acceptance-creds=None +--dependencies=mongo,redis,s3 --docker-repos=gcr.io/overleaf-ops --env-pass-through= ---script-version=1.3.3 +--script-version=1.3.4 diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.coffee index fa5d730f33..897c5a0536 100755 --- a/services/track-changes/config/settings.defaults.coffee +++ b/services/track-changes/config/settings.defaults.coffee @@ -38,6 +38,8 @@ module.exports = s3: key: process.env['AWS_ACCESS_KEY_ID'] secret: process.env['AWS_SECRET_ACCESS_KEY'] + endpoint: process.env['AWS_S3_ENDPOINT'] + pathStyle: process.env['AWS_S3_PATH_STYLE'] == 'true' stores: doc_history: process.env['AWS_BUCKET'] continueOnError: process.env['TRACK_CHANGES_CONTINUE_ON_ERROR'] or false diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index d3fd49dfae..58033f8156 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.3 +# Version: 1.3.4 version: "2.1" @@ -22,16 +22,20 @@ services: REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} - AWS_BUCKET: ${AWS_BUCKET} + AWS_S3_ENDPOINT: http://s3:9090 + AWS_S3_PATH_STYLE: 'true' + AWS_ACCESS_KEY_ID: fake + AWS_SECRET_ACCESS_KEY: fake MOCHA_GREP: ${MOCHA_GREP} NODE_ENV: test + AWS_BUCKET: bucket depends_on: mongo: condition: service_healthy redis: condition: service_healthy + s3: + condition: service_healthy user: node command: npm run test:acceptance:_run @@ -48,3 +52,9 @@ services: mongo: image: mongo:3.6 + s3: + image: adobe/s3mock + environment: + - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9090"] diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 450979f592..d9bee3a71b 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.3 +# Version: 1.3.4 version: "2.1" @@ -27,18 +27,22 @@ services: REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} - AWS_BUCKET: ${AWS_BUCKET} + AWS_S3_ENDPOINT: http://s3:9090 + AWS_S3_PATH_STYLE: 'true' + AWS_ACCESS_KEY_ID: fake + AWS_SECRET_ACCESS_KEY: fake MOCHA_GREP: ${MOCHA_GREP} LOG_LEVEL: ERROR NODE_ENV: test + AWS_BUCKET: bucket user: node depends_on: mongo: condition: service_healthy redis: condition: service_healthy + s3: + condition: service_healthy command: npm run test:acceptance redis: @@ -47,3 +51,9 @@ services: mongo: image: mongo:3.6 + s3: + image: adobe/s3mock + environment: + - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9090"] diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b5320f4f5d..72e3e7dbed 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -9,7 +9,7 @@ "scripts": { "compile:app": "([ -e app/coffee ] && coffee -m $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee -m $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY AWS_BUCKET=$AWS_BUCKET AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 0ff62d70d9..25397cfa61 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -17,7 +17,7 @@ MockWebApi = require "./helpers/MockWebApi" describe "Archiving updates", -> before (done) -> if Settings?.trackchanges?.s3?.key.length < 1 - message = "s3 keys not setup, this test setup will fail" + message = new Error("s3 keys not setup, this test setup will fail") return done(message) @now = Date.now() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index e927333004..9f17939656 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -6,6 +6,15 @@ rclient = require("redis-sharelatex").createClient(Settings.redis.history) # Onl Keys = Settings.redis.history.key_schema {db, ObjectId} = require "../../../../app/js/mongojs" +aws = require "aws-sdk" +s3 = new aws.S3( + accessKeyId: Settings.trackchanges.s3.key + secretAccessKey: Settings.trackchanges.s3.secret + endpoint: Settings.trackchanges.s3.endpoint + s3ForcePathStyle: Settings.trackchanges.s3.pathStyle +) +S3_BUCKET = Settings.trackchanges.stores.doc_history + module.exports = TrackChangesClient = flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> TrackChangesClient.flushDoc project_id, doc_id, (error) -> @@ -91,32 +100,30 @@ module.exports = TrackChangesClient = response.statusCode.should.equal 204 callback(error) - buildS3Options: (content, key)-> - return { - aws: - key: Settings.trackchanges.s3.key - secret: Settings.trackchanges.s3.secret - bucket: Settings.trackchanges.stores.doc_history - timeout: 30 * 1000 - json: content - uri:"https://#{Settings.trackchanges.stores.doc_history}.s3.amazonaws.com/#{key}" - } - getS3Doc: (project_id, doc_id, pack_id, callback = (error, body) ->) -> - options = TrackChangesClient.buildS3Options(true, project_id+"/changes-"+doc_id+"/pack-"+pack_id) - options.encoding = null - request.get options, (err, res, body) -> + params = + Bucket: S3_BUCKET + Key: "#{project_id}/changes-#{doc_id}/pack-#{pack_id}" + + s3.getObject params, (error, data) -> return callback(error) if error? + body = data.Body return callback(new Error("empty response from s3")) if not body? zlib.gunzip body, (err, result) -> return callback(err) if err? callback(null, JSON.parse(result.toString())) removeS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> - options = TrackChangesClient.buildS3Options(true, "?prefix=" + project_id + "/changes-" +doc_id) - request.get options, (error, res, body) -> - keys = body.match /[0-9a-f]{24}\/changes-[0-9a-f]{24}\/pack-[0-9a-f]{24}/g - async.eachSeries keys, (key, cb) -> - options = TrackChangesClient.buildS3Options(true, key) - request.del options, cb - , callback + params = + Bucket: S3_BUCKET + Prefix: "#{project_id}/changes-#{doc_id}" + + s3.listObjects params, (error, data) -> + return callback(error) if error? + + params = + Bucket: S3_BUCKET + Delete: + Objects: data.Contents.map((s3object) -> {Key: s3object.Key}) + + s3.deleteObjects params, callback From ca668ad657c1e5370a4193e9502ff13977191d71 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Fri, 24 Jan 2020 19:13:29 +0100 Subject: [PATCH 453/549] [misc] test/acceptance: wait for the s3 container to start --- .../coffee/ArchivingUpdatesTests.coffee | 3 +++ .../coffee/helpers/TrackChangesClient.coffee | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee index 25397cfa61..282aaec2f5 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee @@ -20,6 +20,9 @@ describe "Archiving updates", -> message = new Error("s3 keys not setup, this test setup will fail") return done(message) + TrackChangesClient.waitForS3 done + + before (done) -> @now = Date.now() @to = @now @user_id = ObjectId().toString() diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee index 9f17939656..1ae07426b2 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee @@ -100,6 +100,21 @@ module.exports = TrackChangesClient = response.statusCode.should.equal 204 callback(error) + waitForS3: (done, retries=42) -> + if !Settings.trackchanges.s3.endpoint + return done() + + request.get "#{Settings.trackchanges.s3.endpoint}/", (err, res) -> + if res && res.statusCode < 500 + return done() + + if retries == 0 + return done(err or new Error("s3 returned #{res.statusCode}")) + + setTimeout () -> + TrackChangesClient.waitForS3(done, --retries) + , 1000 + getS3Doc: (project_id, doc_id, pack_id, callback = (error, body) ->) -> params = Bucket: S3_BUCKET From 732860a49e9afbf194a22e9bf0968c8eca212068 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 27 Jan 2020 12:24:45 +0100 Subject: [PATCH 454/549] [misc] upgrade node to version 10.18.1 --- services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 4 ++-- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index e18a34b9d6..fd26bfb7c5 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -6.11.2 +10.18.1 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 3066d5d478..27a4a72016 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -3,7 +3,7 @@ # https://github.com/sharelatex/sharelatex-dev-environment # Version: 1.3.4 -FROM node:6.11.2 as app +FROM node:10.18.1 as app WORKDIR /app @@ -17,7 +17,7 @@ COPY . /app RUN npm run compile:all -FROM node:6.11.2 +FROM node:10.18.1 COPY --from=app /app /app diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 83d66e52e5..bca7c5e461 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -2,7 +2,7 @@ track-changes --public-repo=True --language=coffeescript --env-add=AWS_BUCKET=bucket ---node-version=6.11.2 +--node-version=10.18.1 --acceptance-creds=None --dependencies=mongo,redis,s3 --docker-repos=gcr.io/overleaf-ops diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index d9bee3a71b..eced5ceb02 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -7,7 +7,7 @@ version: "2.1" services: test_unit: - image: node:6.11.2 + image: node:10.18.1 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: node:6.11.2 + image: node:10.18.1 volumes: - .:/app working_dir: /app From 86a5c27833cabe5e7c419150faf756f6fba306a9 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 4 Feb 2020 16:09:56 +0000 Subject: [PATCH 455/549] upgrade to local node:10.18.1 image --- services/track-changes/Dockerfile | 4 ++-- services/track-changes/docker-compose.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 27a4a72016..73bc05b211 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -3,7 +3,7 @@ # https://github.com/sharelatex/sharelatex-dev-environment # Version: 1.3.4 -FROM node:10.18.1 as app +FROM gcr.io/overleaf-ops/node:10.18.1 as app WORKDIR /app @@ -17,7 +17,7 @@ COPY . /app RUN npm run compile:all -FROM node:10.18.1 +FROM gcr.io/overleaf-ops/node:10.18.1 COPY --from=app /app /app diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index eced5ceb02..f428e4574c 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -7,7 +7,7 @@ version: "2.1" services: test_unit: - image: node:10.18.1 + image: gcr.io/overleaf-ops/node:10.18.1 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: node:10.18.1 + image: gcr.io/overleaf-ops/node:10.18.1 volumes: - .:/app working_dir: /app @@ -28,7 +28,7 @@ services: MONGO_HOST: mongo POSTGRES_HOST: postgres AWS_S3_ENDPOINT: http://s3:9090 - AWS_S3_PATH_STYLE: 'true' + AWS_S3_PATH_STYLE: "true" AWS_ACCESS_KEY_ID: fake AWS_SECRET_ACCESS_KEY: fake MOCHA_GREP: ${MOCHA_GREP} From efb5f1a0571a67a67cccee7368109a8eabbe1b85 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Thu, 6 Feb 2020 03:36:52 +0000 Subject: [PATCH 456/549] update to gcr.io/overleaf-ops/node:10.19.0 --- services/track-changes/Dockerfile | 4 ++-- services/track-changes/docker-compose.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 73bc05b211..9e4dc5fe96 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -3,7 +3,7 @@ # https://github.com/sharelatex/sharelatex-dev-environment # Version: 1.3.4 -FROM gcr.io/overleaf-ops/node:10.18.1 as app +FROM gcr.io/overleaf-ops/node:10.19.0 as app WORKDIR /app @@ -17,7 +17,7 @@ COPY . /app RUN npm run compile:all -FROM gcr.io/overleaf-ops/node:10.18.1 +FROM gcr.io/overleaf-ops/node:10.19.0 COPY --from=app /app /app diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index f428e4574c..f95c088267 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -7,7 +7,7 @@ version: "2.1" services: test_unit: - image: gcr.io/overleaf-ops/node:10.18.1 + image: gcr.io/overleaf-ops/node:10.19.0 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: gcr.io/overleaf-ops/node:10.18.1 + image: gcr.io/overleaf-ops/node:10.19.0 volumes: - .:/app working_dir: /app From bb033c479fec028e3ebdb7e7a4c02407889c2621 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Fri, 7 Feb 2020 14:15:41 +0000 Subject: [PATCH 457/549] use public node:10.19.0 image --- services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 4 ++-- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index fd26bfb7c5..5b7269c0a9 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -10.18.1 +10.19.0 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 9e4dc5fe96..31e35b1529 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -3,7 +3,7 @@ # https://github.com/sharelatex/sharelatex-dev-environment # Version: 1.3.4 -FROM gcr.io/overleaf-ops/node:10.19.0 as app +FROM node:10.19.0 as app WORKDIR /app @@ -17,7 +17,7 @@ COPY . /app RUN npm run compile:all -FROM gcr.io/overleaf-ops/node:10.19.0 +FROM node:10.19.0 COPY --from=app /app /app diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index bca7c5e461..7da4826a03 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -2,7 +2,7 @@ track-changes --public-repo=True --language=coffeescript --env-add=AWS_BUCKET=bucket ---node-version=10.18.1 +--node-version=10.19.0 --acceptance-creds=None --dependencies=mongo,redis,s3 --docker-repos=gcr.io/overleaf-ops diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index f95c088267..74d0acc683 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -7,7 +7,7 @@ version: "2.1" services: test_unit: - image: gcr.io/overleaf-ops/node:10.19.0 + image: node:10.19.0 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: gcr.io/overleaf-ops/node:10.19.0 + image: node:10.19.0 volumes: - .:/app working_dir: /app From 04e0960f0558c1c1e0e6f971b193a39706719c29 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 10 Feb 2020 17:10:40 +0100 Subject: [PATCH 458/549] [misc] update the build scripts to 1.3.5 --- services/track-changes/Dockerfile | 10 +++++----- services/track-changes/Makefile | 2 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.ci.yml | 4 ++-- services/track-changes/docker-compose.yml | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 31e35b1529..e538fb48d9 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -1,12 +1,14 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.4 +# Version: 1.3.5 -FROM node:10.19.0 as app +FROM node:10.19.0 as base WORKDIR /app +FROM base as app + #wildcard as some files may not be in all repos COPY package*.json npm-shrink*.json /app/ @@ -17,11 +19,9 @@ COPY . /app RUN npm run compile:all -FROM node:10.19.0 +FROM base COPY --from=app /app /app - -WORKDIR /app USER node CMD ["node", "--expose-gc", "app.js"] diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 6be6b7acb2..b139b47573 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.4 +# Version: 1.3.5 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 7da4826a03..039db45c26 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -7,4 +7,4 @@ track-changes --dependencies=mongo,redis,s3 --docker-repos=gcr.io/overleaf-ops --env-pass-through= ---script-version=1.3.4 +--script-version=1.3.5 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 58033f8156..e9c3f7ecbf 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,9 +1,9 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.4 +# Version: 1.3.5 -version: "2.1" +version: "2.3" services: test_unit: diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 74d0acc683..300d7b45ec 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,9 +1,9 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.4 +# Version: 1.3.5 -version: "2.1" +version: "2.3" services: test_unit: @@ -28,7 +28,7 @@ services: MONGO_HOST: mongo POSTGRES_HOST: postgres AWS_S3_ENDPOINT: http://s3:9090 - AWS_S3_PATH_STYLE: "true" + AWS_S3_PATH_STYLE: 'true' AWS_ACCESS_KEY_ID: fake AWS_SECRET_ACCESS_KEY: fake MOCHA_GREP: ${MOCHA_GREP} From 883c0e83cffb127083af05bc94032afb1e97960d Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 12 Feb 2020 12:37:01 +0000 Subject: [PATCH 459/549] remove unused .travis.yml file --- services/track-changes/.travis.yml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 services/track-changes/.travis.yml diff --git a/services/track-changes/.travis.yml b/services/track-changes/.travis.yml deleted file mode 100644 index ccad90efe3..0000000000 --- a/services/track-changes/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js - -before_install: - - npm install -g grunt-cli - -install: - - npm install - - grunt install - -before_script: - - grunt forever:app:start - -script: - - grunt test:unit - -services: - - redis-server - - mongodb From 660c9145ad27d12ec9ec4abcf60a1f5ec608f4b8 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Wed, 12 Feb 2020 14:39:52 +0100 Subject: [PATCH 460/549] [misc] rename npm-shrinkwrap.json to package-lock.json and run npm i --- services/track-changes/npm-shrinkwrap.json | 2009 -------------- services/track-changes/package-lock.json | 2791 ++++++++++++++++++++ 2 files changed, 2791 insertions(+), 2009 deletions(-) delete mode 100644 services/track-changes/npm-shrinkwrap.json create mode 100644 services/track-changes/package-lock.json diff --git a/services/track-changes/npm-shrinkwrap.json b/services/track-changes/npm-shrinkwrap.json deleted file mode 100644 index fcfd97e71f..0000000000 --- a/services/track-changes/npm-shrinkwrap.json +++ /dev/null @@ -1,2009 +0,0 @@ -{ - "name": "history-sharelatex", - "version": "0.1.4", - "dependencies": { - "@google-cloud/common": { - "version": "0.32.1", - "from": "@google-cloud/common@>=0.32.0 <0.33.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz" - }, - "@google-cloud/debug-agent": { - "version": "3.2.0", - "from": "@google-cloud/debug-agent@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.2.0.tgz", - "dependencies": { - "coffeescript": { - "version": "2.4.1", - "from": "coffeescript@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.4.1.tgz" - }, - "semver": { - "version": "6.1.1", - "from": "semver@>=6.0.0 <7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz" - } - } - }, - "@google-cloud/profiler": { - "version": "0.2.3", - "from": "@google-cloud/profiler@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-0.2.3.tgz", - "dependencies": { - "@google-cloud/common": { - "version": "0.26.2", - "from": "@google-cloud/common@>=0.26.0 <0.27.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz" - }, - "@google-cloud/promisify": { - "version": "0.3.1", - "from": "@google-cloud/promisify@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz" - }, - "arrify": { - "version": "1.0.1", - "from": "arrify@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" - }, - "gcp-metadata": { - "version": "0.9.3", - "from": "gcp-metadata@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz" - }, - "google-auth-library": { - "version": "2.0.2", - "from": "google-auth-library@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", - "dependencies": { - "gcp-metadata": { - "version": "0.7.0", - "from": "gcp-metadata@>=0.7.0 <0.8.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz" - } - } - }, - "lru-cache": { - "version": "5.1.1", - "from": "lru-cache@^5.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" - }, - "through2": { - "version": "3.0.1", - "from": "through2@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz" - } - } - }, - "@google-cloud/projectify": { - "version": "0.3.3", - "from": "@google-cloud/projectify@>=0.3.3 <0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz" - }, - "@google-cloud/promisify": { - "version": "0.4.0", - "from": "@google-cloud/promisify@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz" - }, - "@google-cloud/trace-agent": { - "version": "3.6.1", - "from": "@google-cloud/trace-agent@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.6.1.tgz", - "dependencies": { - "methods": { - "version": "1.1.2", - "from": "methods@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" - }, - "semver": { - "version": "6.1.1", - "from": "semver@^6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz" - } - } - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "from": "@protobufjs/aspromise@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "from": "@protobufjs/base64@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "from": "@protobufjs/codegen@>=2.0.4 <3.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "from": "@protobufjs/eventemitter@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "from": "@protobufjs/fetch@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" - }, - "@protobufjs/float": { - "version": "1.0.2", - "from": "@protobufjs/float@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "from": "@protobufjs/inquire@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" - }, - "@protobufjs/path": { - "version": "1.1.2", - "from": "@protobufjs/path@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "from": "@protobufjs/pool@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "from": "@protobufjs/utf8@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" - }, - "@sindresorhus/is": { - "version": "0.15.0", - "from": "@sindresorhus/is@>=0.15.0 <0.16.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.15.0.tgz" - }, - "@sinonjs/commons": { - "version": "1.4.0", - "from": "@sinonjs/commons@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", - "dev": true - }, - "@sinonjs/formatio": { - "version": "3.2.1", - "from": "@sinonjs/formatio@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", - "dev": true - }, - "@sinonjs/samsam": { - "version": "3.3.2", - "from": "@sinonjs/samsam@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", - "dev": true - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "from": "@sinonjs/text-encoding@>=0.7.1 <0.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "dev": true - }, - "@types/caseless": { - "version": "0.12.2", - "from": "@types/caseless@*", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz" - }, - "@types/console-log-level": { - "version": "1.4.0", - "from": "@types/console-log-level@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz" - }, - "@types/duplexify": { - "version": "3.6.0", - "from": "@types/duplexify@>=3.5.0 <4.0.0", - "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.0.tgz" - }, - "@types/form-data": { - "version": "2.2.1", - "from": "@types/form-data@*", - "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz" - }, - "@types/long": { - "version": "4.0.0", - "from": "@types/long@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz" - }, - "@types/node": { - "version": "12.0.8", - "from": "@types/node@*", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz" - }, - "@types/request": { - "version": "2.48.1", - "from": "@types/request@>=2.47.0 <3.0.0", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz" - }, - "@types/semver": { - "version": "5.5.0", - "from": "@types/semver@>=5.5.0 <6.0.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz" - }, - "@types/tough-cookie": { - "version": "2.3.5", - "from": "@types/tough-cookie@*", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz" - }, - "abort-controller": { - "version": "3.0.0", - "from": "abort-controller@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" - }, - "acorn": { - "version": "6.1.1", - "from": "acorn@>=6.0.0 <7.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz" - }, - "agent-base": { - "version": "4.3.0", - "from": "agent-base@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz" - }, - "ajv": { - "version": "6.6.2", - "from": "ajv@>=6.5.5 <7.0.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz" - }, - "array-from": { - "version": "2.1.1", - "from": "array-from@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "dev": true - }, - "arrify": { - "version": "2.0.1", - "from": "arrify@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" - }, - "asn1": { - "version": "0.1.11", - "from": "asn1@0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", - "optional": true - }, - "assert-plus": { - "version": "0.1.5", - "from": "assert-plus@>=0.1.5 <0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "optional": true - }, - "assertion-error": { - "version": "1.1.0", - "from": "assertion-error@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "dev": true - }, - "async": { - "version": "0.2.10", - "from": "async@>=0.2.10 <0.3.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" - }, - "async-listener": { - "version": "0.6.10", - "from": "async-listener@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz" - }, - "asynckit": { - "version": "0.4.0", - "from": "asynckit@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - }, - "aws-sdk": { - "version": "2.384.0", - "from": "aws-sdk@>=2.102.0 <3.0.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.384.0.tgz" - }, - "aws-sign2": { - "version": "0.5.0", - "from": "aws-sign2@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", - "optional": true - }, - "aws4": { - "version": "1.8.0", - "from": "aws4@>=1.8.0 <2.0.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz" - }, - "axios": { - "version": "0.18.1", - "from": "axios@>=0.18.0 <0.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz" - }, - "balanced-match": { - "version": "1.0.0", - "from": "balanced-match@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" - }, - "base64-js": { - "version": "1.3.0", - "from": "base64-js@>=1.0.2 <2.0.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" - }, - "bignumber.js": { - "version": "7.2.1", - "from": "bignumber.js@>=7.0.0 <8.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz" - }, - "bindings": { - "version": "1.5.0", - "from": "bindings@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" - }, - "bintrees": { - "version": "1.0.1", - "from": "bintrees@1.0.1", - "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" - }, - "boom": { - "version": "0.4.2", - "from": "boom@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" - }, - "brace-expansion": { - "version": "1.1.11", - "from": "brace-expansion@>=1.1.7 <2.0.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" - }, - "browser-stdout": { - "version": "1.3.0", - "from": "browser-stdout@1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "dev": true - }, - "bson": { - "version": "0.4.23", - "from": "bson@>=0.4.20 <0.5.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz" - }, - "buffer": { - "version": "4.9.1", - "from": "buffer@4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz" - }, - "buffer-crc32": { - "version": "0.2.1", - "from": "buffer-crc32@0.2.1", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "from": "buffer-equal-constant-time@1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" - }, - "buffer-shims": { - "version": "1.0.0", - "from": "buffer-shims@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "builtin-modules": { - "version": "3.1.0", - "from": "builtin-modules@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz" - }, - "bunyan": { - "version": "2.0.2", - "from": "bunyan@>=2.0.2 <2.1.0", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", - "dev": true - }, - "byline": { - "version": "4.2.2", - "from": "byline@>=4.2.1 <5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz" - }, - "bytes": { - "version": "0.2.0", - "from": "bytes@0.2.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" - }, - "caseless": { - "version": "0.12.0", - "from": "caseless@>=0.12.0 <0.13.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - }, - "chai": { - "version": "4.1.2", - "from": "chai@>=4.1.1 <4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "from": "check-error@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "dev": true - }, - "cli": { - "version": "0.6.6", - "from": "cli@>=0.6.6 <0.7.0", - "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz" - }, - "cluster-key-slot": { - "version": "1.0.12", - "from": "cluster-key-slot@>=1.0.6 <2.0.0", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz" - }, - "coffee-script": { - "version": "1.12.4", - "from": "coffee-script@1.12.4", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz", - "dev": true - }, - "combined-stream": { - "version": "0.0.7", - "from": "combined-stream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "optional": true - }, - "commander": { - "version": "1.2.0", - "from": "commander@1.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz" - }, - "concat-map": { - "version": "0.0.1", - "from": "concat-map@0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - }, - "connect": { - "version": "2.8.5", - "from": "connect@2.8.5", - "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz" - }, - "console-log-level": { - "version": "1.4.1", - "from": "console-log-level@>=1.4.0 <2.0.0", - "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz" - }, - "continuation-local-storage": { - "version": "3.2.1", - "from": "continuation-local-storage@>=3.2.1 <4.0.0", - "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz" - }, - "cookie": { - "version": "0.1.0", - "from": "cookie@0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" - }, - "cookie-signature": { - "version": "1.0.1", - "from": "cookie-signature@1.0.1", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "cryptiles": { - "version": "0.2.2", - "from": "cryptiles@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", - "optional": true - }, - "ctype": { - "version": "0.5.3", - "from": "ctype@0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", - "optional": true - }, - "dashdash": { - "version": "1.14.1", - "from": "dashdash@>=1.12.0 <2.0.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "debug": { - "version": "4.1.1", - "from": "debug@*", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz" - }, - "deep-eql": { - "version": "3.0.1", - "from": "deep-eql@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "dev": true - }, - "delay": { - "version": "4.3.0", - "from": "delay@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz" - }, - "delayed-stream": { - "version": "0.0.5", - "from": "delayed-stream@0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "optional": true - }, - "denque": { - "version": "1.4.1", - "from": "denque@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz" - }, - "diff": { - "version": "3.3.1", - "from": "diff@3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "dev": true - }, - "dtrace-provider": { - "version": "0.8.7", - "from": "dtrace-provider@>=0.8.0 <0.9.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", - "optional": true - }, - "duplexify": { - "version": "3.7.1", - "from": "duplexify@>=3.6.0 <4.0.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz" - }, - "each-series": { - "version": "1.0.0", - "from": "each-series@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz" - }, - "ecc-jsbn": { - "version": "0.1.2", - "from": "ecc-jsbn@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "from": "ecdsa-sig-formatter@1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" - }, - "emitter-listener": { - "version": "1.1.2", - "from": "emitter-listener@>=1.1.1 <2.0.0", - "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz" - }, - "end-of-stream": { - "version": "1.4.1", - "from": "end-of-stream@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz" - }, - "ent": { - "version": "2.2.0", - "from": "ent@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" - }, - "es6-promise": { - "version": "4.2.8", - "from": "es6-promise@>=4.0.3 <5.0.0", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" - }, - "es6-promisify": { - "version": "5.0.0", - "from": "es6-promisify@>=5.0.0 <6.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" - }, - "escape-string-regexp": { - "version": "1.0.5", - "from": "escape-string-regexp@1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "dev": true - }, - "event-target-shim": { - "version": "5.0.1", - "from": "event-target-shim@>=5.0.0 <6.0.0", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" - }, - "events": { - "version": "1.1.1", - "from": "events@1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz" - }, - "exeunt": { - "version": "1.1.0", - "from": "exeunt@1.1.0", - "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", - "dev": true - }, - "exit": { - "version": "0.1.2", - "from": "exit@0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" - }, - "express": { - "version": "3.3.5", - "from": "express@3.3.5", - "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz" - }, - "extend": { - "version": "3.0.2", - "from": "extend@>=3.0.1 <4.0.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" - }, - "extsprintf": { - "version": "1.3.0", - "from": "extsprintf@1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" - }, - "fast-deep-equal": { - "version": "2.0.1", - "from": "fast-deep-equal@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "from": "fast-json-stable-stringify@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz" - }, - "fast-text-encoding": { - "version": "1.0.0", - "from": "fast-text-encoding@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz" - }, - "file-uri-to-path": { - "version": "1.0.0", - "from": "file-uri-to-path@1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" - }, - "findit2": { - "version": "2.2.3", - "from": "findit2@>=2.2.3 <3.0.0", - "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz" - }, - "follow-redirects": { - "version": "1.5.10", - "from": "follow-redirects@1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "dependencies": { - "debug": { - "version": "3.1.0", - "from": "debug@3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" - }, - "ms": { - "version": "2.0.0", - "from": "ms@2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - } - } - }, - "forever-agent": { - "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" - }, - "form-data": { - "version": "0.1.4", - "from": "form-data@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", - "optional": true, - "dependencies": { - "async": { - "version": "0.9.2", - "from": "async@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "optional": true - } - } - }, - "formatio": { - "version": "1.2.0", - "from": "formatio@1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "dev": true - }, - "formidable": { - "version": "1.0.14", - "from": "formidable@1.0.14", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" - }, - "fresh": { - "version": "0.2.0", - "from": "fresh@0.2.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" - }, - "fs.realpath": { - "version": "1.0.0", - "from": "fs.realpath@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "dev": true - }, - "gaxios": { - "version": "1.8.4", - "from": "gaxios@>=1.2.1 <2.0.0", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz" - }, - "gcp-metadata": { - "version": "1.0.0", - "from": "gcp-metadata@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz" - }, - "get-func-name": { - "version": "2.0.0", - "from": "get-func-name@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "from": "getpass@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "glob": { - "version": "3.2.11", - "from": "glob@>=3.2.1 <3.3.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz" - }, - "google-auth-library": { - "version": "3.1.2", - "from": "google-auth-library@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "from": "lru-cache@>=5.0.0 <6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" - } - } - }, - "google-p12-pem": { - "version": "1.0.4", - "from": "google-p12-pem@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz" - }, - "growl": { - "version": "1.10.3", - "from": "growl@1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "dev": true - }, - "gtoken": { - "version": "2.3.3", - "from": "gtoken@>=2.3.2 <3.0.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "dependencies": { - "mime": { - "version": "2.4.4", - "from": "mime@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz" - } - } - }, - "har-schema": { - "version": "2.0.0", - "from": "har-schema@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - }, - "har-validator": { - "version": "5.1.3", - "from": "har-validator@>=5.1.0 <5.2.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz" - }, - "has-flag": { - "version": "2.0.0", - "from": "has-flag@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "dev": true - }, - "hawk": { - "version": "1.0.0", - "from": "hawk@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", - "optional": true - }, - "he": { - "version": "1.1.1", - "from": "he@1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "dev": true - }, - "heap": { - "version": "0.2.6", - "from": "heap@>=0.2.6 <0.3.0", - "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz" - }, - "hex2dec": { - "version": "1.1.2", - "from": "hex2dec@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz" - }, - "hoek": { - "version": "0.9.1", - "from": "hoek@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" - }, - "http-signature": { - "version": "0.10.1", - "from": "http-signature@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", - "optional": true - }, - "https-proxy-agent": { - "version": "2.2.1", - "from": "https-proxy-agent@>=2.2.1 <3.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "dependencies": { - "debug": { - "version": "3.2.6", - "from": "debug@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" - } - } - }, - "ieee754": { - "version": "1.1.8", - "from": "ieee754@1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz" - }, - "inflight": { - "version": "1.0.6", - "from": "inflight@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - }, - "inherits": { - "version": "2.0.3", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - }, - "ioredis": { - "version": "4.9.5", - "from": "ioredis@>=4.9.1 <4.10.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.9.5.tgz", - "dependencies": { - "debug": { - "version": "3.2.6", - "from": "debug@>=3.1.0 <4.0.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" - } - } - }, - "is": { - "version": "3.3.0", - "from": "is@>=3.2.0 <4.0.0", - "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz" - }, - "is-buffer": { - "version": "2.0.3", - "from": "is-buffer@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz" - }, - "is-typedarray": { - "version": "1.0.0", - "from": "is-typedarray@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isarray": { - "version": "1.0.0", - "from": "isarray@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "from": "isstream@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "jmespath": { - "version": "0.15.0", - "from": "jmespath@0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz" - }, - "jsbn": { - "version": "0.1.1", - "from": "jsbn@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - }, - "json-bigint": { - "version": "0.3.0", - "from": "json-bigint@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz" - }, - "json-schema": { - "version": "0.2.3", - "from": "json-schema@0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - }, - "json-schema-traverse": { - "version": "0.4.1", - "from": "json-schema-traverse@>=0.4.1 <0.5.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "from": "json-stringify-safe@5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "jsonparse": { - "version": "1.3.1", - "from": "jsonparse@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" - }, - "JSONStream": { - "version": "1.3.5", - "from": "JSONStream@>=1.0.4 <2.0.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" - }, - "jsprim": { - "version": "1.4.1", - "from": "jsprim@>=1.2.2 <2.0.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "just-extend": { - "version": "4.0.2", - "from": "just-extend@>=4.0.2 <5.0.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "dev": true - }, - "jwa": { - "version": "1.4.1", - "from": "jwa@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" - }, - "jws": { - "version": "3.2.2", - "from": "jws@>=3.1.5 <4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" - }, - "keypress": { - "version": "0.1.0", - "from": "keypress@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz" - }, - "line-reader": { - "version": "0.2.4", - "from": "line-reader@>=0.2.4 <0.3.0", - "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz" - }, - "lodash": { - "version": "4.17.11", - "from": "lodash@>=4.17.11 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz" - }, - "lodash.defaults": { - "version": "4.2.0", - "from": "lodash.defaults@>=4.2.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" - }, - "lodash.flatten": { - "version": "4.4.0", - "from": "lodash.flatten@>=4.4.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" - }, - "lodash.pickby": { - "version": "4.6.0", - "from": "lodash.pickby@>=4.6.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz" - }, - "logger-sharelatex": { - "version": "1.7.0", - "from": "logger-sharelatex@1.7.0", - "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.7.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "aws-sign2": { - "version": "0.7.0", - "from": "aws-sign2@>=0.7.0 <0.8.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - }, - "bunyan": { - "version": "1.8.12", - "from": "bunyan@1.8.12", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz" - }, - "combined-stream": { - "version": "1.0.8", - "from": "combined-stream@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" - }, - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "2.3.3", - "from": "form-data@>=2.3.2 <2.4.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - }, - "http-signature": { - "version": "1.2.0", - "from": "http-signature@>=1.2.0 <1.3.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - }, - "oauth-sign": { - "version": "0.9.0", - "from": "oauth-sign@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" - }, - "punycode": { - "version": "1.4.1", - "from": "punycode@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - }, - "qs": { - "version": "6.5.2", - "from": "qs@>=6.5.2 <6.6.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" - }, - "request": { - "version": "2.88.0", - "from": "request@>=2.88.0 <3.0.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz" - }, - "tough-cookie": { - "version": "2.4.3", - "from": "tough-cookie@>=2.4.3 <2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz" - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - }, - "uuid": { - "version": "3.3.2", - "from": "uuid@>=3.3.2 <4.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" - } - } - }, - "lolex": { - "version": "2.7.5", - "from": "lolex@>=2.1.2 <3.0.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "dev": true - }, - "long": { - "version": "4.0.0", - "from": "long@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz" - }, - "lru-cache": { - "version": "2.7.3", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" - }, - "lsmod": { - "version": "1.0.0", - "from": "lsmod@1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz" - }, - "lynx": { - "version": "0.1.1", - "from": "lynx@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz" - }, - "memorystream": { - "version": "0.3.1", - "from": "memorystream@0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "dev": true - }, - "mersenne": { - "version": "0.0.4", - "from": "mersenne@>=0.0.3 <0.1.0", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz" - }, - "methods": { - "version": "0.0.1", - "from": "methods@0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" - }, - "metrics-sharelatex": { - "version": "2.2.0", - "from": "metrics-sharelatex@2.2.0", - "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.2.0.tgz", - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" - }, - "underscore": { - "version": "1.6.0", - "from": "underscore@>=1.6.0 <1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" - } - } - }, - "mime": { - "version": "1.2.11", - "from": "mime@>=1.2.9 <1.3.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" - }, - "mime-db": { - "version": "1.37.0", - "from": "mime-db@>=1.37.0 <1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz" - }, - "mime-types": { - "version": "2.1.21", - "from": "mime-types@>=2.1.19 <2.2.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz" - }, - "minimatch": { - "version": "0.3.0", - "from": "minimatch@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz" - }, - "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - }, - "mkdirp": { - "version": "0.3.5", - "from": "mkdirp@0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" - }, - "mocha": { - "version": "4.1.0", - "from": "mocha@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "dev": true, - "dependencies": { - "commander": { - "version": "2.11.0", - "from": "commander@2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "dev": true - }, - "debug": { - "version": "3.1.0", - "from": "debug@3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "dev": true - }, - "glob": { - "version": "7.1.2", - "from": "glob@7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=3.0.4 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "dev": true - }, - "ms": { - "version": "2.0.0", - "from": "ms@2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "dev": true - } - } - }, - "module-details-from-path": { - "version": "1.0.3", - "from": "module-details-from-path@>=1.0.3 <2.0.0", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz" - }, - "moment": { - "version": "2.24.0", - "from": "moment@>=2.10.6 <3.0.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "optional": true - }, - "mongo-uri": { - "version": "0.1.2", - "from": "mongo-uri@>=0.1.2 <0.2.0", - "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz" - }, - "mongodb": { - "version": "2.2.36", - "from": "mongodb@>=2.0.45 <3.0.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.36.tgz", - "dependencies": { - "es6-promise": { - "version": "3.2.1", - "from": "es6-promise@3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "from": "process-nextick-args@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "readable-stream": { - "version": "2.2.7", - "from": "readable-stream@2.2.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz" - }, - "string_decoder": { - "version": "1.0.3", - "from": "string_decoder@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" - } - } - }, - "mongodb-core": { - "version": "2.1.20", - "from": "mongodb-core@2.1.20", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz", - "dependencies": { - "bson": { - "version": "1.0.9", - "from": "bson@>=1.0.4 <1.1.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz" - } - } - }, - "mongojs": { - "version": "2.4.0", - "from": "mongojs@2.4.0", - "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.4.0.tgz" - }, - "ms": { - "version": "2.1.1", - "from": "ms@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" - }, - "mv": { - "version": "2.1.1", - "from": "mv@~2", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "optional": true, - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "from": "mkdirp@>=0.5.1 <0.6.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "optional": true - } - } - }, - "nan": { - "version": "2.12.1", - "from": "nan@>=2.0.8 <3.0.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz" - }, - "native-promise-only": { - "version": "0.8.1", - "from": "native-promise-only@>=0.8.1 <0.9.0", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "dev": true - }, - "ncp": { - "version": "2.0.0", - "from": "ncp@~2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "optional": true - }, - "nise": { - "version": "1.5.0", - "from": "nise@>=1.0.1 <2.0.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", - "dev": true, - "dependencies": { - "lolex": { - "version": "4.1.0", - "from": "lolex@>=4.1.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", - "dev": true - } - } - }, - "node-fetch": { - "version": "2.6.0", - "from": "node-fetch@>=2.3.0 <3.0.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz" - }, - "node-forge": { - "version": "0.8.4", - "from": "node-forge@>=0.8.0 <0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.4.tgz" - }, - "oauth-sign": { - "version": "0.3.0", - "from": "oauth-sign@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", - "optional": true - }, - "once": { - "version": "1.4.0", - "from": "once@>=1.3.0 <2.0.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - }, - "p-limit": { - "version": "2.2.0", - "from": "p-limit@>=2.2.0 <3.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz" - }, - "p-try": { - "version": "2.2.0", - "from": "p-try@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" - }, - "parse-duration": { - "version": "0.1.1", - "from": "parse-duration@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.1.tgz" - }, - "parse-mongo-url": { - "version": "1.1.1", - "from": "parse-mongo-url@>=1.1.0 <2.0.0", - "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz" - }, - "parse-ms": { - "version": "2.1.0", - "from": "parse-ms@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz" - }, - "path-is-absolute": { - "version": "1.0.1", - "from": "path-is-absolute@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - }, - "path-parse": { - "version": "1.0.6", - "from": "path-parse@>=1.0.6 <2.0.0", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" - }, - "path-to-regexp": { - "version": "1.7.0", - "from": "path-to-regexp@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "dev": true, - "dependencies": { - "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "dev": true - } - } - }, - "pathval": { - "version": "1.1.0", - "from": "pathval@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "dev": true - }, - "pause": { - "version": "0.0.1", - "from": "pause@0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" - }, - "performance-now": { - "version": "2.1.0", - "from": "performance-now@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" - }, - "pify": { - "version": "4.0.1", - "from": "pify@>=4.0.1 <5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - }, - "pretty-ms": { - "version": "4.0.0", - "from": "pretty-ms@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz" - }, - "process-nextick-args": { - "version": "2.0.0", - "from": "process-nextick-args@>=2.0.0 <2.1.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" - }, - "prom-client": { - "version": "11.5.1", - "from": "prom-client@>=11.1.3 <12.0.0", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.1.tgz" - }, - "protobufjs": { - "version": "6.8.8", - "from": "protobufjs@>=6.8.6 <6.9.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "dependencies": { - "@types/node": { - "version": "10.14.9", - "from": "@types/node@>=10.1.0 <11.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz" - } - } - }, - "psl": { - "version": "1.1.31", - "from": "psl@>=1.1.28 <2.0.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz" - }, - "punycode": { - "version": "1.3.2", - "from": "punycode@1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - }, - "q": { - "version": "0.9.2", - "from": "q@0.9.2", - "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz" - }, - "qs": { - "version": "0.6.5", - "from": "qs@0.6.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" - }, - "querystring": { - "version": "0.2.0", - "from": "querystring@0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - }, - "range-parser": { - "version": "0.0.4", - "from": "range-parser@0.0.4", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" - }, - "raven": { - "version": "1.1.3", - "from": "raven@1.1.3", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.1.3.tgz", - "dependencies": { - "cookie": { - "version": "0.3.1", - "from": "cookie@0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" - }, - "uuid": { - "version": "3.0.0", - "from": "uuid@3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" - } - } - }, - "readable-stream": { - "version": "2.3.6", - "from": "readable-stream@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" - }, - "redis": { - "version": "0.10.3", - "from": "redis@>=0.10.1 <0.11.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz" - }, - "redis-commands": { - "version": "1.4.0", - "from": "redis-commands@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz" - }, - "redis-errors": { - "version": "1.2.0", - "from": "redis-errors@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" - }, - "redis-parser": { - "version": "3.0.0", - "from": "redis-parser@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" - }, - "redis-sentinel": { - "version": "0.1.1", - "from": "redis-sentinel@0.1.1", - "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", - "dependencies": { - "redis": { - "version": "0.11.0", - "from": "redis@>=0.11.0 <0.12.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz" - } - } - }, - "redis-sharelatex": { - "version": "1.0.8", - "from": "redis-sharelatex@1.0.8", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.8.tgz", - "dependencies": { - "async": { - "version": "2.6.2", - "from": "async@>=2.5.0 <3.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz" - }, - "coffee-script": { - "version": "1.8.0", - "from": "coffee-script@1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz" - } - } - }, - "request": { - "version": "2.33.0", - "from": "request@>=2.33.0 <2.34.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", - "dependencies": { - "node-uuid": { - "version": "1.4.8", - "from": "node-uuid@>=1.4.0 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz" - } - } - }, - "requestretry": { - "version": "1.13.0", - "from": "requestretry@>=1.12.0 <2.0.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - }, - "aws-sign2": { - "version": "0.7.0", - "from": "aws-sign2@>=0.7.0 <0.8.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - }, - "combined-stream": { - "version": "1.0.7", - "from": "combined-stream@>=1.0.6 <1.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz" - }, - "delayed-stream": { - "version": "1.0.0", - "from": "delayed-stream@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "from": "forever-agent@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "2.3.3", - "from": "form-data@>=2.3.2 <2.4.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - }, - "http-signature": { - "version": "1.2.0", - "from": "http-signature@>=1.2.0 <1.3.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - }, - "lodash": { - "version": "4.17.11", - "from": "lodash@>=4.15.0 <5.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz" - }, - "oauth-sign": { - "version": "0.9.0", - "from": "oauth-sign@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" - }, - "punycode": { - "version": "1.4.1", - "from": "punycode@>=1.4.1 <2.0.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - }, - "qs": { - "version": "6.5.2", - "from": "qs@>=6.5.2 <6.6.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" - }, - "request": { - "version": "2.88.0", - "from": "request@>=2.74.0 <3.0.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz" - }, - "tough-cookie": { - "version": "2.4.3", - "from": "tough-cookie@>=2.4.3 <2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz" - }, - "tunnel-agent": { - "version": "0.6.0", - "from": "tunnel-agent@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - }, - "uuid": { - "version": "3.3.2", - "from": "uuid@>=3.3.2 <4.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" - } - } - }, - "require_optional": { - "version": "1.0.1", - "from": "require_optional@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz" - }, - "require-in-the-middle": { - "version": "4.0.0", - "from": "require-in-the-middle@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.0.tgz" - }, - "require-like": { - "version": "0.1.2", - "from": "require-like@0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "dev": true - }, - "resolve": { - "version": "1.11.0", - "from": "resolve@>=1.10.0 <2.0.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz" - }, - "resolve-from": { - "version": "2.0.0", - "from": "resolve-from@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz" - }, - "retry-axios": { - "version": "0.3.2", - "from": "retry-axios@0.3.2", - "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz" - }, - "retry-request": { - "version": "4.0.0", - "from": "retry-request@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz" - }, - "rimraf": { - "version": "2.4.5", - "from": "rimraf@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "optional": true, - "dependencies": { - "glob": { - "version": "6.0.4", - "from": "glob@>=6.0.1 <7.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "from": "minimatch@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "optional": true - } - } - }, - "s3-streams": { - "version": "0.3.0", - "from": "s3-streams@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", - "dependencies": { - "bluebird": { - "version": "2.11.0", - "from": "bluebird@>=2.9.27 <3.0.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz" - }, - "lodash": { - "version": "3.10.1", - "from": "lodash@>=3.9.3 <4.0.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "from": "safe-buffer@>=5.1.1 <5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - }, - "safe-json-stringify": { - "version": "1.2.0", - "from": "safe-json-stringify@~1", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "from": "safer-buffer@>=2.0.2 <3.0.0", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" - }, - "samsam": { - "version": "1.3.0", - "from": "samsam@>=1.1.3 <2.0.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "dev": true - }, - "sandboxed-module": { - "version": "0.3.0", - "from": "sandboxed-module@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", - "dev": true, - "dependencies": { - "stack-trace": { - "version": "0.0.6", - "from": "stack-trace@0.0.6", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", - "dev": true - } - } - }, - "sax": { - "version": "1.2.1", - "from": "sax@1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz" - }, - "semver": { - "version": "5.6.0", - "from": "semver@>=5.5.0 <6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz" - }, - "send": { - "version": "0.1.4", - "from": "send@0.1.4", - "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz" - }, - "settings-sharelatex": { - "version": "1.1.0", - "from": "settings-sharelatex@latest", - "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "from": "coffee-script@1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" - } - } - }, - "shimmer": { - "version": "1.2.1", - "from": "shimmer@>=1.2.0 <2.0.0", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz" - }, - "sigmund": { - "version": "1.0.1", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" - }, - "sinon": { - "version": "3.2.1", - "from": "sinon@>=3.2.1 <3.3.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", - "dev": true - }, - "sntp": { - "version": "0.2.4", - "from": "sntp@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", - "optional": true - }, - "source-map": { - "version": "0.6.1", - "from": "source-map@>=0.6.1 <0.7.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - }, - "split": { - "version": "1.0.1", - "from": "split@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz" - }, - "sshpk": { - "version": "1.16.0", - "from": "sshpk@>=1.7.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", - "dependencies": { - "asn1": { - "version": "0.2.4", - "from": "asn1@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" - }, - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "stack-trace": { - "version": "0.0.9", - "from": "stack-trace@0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" - }, - "standard-as-callback": { - "version": "2.0.1", - "from": "standard-as-callback@>=2.0.1 <3.0.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz" - }, - "statsd-parser": { - "version": "0.0.4", - "from": "statsd-parser@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" - }, - "stream-shift": { - "version": "1.0.0", - "from": "stream-shift@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz" - }, - "string_decoder": { - "version": "1.1.1", - "from": "string_decoder@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - }, - "supports-color": { - "version": "4.4.0", - "from": "supports-color@4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "dev": true - }, - "tdigest": { - "version": "0.1.1", - "from": "tdigest@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz" - }, - "teeny-request": { - "version": "3.11.3", - "from": "teeny-request@>=3.11.1 <4.0.0", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", - "dependencies": { - "uuid": { - "version": "3.3.2", - "from": "uuid@>=3.3.2 <4.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz" - } - } - }, - "text-encoding": { - "version": "0.6.4", - "from": "text-encoding@0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "dev": true - }, - "through": { - "version": "2.3.8", - "from": "through@>=2.2.7 <3.0.0", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - }, - "through2": { - "version": "2.0.5", - "from": "through2@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - }, - "thunky": { - "version": "0.1.0", - "from": "thunky@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz" - }, - "timekeeper": { - "version": "0.0.4", - "from": "timekeeper@0.0.4", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", - "dev": true - }, - "to-mongodb-core": { - "version": "2.0.0", - "from": "to-mongodb-core@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz" - }, - "tough-cookie": { - "version": "2.5.0", - "from": "tough-cookie@>=0.12.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "optional": true, - "dependencies": { - "punycode": { - "version": "2.1.1", - "from": "punycode@>=2.1.1 <3.0.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "optional": true - } - } - }, - "tunnel-agent": { - "version": "0.3.0", - "from": "tunnel-agent@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", - "optional": true - }, - "tweetnacl": { - "version": "0.14.5", - "from": "tweetnacl@>=0.14.0 <0.15.0", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - }, - "type-detect": { - "version": "4.0.8", - "from": "type-detect@>=4.0.0 <5.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "dev": true - }, - "uid2": { - "version": "0.0.2", - "from": "uid2@0.0.2", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" - }, - "underscore": { - "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" - }, - "uri-js": { - "version": "4.2.2", - "from": "uri-js@>=4.2.2 <5.0.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "dependencies": { - "punycode": { - "version": "2.1.1", - "from": "punycode@>=2.1.0 <3.0.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - } - } - }, - "url": { - "version": "0.10.3", - "from": "url@0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "from": "util-deprecate@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - }, - "uuid": { - "version": "3.1.0", - "from": "uuid@3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" - }, - "verror": { - "version": "1.10.0", - "from": "verror@1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "from": "assert-plus@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "when": { - "version": "3.7.8", - "from": "when@>=3.7.7 <4.0.0", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz" - }, - "wrappy": { - "version": "1.0.2", - "from": "wrappy@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - }, - "xml2js": { - "version": "0.4.19", - "from": "xml2js@0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz" - }, - "xmlbuilder": { - "version": "9.0.7", - "from": "xmlbuilder@>=9.0.1 <9.1.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz" - }, - "xtend": { - "version": "4.0.1", - "from": "xtend@>=4.0.1 <4.1.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - }, - "yallist": { - "version": "3.0.3", - "from": "yallist@>=3.0.2 <4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz" - } - } -} diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json new file mode 100644 index 0000000000..a6466ed433 --- /dev/null +++ b/services/track-changes/package-lock.json @@ -0,0 +1,2791 @@ +{ + "name": "history-sharelatex", + "version": "0.1.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@google-cloud/common": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", + "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", + "requires": { + "@google-cloud/projectify": "^0.3.3", + "@google-cloud/promisify": "^0.4.0", + "@types/request": "^2.48.1", + "arrify": "^2.0.0", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^3.1.1", + "pify": "^4.0.1", + "retry-request": "^4.0.0", + "teeny-request": "^3.11.3" + } + }, + "@google-cloud/debug-agent": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.2.0.tgz", + "integrity": "sha512-fP87kYbS6aeDna08BivwQ1J260mwJGchRi99XdWCgqbRwuFac8ul0OT5i2wEeDSc5QaDX8ZuWQQ0igZvh1rTyQ==", + "requires": { + "@google-cloud/common": "^0.32.0", + "@sindresorhus/is": "^0.15.0", + "acorn": "^6.0.0", + "coffeescript": "^2.0.0", + "console-log-level": "^1.4.0", + "extend": "^3.0.1", + "findit2": "^2.2.3", + "gcp-metadata": "^1.0.0", + "lodash.pickby": "^4.6.0", + "p-limit": "^2.2.0", + "pify": "^4.0.1", + "semver": "^6.0.0", + "source-map": "^0.6.1", + "split": "^1.0.0" + }, + "dependencies": { + "coffeescript": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.4.1.tgz", + "integrity": "sha512-34GV1aHrsMpTaO3KfMJL40ZNuvKDR/g98THHnE9bQj8HjMaZvSrLik99WWqyMhRtbe8V5hpx5iLgdcSvM/S2wg==" + }, + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==" + } + } + }, + "@google-cloud/profiler": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-0.2.3.tgz", + "integrity": "sha512-rNvtrFtIebIxZEJ/O0t8n7HciZGIXBo8DvHxWqAmsCaeLvkTtsaL6HmPkwxrNQ1IhbYWAxF+E/DwCiHyhKmgTg==", + "requires": { + "@google-cloud/common": "^0.26.0", + "@types/console-log-level": "^1.4.0", + "@types/semver": "^5.5.0", + "bindings": "^1.2.1", + "console-log-level": "^1.4.0", + "delay": "^4.0.1", + "extend": "^3.0.1", + "gcp-metadata": "^0.9.0", + "nan": "^2.11.1", + "parse-duration": "^0.1.1", + "pify": "^4.0.0", + "pretty-ms": "^4.0.0", + "protobufjs": "~6.8.6", + "semver": "^5.5.0", + "teeny-request": "^3.3.0" + }, + "dependencies": { + "@google-cloud/common": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz", + "integrity": "sha512-xJ2M/q3MrUbnYZuFlpF01caAlEhAUoRn0NXp93Hn3pkFpfSOG8YfbKbpBAHvcKVbBOAKVIwPsleNtuyuabUwLQ==", + "requires": { + "@google-cloud/projectify": "^0.3.2", + "@google-cloud/promisify": "^0.3.0", + "@types/duplexify": "^3.5.0", + "@types/request": "^2.47.0", + "arrify": "^1.0.1", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.1", + "google-auth-library": "^2.0.0", + "pify": "^4.0.0", + "retry-request": "^4.0.0", + "through2": "^3.0.0" + } + }, + "@google-cloud/promisify": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz", + "integrity": "sha512-QzB0/IMvB0eFxFK7Eqh+bfC8NLv3E9ScjWQrPOk6GgfNroxcVITdTlT8NRsRrcp5+QQJVPLkRqKG0PUdaWXmHw==" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "gcp-metadata": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", + "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", + "requires": { + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" + } + }, + "google-auth-library": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", + "integrity": "sha512-FURxmo1hBVmcfLauuMRKOPYAPKht3dGuI2wjeJFalDUThO0HoYVjr4yxt5cgYSFm1dgUpmN9G/poa7ceTFAIiA==", + "requires": { + "axios": "^0.18.0", + "gcp-metadata": "^0.7.0", + "gtoken": "^2.3.0", + "https-proxy-agent": "^2.2.1", + "jws": "^3.1.5", + "lru-cache": "^5.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "gcp-metadata": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", + "integrity": "sha512-ffjC09amcDWjh3VZdkDngIo7WoluyC5Ag9PAYxZbmQLOLNI8lvPtoKTSCyU54j2gwy5roZh6sSMTfkY2ct7K3g==", + "requires": { + "axios": "^0.18.0", + "extend": "^3.0.1", + "retry-axios": "0.3.2" + } + } + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "@google-cloud/projectify": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", + "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" + }, + "@google-cloud/promisify": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", + "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==" + }, + "@google-cloud/trace-agent": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.6.1.tgz", + "integrity": "sha512-KDo85aPN4gSxJ7oEIOlKd7aGENZFXAM1kbIn1Ds+61gh/K1CQWSyepgJo3nUpAwH6D1ezDWV7Iaf8ueoITc8Uw==", + "requires": { + "@google-cloud/common": "^0.32.1", + "builtin-modules": "^3.0.0", + "console-log-level": "^1.4.0", + "continuation-local-storage": "^3.2.1", + "extend": "^3.0.0", + "gcp-metadata": "^1.0.0", + "hex2dec": "^1.0.1", + "is": "^3.2.0", + "methods": "^1.1.1", + "require-in-the-middle": "^4.0.0", + "semver": "^6.0.0", + "shimmer": "^1.2.0", + "uuid": "^3.0.1" + }, + "dependencies": { + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "semver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==" + } + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "@sindresorhus/is": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.15.0.tgz", + "integrity": "sha512-lu8BpxjAtRCAo5ifytTpCPCj99LF7o/2Myn+NXyNCBqvPYn7Pjd76AMmUB5l7XF1U6t0hcWrlEM5ESufW7wAeA==" + }, + "@sinonjs/commons": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", + "integrity": "sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/formatio": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", + "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "@sinonjs/samsam": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", + "integrity": "sha512-ILO/rR8LfAb60Y1Yfp9vxfYAASK43NFC2mLzpvLUbCQY/Qu8YwReboseu8aheCEkyElZF2L2T9mHcR2bgdvZyA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.0.2", + "array-from": "^2.1.1", + "lodash": "^4.17.11" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" + }, + "@types/console-log-level": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz", + "integrity": "sha512-x+OscEQwcx5Biair4enH7ov9W+clcqUWaZRaxn5IkT4yNWWjRr2oiYDkY/x1uXSTVZOQ2xlbFQySaQGB+VdXGQ==" + }, + "@types/duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-5zOA53RUlzN74bvrSGwjudssD9F3a797sDZQkiYpUOxW+WHaXTCPz4/d5Dgi6FKnOqZ2CpaTo0DhgIfsXAOE/A==", + "requires": { + "@types/node": "*" + } + }, + "@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", + "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" + }, + "@types/node": { + "version": "12.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz", + "integrity": "sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==" + }, + "@types/request": { + "version": "2.48.1", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", + "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", + "requires": { + "@types/caseless": "*", + "@types/form-data": "*", + "@types/node": "*", + "@types/tough-cookie": "*" + } + }, + "@types/semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" + }, + "@types/tough-cookie": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", + "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==" + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "ajv": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "dev": true + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, + "asn1": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", + "optional": true + }, + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", + "optional": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "async-listener": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "requires": { + "semver": "^5.3.0", + "shimmer": "^1.1.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sdk": { + "version": "2.384.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.384.0.tgz", + "integrity": "sha512-E+pIOWFNhQH7GCkOl5GU+Vl42MlaKtAq0Yenaa2fRGult9097u7TnUx45V1pNKMCN9RnEFWQy3ZH1TEPEYJ0fw==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.1.0", + "xml2js": "0.4.19" + } + }, + "aws-sign2": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", + "optional": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + }, + "boom": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", + "optional": true, + "requires": { + "hoek": "0.9.x" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "bson": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz", + "integrity": "sha1-5louPHUH/63kEJvHV1p25Q+NqRU=" + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-crc32": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", + "integrity": "sha1-vj5TgvwCttYySVasGvmKqYsIU0w=" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==" + }, + "bunyan": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", + "integrity": "sha1-jbsP6xoyC5JVvEK6LURgrWwEUCg=", + "dev": true, + "requires": { + "dtrace-provider": "~0.8", + "exeunt": "1.1.0", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "byline": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz", + "integrity": "sha1-wgOpilsCkIIqk4anjtosvVvNsy8=" + }, + "bytes": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", + "integrity": "sha1-qtM+wU49wsp06OfUUfm6BTrU96A=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "integrity": "sha1-Aq1Eo4Cr8nraxebwzdewQ9dMU+M=", + "requires": { + "exit": "0.1.2", + "glob": "~ 3.2.1" + } + }, + "cluster-key-slot": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz", + "integrity": "sha512-21O0kGmvED5OJ7ZTdqQ5lQQ+sjuez33R+d35jZKLwqUb5mqcPHUsxOSzj61+LHVtxGZd1kShbQM3MjB/gBJkVg==" + }, + "coffee-script": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz", + "integrity": "sha1-/hvO2X/h+zknuZjytFYW4GWL4f8=", + "dev": true + }, + "combined-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", + "optional": true, + "requires": { + "delayed-stream": "0.0.5" + } + }, + "commander": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", + "integrity": "sha1-/VcTv6FTx9bMWZN4patMRcU1Ap4=", + "requires": { + "keypress": "0.1.x" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "connect": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", + "integrity": "sha1-IFcgd7ofYm/bdAsK1waPkTDXAbg=", + "requires": { + "buffer-crc32": "0.2.1", + "bytes": "0.2.0", + "cookie": "0.1.0", + "cookie-signature": "1.0.1", + "debug": "*", + "formidable": "1.0.14", + "fresh": "0.2.0", + "methods": "0.0.1", + "pause": "0.0.1", + "qs": "0.6.5", + "send": "0.1.4", + "uid2": "0.0.2" + } + }, + "console-log-level": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", + "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==" + }, + "continuation-local-storage": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "requires": { + "async-listener": "^0.6.0", + "emitter-listener": "^1.1.1" + } + }, + "cookie": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", + "integrity": "sha1-kOtGndzpBchm3mh+/EMTHYgB+dA=" + }, + "cookie-signature": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz", + "integrity": "sha1-ROByFIrwHm6OJK+/EmkNaK5pjss=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", + "optional": true, + "requires": { + "boom": "0.4.x" + } + }, + "ctype": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", + "optional": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "delay": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz", + "integrity": "sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA==" + }, + "delayed-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", + "optional": true + }, + "denque": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "dtrace-provider": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", + "integrity": "sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ=", + "optional": true, + "requires": { + "nan": "^2.10.0" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", + "integrity": "sha1-+Ibmxm39sl7x/nNWQUbuXLR4r8s=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "emitter-listener": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", + "requires": { + "shimmer": "^1.2.0" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "exeunt": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", + "integrity": "sha1-r3Lbb5Szy3XpIa7jddUTBJhD0oQ=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "express": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", + "integrity": "sha1-P9B3ZgycyuRxD8+zJikKAdHnJWY=", + "requires": { + "buffer-crc32": "0.2.1", + "commander": "1.2.0", + "connect": "2.8.5", + "cookie": "0.1.0", + "cookie-signature": "1.0.1", + "debug": "*", + "fresh": "0.2.0", + "methods": "0.0.1", + "mkdirp": "0.3.5", + "range-parser": "0.0.4", + "send": "0.1.4" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-text-encoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", + "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "findit2": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", + "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "forever-agent": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=" + }, + "form-data": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", + "optional": true, + "requires": { + "async": "~0.9.0", + "combined-stream": "~0.0.4", + "mime": "~1.2.11" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "optional": true + } + } + }, + "formatio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", + "dev": true, + "requires": { + "samsam": "1.x" + } + }, + "formidable": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", + "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=" + }, + "fresh": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", + "integrity": "sha1-v9lALPPfEsSkwxDHn5mj3eE9NKc=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "gaxios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", + "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", + "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", + "requires": { + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" + } + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "google-auth-library": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", + "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", + "requires": { + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^1.2.1", + "gcp-metadata": "^1.0.0", + "gtoken": "^2.3.2", + "https-proxy-agent": "^2.2.1", + "jws": "^3.1.5", + "lru-cache": "^5.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "google-p12-pem": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", + "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", + "requires": { + "node-forge": "^0.8.0", + "pify": "^4.0.0" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "gtoken": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", + "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", + "requires": { + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "hawk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", + "optional": true, + "requires": { + "boom": "0.4.x", + "cryptiles": "0.2.x", + "hoek": "0.9.x", + "sntp": "0.2.x" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "heap": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", + "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=" + }, + "hex2dec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz", + "integrity": "sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA==" + }, + "hoek": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", + "optional": true + }, + "http-signature": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", + "optional": true, + "requires": { + "asn1": "0.1.11", + "assert-plus": "^0.1.5", + "ctype": "0.5.3" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ioredis": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.9.5.tgz", + "integrity": "sha512-L9MVfvX4F3LScTMEgriCGixzqinJsYy7Mt0NPX8RyuOTmx5JW0744pM4Ze2KVQcP3J0zvKYZ1LywAB6KIq7PYg==", + "requires": { + "cluster-key-slot": "^1.0.6", + "debug": "^3.1.0", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "redis-commands": "1.4.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==" + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-bigint": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", + "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "requires": { + "bignumber.js": "^7.0.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "just-extend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", + "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", + "dev": true + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "keypress": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", + "integrity": "sha1-SjGI1CkbZrT2XtuZ+AaqmuKTWSo=" + }, + "line-reader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz", + "integrity": "sha1-xDkrWH3qOFgMlnhXDm6OSfzlJiI=" + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.pickby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", + "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=" + }, + "logger-sharelatex": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.7.0.tgz", + "integrity": "sha512-9sxDGPSphOMDqUqGpOu/KxFAVcpydKggWv60g9D7++FDCxGkhLLn0kmBkDdgB00d1PadgX1CBMWKzIBpptDU/Q==", + "requires": { + "bunyan": "1.8.12", + "raven": "1.1.3", + "request": "2.88.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "bunyan": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "lolex": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "lsmod": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" + }, + "lynx": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", + "requires": { + "mersenne": "~0.0.3", + "statsd-parser": "~0.0.4" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "mersenne": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" + }, + "methods": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", + "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=" + }, + "metrics-sharelatex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.2.0.tgz", + "integrity": "sha512-kjj3EdkrOJrENLFW/QHiPqBr5AbGEHeti90nMbw6sjKO2TOcuPJHT2Y66m8tqgotnMPKw+kXToRs8Rc9+0xuMQ==", + "requires": { + "@google-cloud/debug-agent": "^3.0.0", + "@google-cloud/profiler": "^0.2.3", + "@google-cloud/trace-agent": "^3.2.0", + "coffee-script": "1.6.0", + "lynx": "~0.1.1", + "prom-client": "^11.1.3", + "underscore": "~1.6.0" + }, + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "optional": true + }, + "mongo-uri": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz", + "integrity": "sha1-FzrwFAMzkALgq9C01nWYfTzc+Z4=" + }, + "mongodb": { + "version": "2.2.36", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.36.tgz", + "integrity": "sha512-P2SBLQ8Z0PVx71ngoXwo12+FiSfbNfGOClAao03/bant5DgLNkOPAck5IaJcEk4gKlQhDEURzfR3xuBG1/B+IA==", + "requires": { + "es6-promise": "3.2.1", + "mongodb-core": "2.1.20", + "readable-stream": "2.2.7" + }, + "dependencies": { + "es6-promise": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", + "requires": { + "buffer-shims": "~1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~1.0.0", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "mongodb-core": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz", + "integrity": "sha512-IN57CX5/Q1bhDq6ShAR6gIv4koFsZP7L8WOK1S0lR0pVDQaScffSMV5jxubLsmZ7J+UdqmykKw4r9hG3XQEGgQ==", + "requires": { + "bson": "~1.0.4", + "require_optional": "~1.0.0" + }, + "dependencies": { + "bson": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", + "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==" + } + } + }, + "mongojs": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.4.0.tgz", + "integrity": "sha1-8of7/UV/7fWItakBHmhRPZ3TK/s=", + "requires": { + "each-series": "^1.0.0", + "mongodb": "^2.0.45", + "once": "^1.3.2", + "parse-mongo-url": "^1.1.0", + "readable-stream": "^2.0.2", + "thunky": "^0.1.0", + "to-mongodb-core": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + }, + "native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", + "dev": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "nise": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", + "integrity": "sha512-Z3sfYEkLFzFmL8KY6xnSJLRxwQwYBjOXi/24lb62ZnZiGA0JUzGGTI6TBIgfCSMIDl9Jlu8SRmHNACLTemDHww==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^3.1.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^4.1.0", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "lolex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", + "integrity": "sha512-BYxIEXiVq5lGIXeVHnsFzqa1TxN5acnKnPCdlZSpzm8viNEOhiigupA4vTQ9HEFQ6nLTQ9wQOgBknJgzUYQ9Aw==", + "dev": true + } + } + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-forge": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.4.tgz", + "integrity": "sha512-UOfdpxivIYY4g5tqp5FNRNgROVNxRACUxxJREntJLFaJr1E0UEqFtUIk0F/jYx/E+Y6sVXd0KDi/m5My0yGCVw==" + }, + "oauth-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "parse-duration": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.1.tgz", + "integrity": "sha1-ExFN3JiRwezSgANiRFVN5DZHoiY=" + }, + "parse-mongo-url": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", + "integrity": "sha1-ZiON9fjnwMjKTNlw1KtqE3PrdbU=" + }, + "parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pretty-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz", + "integrity": "sha512-qG66ahoLCwpLXD09ZPHSCbUWYTqdosB7SMP4OffgTgL2PBKXMuUsrk5Bwg8q4qPkjTXsKBMr+YK3Ltd/6F9s/Q==", + "requires": { + "parse-ms": "^2.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "prom-client": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.1.tgz", + "integrity": "sha512-AcFuxVgzoA/4nlpeg9SkM2HkDjNU3V7g2LCLwpudXSbcSLiFpRMVfsCoCY5RYeR/d9jkQng1mCmVKj1mPHvP0Q==", + "requires": { + "tdigest": "^0.1.1" + } + }, + "protobufjs": { + "version": "6.8.8", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", + "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "10.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz", + "integrity": "sha512-NelG/dSahlXYtSoVPErrp06tYFrvzj8XLWmKA+X8x0W//4MqbUyZu++giUG/v0bjAT6/Qxa8IjodrfdACyb0Fg==" + } + } + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "q": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", + "integrity": "sha1-I8BsRsgTKGFqrhaNPuI6Vr1D2vY=" + }, + "qs": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", + "integrity": "sha1-KUsmjksNQlD23eGbO4s0k13/FO8=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "range-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", + "integrity": "sha1-wEJ//vUcEKy6B4KkbJYC50T/Ygs=" + }, + "raven": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.1.3.tgz", + "integrity": "sha1-QnPBrm005CMPUbLAEEGjK5Iygio=", + "requires": { + "cookie": "0.3.1", + "json-stringify-safe": "5.0.1", + "lsmod": "1.0.0", + "stack-trace": "0.0.9", + "uuid": "3.0.0" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "uuid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "redis": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz", + "integrity": "sha1-iSf+IRDuOWF7zz/Te4nY4SORG7Y=" + }, + "redis-commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", + "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "redis-sentinel": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", + "integrity": "sha1-Vj3TQduZMgMfSX+v3Td+hkj/s+U=", + "requires": { + "q": "0.9.2", + "redis": "0.11.x" + }, + "dependencies": { + "redis": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", + "integrity": "sha1-/cAdSrTL5LO7LLKByP5WnDhX9XE=" + } + } + }, + "redis-sharelatex": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.8.tgz", + "integrity": "sha512-X88/tG03NKWoy0uMzTrzARvILaFj9ZoKGhECtjf8N7GeCzo90zCeT0cVIJCVHeECogXCxBRf/ABFUBBQKUOCew==", + "requires": { + "async": "^2.5.0", + "coffee-script": "1.8.0", + "ioredis": "~4.9.1", + "redis-sentinel": "0.1.1", + "underscore": "1.7.0" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "requires": { + "lodash": "^4.17.11" + } + }, + "coffee-script": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "integrity": "sha1-nJ8dK0pSoADe0Vtll5FwNkgmPB0=", + "requires": { + "mkdirp": "~0.3.5" + } + } + } + }, + "request": { + "version": "2.33.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", + "integrity": "sha1-UWeHgTFyYHDsYzdS6iMKI3ncZf8=", + "requires": { + "aws-sign2": "~0.5.0", + "forever-agent": "~0.5.0", + "form-data": "~0.1.0", + "hawk": "~1.0.0", + "http-signature": "~0.10.0", + "json-stringify-safe": "~5.0.0", + "mime": "~1.2.9", + "node-uuid": "~1.4.0", + "oauth-sign": "~0.3.0", + "qs": "~0.6.0", + "tough-cookie": ">=0.12.0", + "tunnel-agent": "~0.3.0" + }, + "dependencies": { + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + } + } + }, + "requestretry": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", + "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", + "requires": { + "extend": "^3.0.0", + "lodash": "^4.15.0", + "request": "^2.74.0", + "when": "^3.7.7" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "require-in-the-middle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.0.tgz", + "integrity": "sha512-GX12iFhCUzzNuIqvei0dTLUbBEjZ420KTY/MmDxe2GQKPDGyH/wgfGMWFABpnM/M6sLwC3IaSg8A95U6gIb+HQ==", + "requires": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.10.0" + } + }, + "require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", + "dev": true + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "retry-axios": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", + "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==" + }, + "retry-request": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", + "integrity": "sha512-S4HNLaWcMP6r8E4TMH52Y7/pM8uNayOcTDDQNBwsCccL1uI+Ol2TljxRDPzaNfbhOB30+XWP5NnZkB3LiJxi1w==", + "requires": { + "through2": "^2.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "s3-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", + "integrity": "sha1-Y/Ax6+FRg/CfdlZMYW8cCc7PMFA=", + "requires": { + "bluebird": "^2.9.27", + "lodash": "^3.9.3", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "dev": true + }, + "sandboxed-module": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", + "integrity": "sha1-8fvvvYCaT2kHO9B8rm/H2y6vX2o=", + "dev": true, + "requires": { + "require-like": "0.1.2", + "stack-trace": "0.0.6" + }, + "dependencies": { + "stack-trace": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "integrity": "sha1-HnGb1qJin/CcGJ4Xqe+QKpT8XbA=", + "dev": true + } + } + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "send": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", + "integrity": "sha1-vnDY0b4B3mGCGvE3gLUDRaT3Gr0=", + "requires": { + "debug": "*", + "fresh": "0.2.0", + "mime": "~1.2.9", + "range-parser": "0.0.4" + } + }, + "settings-sharelatex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", + "integrity": "sha512-f7D+0lnlohoteSn6IKTH72NE+JnAdMWTKwQglAuimZWTID2FRRItZSGeYMTRpvEnaQApkoVwRp//WRMsiddnqw==", + "requires": { + "coffee-script": "1.6.0" + }, + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + } + } + }, + "shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "sinon": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", + "integrity": "sha512-KY3OLOWpek/I4NGAMHetuutVgS2aRgMR5g5/1LSYvPJ3qo2BopIvk3esFztPxF40RWf/NNNJzdFPriSkXUVK3A==", + "dev": true, + "requires": { + "diff": "^3.1.0", + "formatio": "1.2.0", + "lolex": "^2.1.2", + "native-promise-only": "^0.8.1", + "nise": "^1.0.1", + "path-to-regexp": "^1.7.0", + "samsam": "^1.1.3", + "text-encoding": "0.6.4", + "type-detect": "^4.0.0" + } + }, + "sntp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", + "optional": true, + "requires": { + "hoek": "0.9.x" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, + "sshpk": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" + }, + "standard-as-callback": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" + }, + "statsd-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", + "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + }, + "tdigest": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", + "requires": { + "bintrees": "1.0.1" + } + }, + "teeny-request": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", + "requires": { + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", + "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=" + }, + "timekeeper": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", + "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=", + "dev": true + }, + "to-mongodb-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", + "integrity": "sha1-NZbsdhOsmtO5ioncua77pWnNJ+s=" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "optional": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true + } + } + }, + "tunnel-agent": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", + "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=", + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "uid2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz", + "integrity": "sha1-EH+xVcgsETZiB5ftTIjPKwj2qrg=" + }, + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "when": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", + "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + } + } +} From 9cee6c3d2254daa6f3ee453f0f00586dfcbc4aad Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:32:47 +0100 Subject: [PATCH 461/549] removed unnecessary default argument --- services/track-changes/app/coffee/LockManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.coffee index 18a764035c..fe4afaeba9 100644 --- a/services/track-changes/app/coffee/LockManager.coffee +++ b/services/track-changes/app/coffee/LockManager.coffee @@ -66,7 +66,7 @@ module.exports = LockManager = return callback(new Error("tried to release timed out lock")) callback(err,result) - runWithLock: (key, runner = ( (releaseLock = (error) ->) -> ), callback = ( (error) -> )) -> + runWithLock: (key, runner, callback = ( (error) -> )) -> LockManager.getLock key, (error, lockValue) -> return callback(error) if error? runner (error1) -> From acd09a8a4e490623b3279c9691f5bf09297bddd6 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:33:01 +0100 Subject: [PATCH 462/549] decaffeinate: update build scripts to es --- services/track-changes/.dockerignore | 2 - services/track-changes/.eslintrc | 65 ++++++++++++++++++++++++++ services/track-changes/.prettierrc | 8 ++++ services/track-changes/Dockerfile | 1 - services/track-changes/Jenkinsfile | 7 +++ services/track-changes/Makefile | 15 ++++-- services/track-changes/buildscript.txt | 2 +- services/track-changes/nodemon.json | 7 ++- services/track-changes/package.json | 18 ++++--- 9 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 services/track-changes/.eslintrc create mode 100644 services/track-changes/.prettierrc diff --git a/services/track-changes/.dockerignore b/services/track-changes/.dockerignore index 386f26df30..ba1c3442de 100644 --- a/services/track-changes/.dockerignore +++ b/services/track-changes/.dockerignore @@ -5,5 +5,3 @@ gitrev .npm .nvmrc nodemon.json -app.js -**/js/* diff --git a/services/track-changes/.eslintrc b/services/track-changes/.eslintrc new file mode 100644 index 0000000000..42a4b5cace --- /dev/null +++ b/services/track-changes/.eslintrc @@ -0,0 +1,65 @@ +// this file was auto-generated, do not edit it directly. +// instead run bin/update_build_scripts from +// https://github.com/sharelatex/sharelatex-dev-environment +// Version: 1.3.5 +{ + "extends": [ + "standard", + "prettier", + "prettier/standard" + ], + "parserOptions": { + "ecmaVersion": 2017 + }, + "plugins": [ + "mocha", + "chai-expect", + "chai-friendly" + ], + "env": { + "node": true, + "mocha": true + }, + "rules": { + // Swap the no-unused-expressions rule with a more chai-friendly one + "no-unused-expressions": 0, + "chai-friendly/no-unused-expressions": "error" + }, + "overrides": [ + { + // Test specific rules + "files": ["test/**/*.js"], + "globals": { + "expect": true + }, + "rules": { + // mocha-specific rules + "mocha/handle-done-callback": "error", + "mocha/no-exclusive-tests": "error", + "mocha/no-global-tests": "error", + "mocha/no-identical-title": "error", + "mocha/no-nested-tests": "error", + "mocha/no-pending-tests": "error", + "mocha/no-skipped-tests": "error", + "mocha/no-mocha-arrows": "error", + + // chai-specific rules + "chai-expect/missing-assertion": "error", + "chai-expect/terminating-properties": "error", + + // prefer-arrow-callback applies to all callbacks, not just ones in mocha tests. + // we don't enforce this at the top-level - just in tests to manage `this` scope + // based on mocha's context mechanism + "mocha/prefer-arrow-callback": "error" + } + }, + { + // Backend specific rules + "files": ["app/**/*.js", "app.js", "index.js"], + "rules": { + // don't allow console.log in backend code + "no-console": "error" + } + } + ] +} diff --git a/services/track-changes/.prettierrc b/services/track-changes/.prettierrc new file mode 100644 index 0000000000..5845b82113 --- /dev/null +++ b/services/track-changes/.prettierrc @@ -0,0 +1,8 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.3.5 +{ + "semi": false, + "singleQuote": true +} diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index e538fb48d9..2d8b097e26 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -17,7 +17,6 @@ RUN npm install --quiet COPY . /app -RUN npm run compile:all FROM base diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile index 23be6d5d33..71f95c3c14 100644 --- a/services/track-changes/Jenkinsfile +++ b/services/track-changes/Jenkinsfile @@ -37,6 +37,13 @@ pipeline { } } + stage('Linting') { + steps { + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make format' + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make lint' + } + } + stage('Unit Tests') { steps { sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_unit' diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index b139b47573..73702fc5b1 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -16,12 +16,17 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - rm -f app.js - rm -rf app/js - rm -rf test/unit/js - rm -rf test/acceptance/js -test: test_unit test_acceptance +format: + $(DOCKER_COMPOSE) run --rm test_unit npm run format + +format_fix: + $(DOCKER_COMPOSE) run --rm test_unit npm run format:fix + +lint: + $(DOCKER_COMPOSE) run --rm test_unit npm run lint + +test: format lint test_unit test_acceptance test_unit: @[ ! -d test/unit ] && echo "track-changes has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 039db45c26..d68e80b307 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,6 +1,6 @@ track-changes --public-repo=True ---language=coffeescript +--language=es --env-add=AWS_BUCKET=bucket --node-version=10.19.0 --acceptance-creds=None diff --git a/services/track-changes/nodemon.json b/services/track-changes/nodemon.json index 98db38d71b..5826281b84 100644 --- a/services/track-changes/nodemon.json +++ b/services/track-changes/nodemon.json @@ -10,10 +10,9 @@ }, "watch": [ - "app/coffee/", - "app.coffee", + "app/js/", + "app.js", "config/" ], - "ext": "coffee" - + "ext": "js" } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 72e3e7dbed..c975e2e15e 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -7,17 +7,15 @@ "url": "https://github.com/sharelatex/track-changes-sharelatex.git" }, "scripts": { - "compile:app": "([ -e app/coffee ] && coffee -m $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee -m $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", - "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", - "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", - "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", - "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", - "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", - "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", - "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && npm run compile:smoke_tests", + "start": "node $NODE_APP_OPTIONS app.js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:acceptance": "npm run test:acceptance:_run -- --grep=$MOCHA_GREP", + "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP", "nodemon": "nodemon --config nodemon.json", - "compile:smoke_tests": "[ ! -e test/smoke/coffee ] && echo 'No smoke tests to compile' || coffee -o test/smoke/js -c test/smoke/coffee" + "lint": "node_modules/.bin/eslint .", + "format": "node_modules/.bin/prettier-eslint '**/*.js' --list-different", + "format:fix": "node_modules/.bin/prettier-eslint '**/*.js' --write" }, "dependencies": { "JSONStream": "^1.0.4", From a77c05c60ca869165b53dd8755cda106582fba4d Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:33:06 +0100 Subject: [PATCH 463/549] decaffeinate: update .gitignore --- services/track-changes/.gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index 9df7f254ec..1cdbe5e293 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -1,8 +1,3 @@ **.swp node_modules/ -app/js -app.js -**/*.map -test/unit/js -test/acceptance/js forever/ From 0366406dbed55a08653f44551b2c32b04814f306 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:33:37 +0100 Subject: [PATCH 464/549] decaffeinate: add eslint and prettier packages --- services/track-changes/package-lock.json | 3495 +++++++++++++++++++++- services/track-changes/package.json | 20 +- 2 files changed, 3350 insertions(+), 165 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index a6466ed433..0825432037 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -4,10 +4,155 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", + "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "dev": true + }, + "@babel/runtime": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", + "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "@babel/template": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", + "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.4", + "@babel/types": "^7.8.3", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, "@google-cloud/common": { "version": "0.32.1", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", - "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", + "integrity": "sha1-ajLDQBcs6j22Z00ODjTnh0CgBz8=", "requires": { "@google-cloud/projectify": "^0.3.3", "@google-cloud/promisify": "^0.4.0", @@ -25,7 +170,7 @@ "@google-cloud/debug-agent": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.2.0.tgz", - "integrity": "sha512-fP87kYbS6aeDna08BivwQ1J260mwJGchRi99XdWCgqbRwuFac8ul0OT5i2wEeDSc5QaDX8ZuWQQ0igZvh1rTyQ==", + "integrity": "sha1-2qdjWhaYpWY31dxXzhED536uKdM=", "requires": { "@google-cloud/common": "^0.32.0", "@sindresorhus/is": "^0.15.0", @@ -46,19 +191,19 @@ "coffeescript": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.4.1.tgz", - "integrity": "sha512-34GV1aHrsMpTaO3KfMJL40ZNuvKDR/g98THHnE9bQj8HjMaZvSrLik99WWqyMhRtbe8V5hpx5iLgdcSvM/S2wg==" + "integrity": "sha1-gV/TN98KNNSedKmKbr6pw+eTD3A=" }, "semver": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==" + "integrity": "sha1-U/U9qbMLIQPNTxXqs6GOy8shDJs=" } } }, "@google-cloud/profiler": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-0.2.3.tgz", - "integrity": "sha512-rNvtrFtIebIxZEJ/O0t8n7HciZGIXBo8DvHxWqAmsCaeLvkTtsaL6HmPkwxrNQ1IhbYWAxF+E/DwCiHyhKmgTg==", + "integrity": "sha1-Fj3738Mwuug1X+RuHlvgZTV7H1w=", "requires": { "@google-cloud/common": "^0.26.0", "@types/console-log-level": "^1.4.0", @@ -80,7 +225,7 @@ "@google-cloud/common": { "version": "0.26.2", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz", - "integrity": "sha512-xJ2M/q3MrUbnYZuFlpF01caAlEhAUoRn0NXp93Hn3pkFpfSOG8YfbKbpBAHvcKVbBOAKVIwPsleNtuyuabUwLQ==", + "integrity": "sha1-nFTiRxqEqgMelaJIJJduCA8lVkU=", "requires": { "@google-cloud/projectify": "^0.3.2", "@google-cloud/promisify": "^0.3.0", @@ -99,7 +244,7 @@ "@google-cloud/promisify": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz", - "integrity": "sha512-QzB0/IMvB0eFxFK7Eqh+bfC8NLv3E9ScjWQrPOk6GgfNroxcVITdTlT8NRsRrcp5+QQJVPLkRqKG0PUdaWXmHw==" + "integrity": "sha1-9kHm2USo4KBe4MsQkd+mAIm+zbo=" }, "arrify": { "version": "1.0.1", @@ -109,7 +254,7 @@ "gcp-metadata": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", - "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", + "integrity": "sha1-H510lfdGChRSZIHynhFZbdVj3SY=", "requires": { "gaxios": "^1.0.2", "json-bigint": "^0.3.0" @@ -118,7 +263,7 @@ "google-auth-library": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", - "integrity": "sha512-FURxmo1hBVmcfLauuMRKOPYAPKht3dGuI2wjeJFalDUThO0HoYVjr4yxt5cgYSFm1dgUpmN9G/poa7ceTFAIiA==", + "integrity": "sha1-ejFdIDZ0Svavyth7IQ7mY4tA9Xs=", "requires": { "axios": "^0.18.0", "gcp-metadata": "^0.7.0", @@ -132,7 +277,7 @@ "gcp-metadata": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", - "integrity": "sha512-ffjC09amcDWjh3VZdkDngIo7WoluyC5Ag9PAYxZbmQLOLNI8lvPtoKTSCyU54j2gwy5roZh6sSMTfkY2ct7K3g==", + "integrity": "sha1-bDXbtSvaMqQnu5yY9UI33dG1QG8=", "requires": { "axios": "^0.18.0", "extend": "^3.0.1", @@ -144,7 +289,7 @@ "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", "requires": { "yallist": "^3.0.2" } @@ -152,7 +297,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", "requires": { "readable-stream": "2 || 3" } @@ -162,17 +307,17 @@ "@google-cloud/projectify": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", - "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" + "integrity": "sha1-vekQPVCyCj6jM334xng6dm5w1B0=" }, "@google-cloud/promisify": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", - "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==" + "integrity": "sha1-T7/PTYW7ai5MzwWqY9KxDWyarZs=" }, "@google-cloud/trace-agent": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.6.1.tgz", - "integrity": "sha512-KDo85aPN4gSxJ7oEIOlKd7aGENZFXAM1kbIn1Ds+61gh/K1CQWSyepgJo3nUpAwH6D1ezDWV7Iaf8ueoITc8Uw==", + "integrity": "sha1-W+dEE5TQ6ldY8o25IqUAT/PwO+w=", "requires": { "@google-cloud/common": "^0.32.1", "builtin-modules": "^3.0.0", @@ -197,7 +342,7 @@ "semver": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==" + "integrity": "sha1-U/U9qbMLIQPNTxXqs6GOy8shDJs=" } } }, @@ -209,12 +354,12 @@ "@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + "integrity": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU=" }, "@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + "integrity": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs=" }, "@protobufjs/eventemitter": { "version": "1.1.0", @@ -258,12 +403,12 @@ "@sindresorhus/is": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.15.0.tgz", - "integrity": "sha512-lu8BpxjAtRCAo5ifytTpCPCj99LF7o/2Myn+NXyNCBqvPYn7Pjd76AMmUB5l7XF1U6t0hcWrlEM5ESufW7wAeA==" + "integrity": "sha1-lpFbqgXmpqHRN7rfSYTT/AWCC7Y=" }, "@sinonjs/commons": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", - "integrity": "sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==", + "integrity": "sha1-ez7C2Wr0gdegMhJS57HJRyTsWng=", "dev": true, "requires": { "type-detect": "4.0.8" @@ -272,7 +417,7 @@ "@sinonjs/formatio": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", - "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", + "integrity": "sha1-UjEPL5vLxnvawYyUrUkBuV/eJn4=", "dev": true, "requires": { "@sinonjs/commons": "^1", @@ -282,7 +427,7 @@ "@sinonjs/samsam": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", - "integrity": "sha512-ILO/rR8LfAb60Y1Yfp9vxfYAASK43NFC2mLzpvLUbCQY/Qu8YwReboseu8aheCEkyElZF2L2T9mHcR2bgdvZyA==", + "integrity": "sha1-Y5QuPV6wt59t4775q/rRX7S2QBs=", "dev": true, "requires": { "@sinonjs/commons": "^1.0.2", @@ -293,7 +438,7 @@ "@sinonjs/text-encoding": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "integrity": "sha1-jaXGUwkVZT86Hzj9XxAdjD+AecU=", "dev": true }, "@types/caseless": { @@ -314,6 +459,12 @@ "@types/node": "*" } }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/form-data": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", @@ -322,6 +473,12 @@ "@types/node": "*" } }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", @@ -353,10 +510,63 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==" }, + "@typescript-eslint/experimental-utils": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", + "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-scope": "^4.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", + "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.13.0", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", + "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "integrity": "sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=", "requires": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -365,7 +575,7 @@ "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "integrity": "sha1-6vVNU7YrrkE46AnKIlyEOabvs5I=", "requires": { "event-target-shim": "^5.0.0" } @@ -373,12 +583,18 @@ "acorn": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + "integrity": "sha1-fSWuBbuK0fm2mRCOEJTs14hK3B8=" + }, + "acorn-jsx": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", + "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "dev": true }, "agent-base": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "integrity": "sha1-gWXwHENgCbzK0LHRIvBe13Dvxu4=", "requires": { "es6-promisify": "^5.0.0" } @@ -386,7 +602,7 @@ "ajv": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", - "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "integrity": "sha1-ys7M9HS/P8POOxR0Q3EaJAY8ww0=", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -394,16 +610,88 @@ "uri-js": "^4.2.2" } }, + "ansi-escapes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, "array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + "integrity": "sha1-yWVekzHgq81YjSp8rX6ZVvZnAfo=" }, "asn1": { "version": "0.1.11", @@ -420,7 +708,19 @@ "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "integrity": "sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs=", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, "async": { @@ -431,7 +731,7 @@ "async-listener": { "version": "0.6.10", "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", - "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "integrity": "sha1-p8l6vlcLpgLXgic8DeYKUePhfLw=", "requires": { "semver": "^5.3.0", "shimmer": "^1.1.0" @@ -445,7 +745,7 @@ "aws-sdk": { "version": "2.384.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.384.0.tgz", - "integrity": "sha512-E+pIOWFNhQH7GCkOl5GU+Vl42MlaKtAq0Yenaa2fRGult9097u7TnUx45V1pNKMCN9RnEFWQy3ZH1TEPEYJ0fw==", + "integrity": "sha1-yP3OfHLwaPbYsZi1PPM7bzXz6BE=", "requires": { "buffer": "4.9.1", "events": "1.1.1", @@ -467,17 +767,48 @@ "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" }, "axios": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "integrity": "sha1-/z8N4ue10YDnV62YAA8Qgbh7zqM=", "requires": { "follow-redirects": "1.5.10", "is-buffer": "^2.0.2" } }, + "axobject-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz", + "integrity": "sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==", + "dev": true + }, + "babel-eslint": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", + "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -486,7 +817,7 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM=" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -499,12 +830,12 @@ "bignumber.js": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", - "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + "integrity": "sha1-gMBIdZ2CaACAfEv9Uh5Q7bulel8=" }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", "requires": { "file-uri-to-path": "1.0.0" } @@ -514,6 +845,12 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" }, + "boolify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz", + "integrity": "sha1-tcCeF8rNET0Rt7s+04TMASmU2Gs=", + "dev": true + }, "boom": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", @@ -526,7 +863,7 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -571,7 +908,7 @@ "builtin-modules": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==" + "integrity": "sha1-qtl8FRMet2tltQ7yCOdYTNdqdIQ=" }, "bunyan": { "version": "2.0.2", @@ -596,6 +933,29 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", "integrity": "sha1-qtM+wU49wsp06OfUUfm6BTrU96A=" }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.1.2.tgz", + "integrity": "sha512-QfFrU0CIw2oltVvpndW32kuJ/9YOJwUnmWrjlXt1nnJZHCaS9i6bfOpg9R4Lw8aZjStkJWM+jc0cdXjWBgVJSw==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -615,6 +975,40 @@ "type-detect": "^4.0.0" } }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -630,10 +1024,61 @@ "glob": "~ 3.2.1" } }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, "cluster-key-slot": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz", - "integrity": "sha512-21O0kGmvED5OJ7ZTdqQ5lQQ+sjuez33R+d35jZKLwqUb5mqcPHUsxOSzj61+LHVtxGZd1kShbQM3MjB/gBJkVg==" + "integrity": "sha1-1d7/KlIHF7yYMTl5tocwmy02jik=" }, "coffee-script": { "version": "1.12.4", @@ -641,6 +1086,21 @@ "integrity": "sha1-/hvO2X/h+zknuZjytFYW4GWL4f8=", "dev": true }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "combined-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", @@ -658,6 +1118,12 @@ "keypress": "0.1.x" } }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -685,12 +1151,18 @@ "console-log-level": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", - "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==" + "integrity": "sha1-nFprue8e9lsFq6gwKLD/iUzfYwo=" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true }, "continuation-local-storage": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", - "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "integrity": "sha1-EfYT906RT+mzTJKtLSj+auHbf/s=", "requires": { "async-listener": "^0.6.0", "emitter-listener": "^1.1.1" @@ -706,11 +1178,30 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz", "integrity": "sha1-ROByFIrwHm6OJK+/EmkNaK5pjss=" }, + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, "cryptiles": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", @@ -726,6 +1217,12 @@ "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", "optional": true }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -744,24 +1241,45 @@ "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", "requires": { "ms": "^2.1.1" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", "dev": true, "requires": { "type-detect": "^4.0.0" } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "delay": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz", - "integrity": "sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA==" + "integrity": "sha1-7+6/uPVFV5yzlrOnIkQ+yW0UxQ4=" }, "delayed-stream": { "version": "0.0.5", @@ -772,14 +1290,29 @@ "denque": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + "integrity": "sha1-Z0T/dkHBSMP4ppwwflEjXB9KN88=" }, "diff": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=", "dev": true }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dtrace-provider": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", @@ -792,7 +1325,7 @@ "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -817,7 +1350,7 @@ "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "integrity": "sha1-rg8PothQRe8UqBfao86azQSJ5b8=", "requires": { "safe-buffer": "^5.0.1" } @@ -825,15 +1358,21 @@ "emitter-listener": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", - "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", + "integrity": "sha1-VrFA6PaZI3Wz18ssqxzHQy2WMug=", "requires": { "shimmer": "^1.2.0" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "requires": { "once": "^1.4.0" } @@ -843,10 +1382,49 @@ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=" }, "es6-promisify": { "version": "5.0.0", @@ -862,10 +1440,521 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.6.0.tgz", + "integrity": "sha512-PpEBq7b6qY/qrOmpYQ/jTMDYfuQMELR4g4WI1M/NaSDDD/bdcMb+dj4Hgks7p41kW2caXsPsEZAEAyAgjVVC0g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + } + }, + "eslint-config-standard": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz", + "integrity": "sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", + "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "dev": true + }, + "eslint-config-standard-react": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-react/-/eslint-config-standard-react-9.2.0.tgz", + "integrity": "sha512-u+KRP2uCtthZ/W4DlLWCC59GZNV/y9k9yicWWammgTs/Omh8ZUUPF3EnYm81MAcbkYQq2Wg0oxutAhi/FQ8mIw==", + "dev": true, + "requires": { + "eslint-config-standard-jsx": "^8.0.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-module-utils": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", + "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-chai-expect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-chai-expect/-/eslint-plugin-chai-expect-2.1.0.tgz", + "integrity": "sha512-rd0/4mjMV6c3i0o4DKkWI4uaFN9DK707kW+/fDphaDI6HVgxXnhML9Xgt5vHnTXmSSnDhupuCFBgsEAEpchXmQ==", + "dev": true + }, + "eslint-plugin-chai-friendly": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.5.0.tgz", + "integrity": "sha512-Pxe6z8C9fP0pn2X2nGFU/b3GBOCM/5FVus1hsMwJsXP3R7RiXFl7g0ksJbsc0GxiLyidTW4mEFk77qsNn7Tk7g==", + "dev": true + }, + "eslint-plugin-es": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.0.tgz", + "integrity": "sha512-6/Jb/J/ZvSebydwbBJO1R9E5ky7YeElfK56Veh7e4QGFHCXoIXGH9HhVz+ibJLM3XJ1XjP+T7rKBLUa/Y7eIng==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", + "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", + "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.5", + "aria-query": "^3.0.0", + "array-includes": "^3.0.3", + "ast-types-flow": "^0.0.7", + "axobject-query": "^2.0.2", + "damerau-levenshtein": "^1.0.4", + "emoji-regex": "^7.0.2", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.1" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + } + } + }, + "eslint-plugin-mocha": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.2.tgz", + "integrity": "sha512-oNhPzfkT6Q6CJ0HMVJ2KLxEWG97VWGTmuHOoRcDLE0U88ugUyFNV9wrT2XIt5cGtqc5W9k38m4xTN34L09KhBA==", + "dev": true, + "requires": { + "ramda": "^0.26.1" + } + }, + "eslint-plugin-node": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.0.0.tgz", + "integrity": "sha512-chUs/NVID+sknFiJzxoN9lM7uKSOEta8GC8365hw1nDfwIPIjjpRSwwPvQanWv8dt/pDe9EV4anmVSwdiSndNg==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.3.tgz", + "integrity": "sha512-Bt56LNHAQCoou88s8ViKRjMB2+36XRejCQ1VoLj716KI1MoE99HpTVvIThJ0rvFmG4E4Gsq+UgToEjn+j044Bg==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.2.3", + "object.entries": "^1.1.1", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.14.2", + "string.prototype.matchall": "^4.0.2" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", + "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "dev": true, + "requires": { + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", + "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + "integrity": "sha1-XU0+vflYPWOlMzzi3rdICrKwV4k=" }, "events": { "version": "1.1.1", @@ -904,7 +1993,18 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } }, "extsprintf": { "version": "1.3.0", @@ -916,30 +2016,120 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fast-text-encoding": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", - "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" + "integrity": "sha1-PlzoKTQJz6pxd6cbnKhOGx5vJe8=" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } }, "findit2": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=", "requires": { "debug": "=3.1.0" }, @@ -947,7 +2137,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "requires": { "ms": "2.0.0" } @@ -1008,10 +2198,22 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gaxios": { "version": "1.8.4", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", - "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", + "integrity": "sha1-4Iw0/pPAqbZ6Ure556ZOZDX5ozk=", "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -1022,18 +2224,30 @@ "gcp-metadata": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", - "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", + "integrity": "sha1-UhJEAin6CZ/C98KlzcuVV16bLKY=", "requires": { "gaxios": "^1.0.2", "json-bigint": "^0.3.0" } }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -1058,10 +2272,25 @@ "minimatch": "0.3" } }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, "google-auth-library": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", - "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", + "integrity": "sha1-/y+IzVzSEYpXvT1a08CTyIN/w1A=", "requires": { "base64-js": "^1.3.0", "fast-text-encoding": "^1.0.0", @@ -1077,7 +2306,7 @@ "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", "requires": { "yallist": "^3.0.2" } @@ -1087,22 +2316,28 @@ "google-p12-pem": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", - "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", + "integrity": "sha1-t3+4M6Lrn388aJ4uVPCVJ293dgU=", "requires": { "node-forge": "^0.8.0", "pify": "^4.0.0" } }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", "dev": true }, "gtoken": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", + "integrity": "sha1-in/hVcXODEtxyIbPsoKpBg2UpkE=", "requires": { "gaxios": "^1.0.4", "google-p12-pem": "^1.0.0", @@ -1114,7 +2349,7 @@ "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "integrity": "sha1-vXuRE1/GsBzePpuuM9ZZtj2IV+U=" } } }, @@ -1126,18 +2361,50 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, "hawk": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", @@ -1164,7 +2431,7 @@ "hex2dec": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz", - "integrity": "sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA==" + "integrity": "sha1-jhzkvvNqdPfVcjw/swkMKGAHczg=" }, "hoek": { "version": "0.9.1", @@ -1172,6 +2439,12 @@ "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", "optional": true }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true + }, "http-signature": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", @@ -1186,7 +2459,7 @@ "https-proxy-agent": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "integrity": "sha1-UVUpcPoE1yPgTFbQQXjD+SWSu8A=", "requires": { "agent-base": "^4.1.0", "debug": "^3.1.0" @@ -1195,18 +2468,63 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "requires": { "ms": "^2.1.1" } } } }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ieee754": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1221,10 +2539,50 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "inquirer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", + "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "internal-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", + "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", + "dev": true, + "requires": { + "es-abstract": "^1.17.0-next.1", + "has": "^1.0.3", + "side-channel": "^1.0.2" + } + }, "ioredis": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.9.5.tgz", - "integrity": "sha512-L9MVfvX4F3LScTMEgriCGixzqinJsYy7Mt0NPX8RyuOTmx5JW0744pM4Ze2KVQcP3J0zvKYZ1LywAB6KIq7PYg==", + "integrity": "sha1-C7ugqfquk0hdMjHhuBnS1OIycdk=", "requires": { "cluster-key-slot": "^1.0.6", "debug": "^3.1.0", @@ -1240,7 +2598,7 @@ "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", "requires": { "ms": "^2.1.1" } @@ -1250,12 +2608,81 @@ "is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==" + "integrity": "sha1-Yc/23TxBk9uUo9YlggcrROVkXXk=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, "is-buffer": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + "integrity": "sha1-Ts8/z3ScvR5HJonhCaxmJhol5yU=" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } }, "is-typedarray": { "version": "1.0.0", @@ -1267,6 +2694,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -1277,11 +2710,33 @@ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "json-bigint": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", @@ -1298,7 +2753,13 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", @@ -1328,16 +2789,26 @@ } } }, + "jsx-ast-utils": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", + "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" + } + }, "just-extend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", + "integrity": "sha1-8/R/ffyg+YnFVBCn68iFSwcQivw=", "dev": true }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "integrity": "sha1-dDwymFy56YZVUw1TZBtmyGRbA5o=", "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -1347,7 +2818,7 @@ "jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "integrity": "sha1-ABCZ82OUaMlBQADpmZX6UvtHgwQ=", "requires": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" @@ -1358,15 +2829,55 @@ "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", "integrity": "sha1-SjGI1CkbZrT2XtuZ+AaqmuKTWSo=" }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "line-reader": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz", "integrity": "sha1-xDkrWH3qOFgMlnhXDm6OSfzlJiI=" }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" }, "lodash.defaults": { "version": "4.2.0", @@ -1378,15 +2889,33 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "lodash.pickby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=" }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "logger-sharelatex": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.7.0.tgz", - "integrity": "sha512-9sxDGPSphOMDqUqGpOu/KxFAVcpydKggWv60g9D7++FDCxGkhLLn0kmBkDdgB00d1PadgX1CBMWKzIBpptDU/Q==", + "integrity": "sha1-XuMje84im1rITZ7SLoXa6eI3/HQ=", "requires": { "bunyan": "1.8.12", "raven": "1.1.3", @@ -1417,7 +2946,7 @@ "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", "requires": { "delayed-stream": "~1.0.0" } @@ -1435,7 +2964,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -1455,7 +2984,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" }, "punycode": { "version": "1.4.1", @@ -1465,12 +2994,12 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -1497,7 +3026,7 @@ "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -1514,20 +3043,87 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + } + } + }, + "loglevel": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", + "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", + "dev": true + }, + "loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, "lolex": { "version": "2.7.5", "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", + "integrity": "sha1-ETAB1Wv8fgLVbjYpHMXEE9GqBzM=", "dev": true }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } }, "lru-cache": { "version": "2.7.3", @@ -1548,6 +3144,30 @@ "statsd-parser": "~0.0.4" } }, + "make-plural": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", + "integrity": "sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "dev": true + }, "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -1559,6 +3179,29 @@ "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" }, + "messageformat": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz", + "integrity": "sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==", + "dev": true, + "requires": { + "make-plural": "^4.3.0", + "messageformat-formatters": "^2.0.1", + "messageformat-parser": "^4.1.2" + } + }, + "messageformat-formatters": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz", + "integrity": "sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==", + "dev": true + }, + "messageformat-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.2.tgz", + "integrity": "sha512-7dWuifeyldz7vhEuL96Kwq1fhZXBW+TUfbnHN4UCrCxoXQTYjHnR78eI66Gk9LaLLsAvzPNVJBaa66DRfFNaiA==", + "dev": true + }, "methods": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", @@ -1567,7 +3210,7 @@ "metrics-sharelatex": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.2.0.tgz", - "integrity": "sha512-kjj3EdkrOJrENLFW/QHiPqBr5AbGEHeti90nMbw6sjKO2TOcuPJHT2Y66m8tqgotnMPKw+kXToRs8Rc9+0xuMQ==", + "integrity": "sha1-RM9oy9FuUQYgfrZ+PvkAhaQWwqk=", "requires": { "@google-cloud/debug-agent": "^3.0.0", "@google-cloud/profiler": "^0.2.3", @@ -1598,16 +3241,22 @@ "mime-db": { "version": "1.37.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + "integrity": "sha1-C2oM5v2+lXbiXx8tL96IMNwK0Ng=" }, "mime-types": { "version": "2.1.21", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "integrity": "sha1-KJlaoey3cHQv5q5+WPkYHHRLP5Y=", "requires": { "mime-db": "~1.37.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimatch": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", @@ -1630,7 +3279,7 @@ "mocha": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -1648,13 +3297,13 @@ "commander": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", "dev": true }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -1663,7 +3312,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1677,7 +3326,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -1708,7 +3357,7 @@ "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "integrity": "sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s=", "optional": true }, "mongo-uri": { @@ -1719,7 +3368,7 @@ "mongodb": { "version": "2.2.36", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.36.tgz", - "integrity": "sha512-P2SBLQ8Z0PVx71ngoXwo12+FiSfbNfGOClAao03/bant5DgLNkOPAck5IaJcEk4gKlQhDEURzfR3xuBG1/B+IA==", + "integrity": "sha1-HFc2gLKEn7D0esu6PcX6Io3pdfU=", "requires": { "es6-promise": "3.2.1", "mongodb-core": "2.1.20", @@ -1753,7 +3402,7 @@ "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "requires": { "safe-buffer": "~5.1.0" } @@ -1763,7 +3412,7 @@ "mongodb-core": { "version": "2.1.20", "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz", - "integrity": "sha512-IN57CX5/Q1bhDq6ShAR6gIv4koFsZP7L8WOK1S0lR0pVDQaScffSMV5jxubLsmZ7J+UdqmykKw4r9hG3XQEGgQ==", + "integrity": "sha1-/s6N12tZ7n1/LTE7ZTIsFgSS2PE=", "requires": { "bson": "~1.0.4", "require_optional": "~1.0.0" @@ -1772,7 +3421,7 @@ "bson": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", - "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==" + "integrity": "sha1-EjGfgyOxJUc5t8a++NPomuBaL1c=" } } }, @@ -1794,7 +3443,13 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true }, "mv": { "version": "2.1.1", @@ -1821,7 +3476,7 @@ "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + "integrity": "sha1-exqhk+mqhgV+PHu9CsRI53CSVVI=" }, "native-promise-only": { "version": "0.8.1", @@ -1829,16 +3484,28 @@ "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "nise": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", - "integrity": "sha512-Z3sfYEkLFzFmL8KY6xnSJLRxwQwYBjOXi/24lb62ZnZiGA0JUzGGTI6TBIgfCSMIDl9Jlu8SRmHNACLTemDHww==", + "integrity": "sha1-0D6g5sG3XGOAFao1he3cEylJpQ0=", "dev": true, "requires": { "@sinonjs/formatio": "^3.1.0", @@ -1851,7 +3518,7 @@ "lolex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", - "integrity": "sha512-BYxIEXiVq5lGIXeVHnsFzqa1TxN5acnKnPCdlZSpzm8viNEOhiigupA4vTQ9HEFQ6nLTQ9wQOgBknJgzUYQ9Aw==", + "integrity": "sha1-7N17hlOTkdgjeUejQZqorJdfD+E=", "dev": true } } @@ -1859,12 +3526,24 @@ "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "integrity": "sha1-5jNFY4bUqlWGP2dqerDaqP3ssP0=" }, "node-forge": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.4.tgz", - "integrity": "sha512-UOfdpxivIYY4g5tqp5FNRNgROVNxRACUxxJREntJLFaJr1E0UEqFtUIk0F/jYx/E+Y6sVXd0KDi/m5My0yGCVw==" + "integrity": "sha1-1nOGYrZhvhnicR7wGqOxghLxMDA=" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } }, "oauth-sign": { "version": "0.3.0", @@ -1872,6 +3551,72 @@ "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", "optional": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", + "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1880,24 +3625,97 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-limit": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "integrity": "sha1-QXyZQeYCepq8ulCS3SkE4lW1+8I=", "requires": { "p-try": "^2.0.0" } }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } }, "parse-duration": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.1.tgz", "integrity": "sha1-ExFN3JiRwezSgANiRFVN5DZHoiY=" }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "parse-mongo-url": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", @@ -1906,17 +3724,35 @@ "parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" + "integrity": "sha1-NIVlp1PUOR+lJAKZVrFyy3dTCX0=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=" }, "path-to-regexp": { "version": "1.7.0", @@ -1935,6 +3771,23 @@ } } }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -1954,12 +3807,666 @@ "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=" + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "prettier-eslint": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-9.0.1.tgz", + "integrity": "sha512-KZT65QTosSAqBBqmrC+RpXbsMRe7Os2YSR9cAfFbDlyPAopzA/S5bioiZ3rpziNQNSJaOxmtXSx07EQ+o2Dlug==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^1.10.2", + "common-tags": "^1.4.0", + "core-js": "^3.1.4", + "dlv": "^1.1.0", + "eslint": "^5.0.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^1.7.0", + "pretty-format": "^23.0.1", + "require-relative": "^0.8.7", + "typescript": "^3.2.1", + "vue-eslint-parser": "^2.0.2" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "prettier-eslint-cli": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/prettier-eslint-cli/-/prettier-eslint-cli-5.0.0.tgz", + "integrity": "sha512-cei9UbN1aTrz3sQs88CWpvY/10PYTevzd76zoG1tdJ164OhmNTFRKPTOZrutVvscoQWzbnLKkviS3gu5JXwvZg==", + "dev": true, + "requires": { + "arrify": "^2.0.1", + "boolify": "^1.0.0", + "camelcase-keys": "^6.0.0", + "chalk": "^2.4.2", + "common-tags": "^1.8.0", + "core-js": "^3.1.4", + "eslint": "^5.0.0", + "find-up": "^4.1.0", + "get-stdin": "^7.0.0", + "glob": "^7.1.4", + "ignore": "^5.1.2", + "lodash.memoize": "^4.1.2", + "loglevel-colored-level-prefix": "^1.0.0", + "messageformat": "^2.2.1", + "prettier-eslint": "^9.0.0", + "rxjs": "^6.5.2", + "yargs": "^13.2.4" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } }, "pretty-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz", - "integrity": "sha512-qG66ahoLCwpLXD09ZPHSCbUWYTqdosB7SMP4OffgTgL2PBKXMuUsrk5Bwg8q4qPkjTXsKBMr+YK3Ltd/6F9s/Q==", + "integrity": "sha1-Mbr0G5T9AiJwmKqgO9YmCOsNbpI=", "requires": { "parse-ms": "^2.0.0" } @@ -1967,20 +4474,37 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true }, "prom-client": { "version": "11.5.1", "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.1.tgz", - "integrity": "sha512-AcFuxVgzoA/4nlpeg9SkM2HkDjNU3V7g2LCLwpudXSbcSLiFpRMVfsCoCY5RYeR/d9jkQng1mCmVKj1mPHvP0Q==", + "integrity": "sha1-FcZsrN7EUwELz68EEJvMNOa92pw=", "requires": { "tdigest": "^0.1.1" } }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "protobufjs": { "version": "6.8.8", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "integrity": "sha1-yLTxKC/XqQ5vWxCe0RyEr4KQjnw=", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -2000,14 +4524,14 @@ "@types/node": { "version": "10.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz", - "integrity": "sha512-NelG/dSahlXYtSoVPErrp06tYFrvzj8XLWmKA+X8x0W//4MqbUyZu++giUG/v0bjAT6/Qxa8IjodrfdACyb0Fg==" + "integrity": "sha1-Lo1ngDnSeUPOU6GRM4YTMif9kGY=" } } }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + "integrity": "sha1-6aqG0BAbWxBcvpOsa3hM1UcnYYQ=" }, "punycode": { "version": "1.3.2", @@ -2029,6 +4553,18 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "ramda": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", + "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", + "dev": true + }, "range-parser": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", @@ -2058,10 +4594,37 @@ } } }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2080,7 +4643,7 @@ "redis-commands": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", - "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==" + "integrity": "sha1-UvnPmRU+/M5WqPhq+Ya9BOmIYC8=" }, "redis-errors": { "version": "1.2.0", @@ -2114,7 +4677,7 @@ "redis-sharelatex": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.8.tgz", - "integrity": "sha512-X88/tG03NKWoy0uMzTrzARvILaFj9ZoKGhECtjf8N7GeCzo90zCeT0cVIJCVHeECogXCxBRf/ABFUBBQKUOCew==", + "integrity": "sha1-6cDkGpNCNcsWLjQMC9IxCbZIe70=", "requires": { "async": "^2.5.0", "coffee-script": "1.8.0", @@ -2126,7 +4689,7 @@ "async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "integrity": "sha1-GDMOp+bjE4h/XS8qkEusb+TdU4E=", "requires": { "lodash": "^4.17.11" } @@ -2141,6 +4704,28 @@ } } }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, "request": { "version": "2.33.0", "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", @@ -2170,7 +4755,7 @@ "requestretry": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", + "integrity": "sha1-IT7BAG7rdQ6LjOVBdig9FajVXZQ=", "requires": { "extend": "^3.0.0", "lodash": "^4.15.0", @@ -2191,7 +4776,7 @@ "combined-stream": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "integrity": "sha1-LR0kMXr7ir6V1tLAsHtXgTU52Cg=", "requires": { "delayed-stream": "~1.0.0" } @@ -2209,7 +4794,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -2229,12 +4814,12 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" }, "punycode": { "version": "1.4.1", @@ -2244,12 +4829,12 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -2276,7 +4861,7 @@ "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -2293,14 +4878,20 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" } } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, "require-in-the-middle": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.0.tgz", - "integrity": "sha512-GX12iFhCUzzNuIqvei0dTLUbBEjZ420KTY/MmDxe2GQKPDGyH/wgfGMWFABpnM/M6sLwC3IaSg8A95U6gIb+HQ==", + "integrity": "sha1-PHUoik7EgM30S8d950T4q+WFQFs=", "requires": { "debug": "^4.1.1", "module-details-from-path": "^1.0.3", @@ -2313,10 +4904,22 @@ "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", "dev": true }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "integrity": "sha1-TPNaQkf2TKPfjC7yCMxJSxyo/C4=", "requires": { "resolve-from": "^2.0.0", "semver": "^5.1.0" @@ -2325,7 +4928,7 @@ "resolve": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "integrity": "sha1-QBSHC6KWF2uGND1Qtg87UGCc4jI=", "requires": { "path-parse": "^1.0.6" } @@ -2335,15 +4938,25 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "retry-axios": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", - "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==" + "integrity": "sha1-V1fID1hbTMTEmGqi/9R6YMbTXhM=" }, "retry-request": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", - "integrity": "sha512-S4HNLaWcMP6r8E4TMH52Y7/pM8uNayOcTDDQNBwsCccL1uI+Ol2TljxRDPzaNfbhOB30+XWP5NnZkB3LiJxi1w==", + "integrity": "sha1-XDZhZiebPhDp16oTJ0RnoFy2kpA=", "requires": { "through2": "^2.0.0" } @@ -2373,7 +4986,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "optional": true, "requires": { "brace-expansion": "^1.1.7" @@ -2381,6 +4994,24 @@ } } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "s3-streams": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", @@ -2406,23 +5037,23 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" }, "safe-json-stringify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "integrity": "sha1-NW5EvJjx+TzkXfFLzXwBzahuCv0=", "optional": true }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, "samsam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "integrity": "sha1-jR2TUOJWItow3j5EumkrUiGrfFA=", "dev": true }, "sandboxed-module": { @@ -2451,7 +5082,7 @@ "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=" }, "send": { "version": "0.1.4", @@ -2464,10 +5095,16 @@ "range-parser": "0.0.4" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "settings-sharelatex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", - "integrity": "sha512-f7D+0lnlohoteSn6IKTH72NE+JnAdMWTKwQglAuimZWTID2FRRItZSGeYMTRpvEnaQApkoVwRp//WRMsiddnqw==", + "integrity": "sha1-Tv4vUpPbjxwVlnEEx5BfqHD/mS0=", "requires": { "coffee-script": "1.6.0" }, @@ -2479,20 +5116,51 @@ } } }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, "shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + "integrity": "sha1-YQhZ994ye1h+/r9QH7QxF/mv8zc=" + }, + "side-channel": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", + "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", + "dev": true, + "requires": { + "es-abstract": "^1.17.0-next.1", + "object-inspect": "^1.7.0" + } }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, "sinon": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", - "integrity": "sha512-KY3OLOWpek/I4NGAMHetuutVgS2aRgMR5g5/1LSYvPJ3qo2BopIvk3esFztPxF40RWf/NNNJzdFPriSkXUVK3A==", + "integrity": "sha1-2K2r2QBzD9SXeIoCcEnGSwi+kcI=", "dev": true, "requires": { "diff": "^3.1.0", @@ -2506,6 +5174,25 @@ "type-detect": "^4.0.0" } }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, "sntp": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", @@ -2518,20 +5205,58 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true }, "split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "integrity": "sha1-YFvZvjA6pZ+zX5Ip++oN3snqB9k=", "requires": { "through": "2" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", - "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "integrity": "sha1-HUljovv/5YBQqpCEyiC+gXQcB94=", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -2547,7 +5272,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", "requires": { "safer-buffer": "~2.1.0" } @@ -2567,7 +5292,7 @@ "standard-as-callback": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", - "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" + "integrity": "sha1-7YuyVkjhWDF1m2Ajvbh+a2CzgSY=" }, "statsd-parser": { "version": "0.0.4", @@ -2579,23 +5304,169 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.matchall": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz", + "integrity": "sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2" + } + }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "requires": { "safe-buffer": "~5.1.0" } }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, "supports-color": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", "dev": true, "requires": { "has-flag": "^2.0.0" } }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, "tdigest": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", @@ -2607,7 +5478,7 @@ "teeny-request": { "version": "3.11.3", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", - "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", + "integrity": "sha1-M1xin3ZF5dZZk2LfLzIwxMvCOlU=", "requires": { "https-proxy-agent": "^2.2.1", "node-fetch": "^2.2.0", @@ -2617,7 +5488,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" } } }, @@ -2627,6 +5498,12 @@ "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", "dev": true }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -2635,7 +5512,7 @@ "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -2652,6 +5529,21 @@ "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=", "dev": true }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "to-mongodb-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", @@ -2660,7 +5552,7 @@ "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "integrity": "sha1-zZ+yoKodWhK0c72fuW+j3P9lreI=", "optional": true, "requires": { "psl": "^1.1.28", @@ -2670,11 +5562,17 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", "optional": true } } }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, "tunnel-agent": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", @@ -2686,10 +5584,31 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typescript": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", + "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", "dev": true }, "uid2": { @@ -2705,7 +5624,7 @@ "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", "requires": { "punycode": "^2.1.0" }, @@ -2713,7 +5632,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" } } }, @@ -2734,7 +5653,23 @@ "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } }, "verror": { "version": "1.10.0", @@ -2753,20 +5688,165 @@ } } }, + "vue-eslint-parser": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", + "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + } + } + }, "when": { "version": "3.7.8", "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=" }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=", "requires": { "sax": ">=0.6.0", "xmlbuilder": "~9.0.1" @@ -2782,10 +5862,97 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "integrity": "sha1-tLBJ4xS+VF486AIjbWzSLNkcPek=" + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index c975e2e15e..56007ef786 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -40,11 +40,29 @@ "underscore": "~1.7.0" }, "devDependencies": { + "babel-eslint": "^10.0.3", "bunyan": "~2.0.2", "chai": "~4.1.1", + "coffee-script": "^1.7.1", + "eslint": "^6.6.0", + "eslint-config-prettier": "^6.10.0", + "eslint-config-standard": "^14.1.0", + "eslint-config-standard-jsx": "^8.1.0", + "eslint-config-standard-react": "^9.2.0", + "eslint-plugin-chai-expect": "^2.1.0", + "eslint-plugin-chai-friendly": "^0.5.0", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-mocha": "^6.2.2", + "eslint-plugin-node": "^11.0.0", + "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-react": "^7.18.3", + "eslint-plugin-standard": "^4.0.1", "memorystream": "0.3.1", "mocha": "^4.0.1", - "coffee-script": "^1.7.1", + "prettier": "^1.19.1", + "prettier-eslint-cli": "^5.0.0", "sandboxed-module": "~0.3.0", "sinon": "~3.2.1", "timekeeper": "0.0.4" From a971c5895b15f1f7207e543c94580e2f87af55fd Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:33:53 +0100 Subject: [PATCH 465/549] decaffeinate: Rename DiffGenerator.coffee and 17 other files from .coffee to .js --- .../app/coffee/{DiffGenerator.coffee => DiffGenerator.js} | 0 .../app/coffee/{DiffManager.coffee => DiffManager.js} | 0 .../{DocumentUpdaterManager.coffee => DocumentUpdaterManager.js} | 0 .../app/coffee/{HealthChecker.coffee => HealthChecker.js} | 0 .../app/coffee/{HttpController.coffee => HttpController.js} | 0 .../app/coffee/{LockManager.coffee => LockManager.js} | 0 .../track-changes/app/coffee/{MongoAWS.coffee => MongoAWS.js} | 0 .../app/coffee/{MongoManager.coffee => MongoManager.js} | 0 .../app/coffee/{PackManager.coffee => PackManager.js} | 0 .../track-changes/app/coffee/{PackWorker.coffee => PackWorker.js} | 0 .../app/coffee/{ProjectIterator.coffee => ProjectIterator.js} | 0 .../app/coffee/{RedisManager.coffee => RedisManager.js} | 0 .../app/coffee/{RestoreManager.coffee => RestoreManager.js} | 0 .../app/coffee/{UpdateCompressor.coffee => UpdateCompressor.js} | 0 .../app/coffee/{UpdateTrimmer.coffee => UpdateTrimmer.js} | 0 .../app/coffee/{UpdatesManager.coffee => UpdatesManager.js} | 0 .../app/coffee/{WebApiManager.coffee => WebApiManager.js} | 0 services/track-changes/app/coffee/{mongojs.coffee => mongojs.js} | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/app/coffee/{DiffGenerator.coffee => DiffGenerator.js} (100%) rename services/track-changes/app/coffee/{DiffManager.coffee => DiffManager.js} (100%) rename services/track-changes/app/coffee/{DocumentUpdaterManager.coffee => DocumentUpdaterManager.js} (100%) rename services/track-changes/app/coffee/{HealthChecker.coffee => HealthChecker.js} (100%) rename services/track-changes/app/coffee/{HttpController.coffee => HttpController.js} (100%) rename services/track-changes/app/coffee/{LockManager.coffee => LockManager.js} (100%) rename services/track-changes/app/coffee/{MongoAWS.coffee => MongoAWS.js} (100%) rename services/track-changes/app/coffee/{MongoManager.coffee => MongoManager.js} (100%) rename services/track-changes/app/coffee/{PackManager.coffee => PackManager.js} (100%) rename services/track-changes/app/coffee/{PackWorker.coffee => PackWorker.js} (100%) rename services/track-changes/app/coffee/{ProjectIterator.coffee => ProjectIterator.js} (100%) rename services/track-changes/app/coffee/{RedisManager.coffee => RedisManager.js} (100%) rename services/track-changes/app/coffee/{RestoreManager.coffee => RestoreManager.js} (100%) rename services/track-changes/app/coffee/{UpdateCompressor.coffee => UpdateCompressor.js} (100%) rename services/track-changes/app/coffee/{UpdateTrimmer.coffee => UpdateTrimmer.js} (100%) rename services/track-changes/app/coffee/{UpdatesManager.coffee => UpdatesManager.js} (100%) rename services/track-changes/app/coffee/{WebApiManager.coffee => WebApiManager.js} (100%) rename services/track-changes/app/coffee/{mongojs.coffee => mongojs.js} (100%) diff --git a/services/track-changes/app/coffee/DiffGenerator.coffee b/services/track-changes/app/coffee/DiffGenerator.js similarity index 100% rename from services/track-changes/app/coffee/DiffGenerator.coffee rename to services/track-changes/app/coffee/DiffGenerator.js diff --git a/services/track-changes/app/coffee/DiffManager.coffee b/services/track-changes/app/coffee/DiffManager.js similarity index 100% rename from services/track-changes/app/coffee/DiffManager.coffee rename to services/track-changes/app/coffee/DiffManager.js diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.coffee b/services/track-changes/app/coffee/DocumentUpdaterManager.js similarity index 100% rename from services/track-changes/app/coffee/DocumentUpdaterManager.coffee rename to services/track-changes/app/coffee/DocumentUpdaterManager.js diff --git a/services/track-changes/app/coffee/HealthChecker.coffee b/services/track-changes/app/coffee/HealthChecker.js similarity index 100% rename from services/track-changes/app/coffee/HealthChecker.coffee rename to services/track-changes/app/coffee/HealthChecker.js diff --git a/services/track-changes/app/coffee/HttpController.coffee b/services/track-changes/app/coffee/HttpController.js similarity index 100% rename from services/track-changes/app/coffee/HttpController.coffee rename to services/track-changes/app/coffee/HttpController.js diff --git a/services/track-changes/app/coffee/LockManager.coffee b/services/track-changes/app/coffee/LockManager.js similarity index 100% rename from services/track-changes/app/coffee/LockManager.coffee rename to services/track-changes/app/coffee/LockManager.js diff --git a/services/track-changes/app/coffee/MongoAWS.coffee b/services/track-changes/app/coffee/MongoAWS.js similarity index 100% rename from services/track-changes/app/coffee/MongoAWS.coffee rename to services/track-changes/app/coffee/MongoAWS.js diff --git a/services/track-changes/app/coffee/MongoManager.coffee b/services/track-changes/app/coffee/MongoManager.js similarity index 100% rename from services/track-changes/app/coffee/MongoManager.coffee rename to services/track-changes/app/coffee/MongoManager.js diff --git a/services/track-changes/app/coffee/PackManager.coffee b/services/track-changes/app/coffee/PackManager.js similarity index 100% rename from services/track-changes/app/coffee/PackManager.coffee rename to services/track-changes/app/coffee/PackManager.js diff --git a/services/track-changes/app/coffee/PackWorker.coffee b/services/track-changes/app/coffee/PackWorker.js similarity index 100% rename from services/track-changes/app/coffee/PackWorker.coffee rename to services/track-changes/app/coffee/PackWorker.js diff --git a/services/track-changes/app/coffee/ProjectIterator.coffee b/services/track-changes/app/coffee/ProjectIterator.js similarity index 100% rename from services/track-changes/app/coffee/ProjectIterator.coffee rename to services/track-changes/app/coffee/ProjectIterator.js diff --git a/services/track-changes/app/coffee/RedisManager.coffee b/services/track-changes/app/coffee/RedisManager.js similarity index 100% rename from services/track-changes/app/coffee/RedisManager.coffee rename to services/track-changes/app/coffee/RedisManager.js diff --git a/services/track-changes/app/coffee/RestoreManager.coffee b/services/track-changes/app/coffee/RestoreManager.js similarity index 100% rename from services/track-changes/app/coffee/RestoreManager.coffee rename to services/track-changes/app/coffee/RestoreManager.js diff --git a/services/track-changes/app/coffee/UpdateCompressor.coffee b/services/track-changes/app/coffee/UpdateCompressor.js similarity index 100% rename from services/track-changes/app/coffee/UpdateCompressor.coffee rename to services/track-changes/app/coffee/UpdateCompressor.js diff --git a/services/track-changes/app/coffee/UpdateTrimmer.coffee b/services/track-changes/app/coffee/UpdateTrimmer.js similarity index 100% rename from services/track-changes/app/coffee/UpdateTrimmer.coffee rename to services/track-changes/app/coffee/UpdateTrimmer.js diff --git a/services/track-changes/app/coffee/UpdatesManager.coffee b/services/track-changes/app/coffee/UpdatesManager.js similarity index 100% rename from services/track-changes/app/coffee/UpdatesManager.coffee rename to services/track-changes/app/coffee/UpdatesManager.js diff --git a/services/track-changes/app/coffee/WebApiManager.coffee b/services/track-changes/app/coffee/WebApiManager.js similarity index 100% rename from services/track-changes/app/coffee/WebApiManager.coffee rename to services/track-changes/app/coffee/WebApiManager.js diff --git a/services/track-changes/app/coffee/mongojs.coffee b/services/track-changes/app/coffee/mongojs.js similarity index 100% rename from services/track-changes/app/coffee/mongojs.coffee rename to services/track-changes/app/coffee/mongojs.js From 57345632e0612b9fab34ab8eb05876c6962a45d7 Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:34:04 +0100 Subject: [PATCH 466/549] decaffeinate: Convert DiffGenerator.coffee and 17 other files to JS --- .../track-changes/app/coffee/DiffGenerator.js | 446 ++++--- .../track-changes/app/coffee/DiffManager.js | 186 +-- .../app/coffee/DocumentUpdaterManager.js | 99 +- .../track-changes/app/coffee/HealthChecker.js | 106 +- .../app/coffee/HttpController.js | 314 +++-- .../track-changes/app/coffee/LockManager.js | 180 +-- services/track-changes/app/coffee/MongoAWS.js | 213 ++-- .../track-changes/app/coffee/MongoManager.js | 175 +-- .../track-changes/app/coffee/PackManager.js | 1096 ++++++++++------- .../track-changes/app/coffee/PackWorker.js | 284 +++-- .../app/coffee/ProjectIterator.js | 124 +- .../track-changes/app/coffee/RedisManager.js | 181 +-- .../app/coffee/RestoreManager.js | 34 +- .../app/coffee/UpdateCompressor.js | 410 +++--- .../track-changes/app/coffee/UpdateTrimmer.js | 63 +- .../app/coffee/UpdatesManager.js | 754 +++++++----- .../track-changes/app/coffee/WebApiManager.js | 154 ++- services/track-changes/app/coffee/mongojs.js | 15 +- 18 files changed, 2834 insertions(+), 2000 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.js b/services/track-changes/app/coffee/DiffGenerator.js index 8738621f67..c23d3c19c3 100644 --- a/services/track-changes/app/coffee/DiffGenerator.js +++ b/services/track-changes/app/coffee/DiffGenerator.js @@ -1,227 +1,293 @@ -ConsistencyError = (message) -> - error = new Error(message) - error.name = "ConsistencyError" - error.__proto__ = ConsistencyError.prototype - return error -ConsistencyError.prototype.__proto__ = Error.prototype +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let DiffGenerator; +var ConsistencyError = function(message) { + const error = new Error(message); + error.name = "ConsistencyError"; + error.__proto__ = ConsistencyError.prototype; + return error; +}; +ConsistencyError.prototype.__proto__ = Error.prototype; -logger = require "logger-sharelatex" +const logger = require("logger-sharelatex"); -module.exports = DiffGenerator = - ConsistencyError: ConsistencyError +module.exports = (DiffGenerator = { + ConsistencyError, - rewindUpdate: (content, update) -> - for op, i in update.op by -1 when op.broken isnt true - try - content = DiffGenerator.rewindOp content, op - catch e - if e instanceof ConsistencyError and i = update.op.length - 1 - # catch known case where the last op in an array has been - # merged into a later op - logger.error {err: e, update, op: JSON.stringify(op)}, "marking op as broken" - op.broken = true - else - throw e # rethrow the execption - return content + rewindUpdate(content, update) { + for (let j = update.op.length - 1, i = j; j >= 0; j--, i = j) { + const op = update.op[i]; + if (op.broken !== true) { + try { + content = DiffGenerator.rewindOp(content, op); + } catch (e) { + if (e instanceof ConsistencyError && (i = update.op.length - 1)) { + // catch known case where the last op in an array has been + // merged into a later op + logger.error({err: e, update, op: JSON.stringify(op)}, "marking op as broken"); + op.broken = true; + } else { + throw e; // rethrow the execption + } + } + } + } + return content; + }, - rewindOp: (content, op) -> - if op.i? - # ShareJS will accept an op where p > content.length when applied, - # and it applies as though p == content.length. However, the op is - # passed to us with the original p > content.length. Detect if that - # is the case with this op, and shift p back appropriately to match - # ShareJS if so. - p = op.p - max_p = content.length - op.i.length - if p > max_p - logger.warn {max_p, p}, "truncating position to content length" - p = max_p + rewindOp(content, op) { + let p; + if (op.i != null) { + // ShareJS will accept an op where p > content.length when applied, + // and it applies as though p == content.length. However, the op is + // passed to us with the original p > content.length. Detect if that + // is the case with this op, and shift p back appropriately to match + // ShareJS if so. + ({ p } = op); + const max_p = content.length - op.i.length; + if (p > max_p) { + logger.warn({max_p, p}, "truncating position to content length"); + p = max_p; + } - textToBeRemoved = content.slice(p, p + op.i.length) - if op.i != textToBeRemoved + const textToBeRemoved = content.slice(p, p + op.i.length); + if (op.i !== textToBeRemoved) { throw new ConsistencyError( - "Inserted content, '#{op.i}', does not match text to be removed, '#{textToBeRemoved}'" - ) + `Inserted content, '${op.i}', does not match text to be removed, '${textToBeRemoved}'` + ); + } - return content.slice(0, p) + content.slice(p + op.i.length) + return content.slice(0, p) + content.slice(p + op.i.length); - else if op.d? - return content.slice(0, op.p) + op.d + content.slice(op.p) + } else if (op.d != null) { + return content.slice(0, op.p) + op.d + content.slice(op.p); - else - return content + } else { + return content; + } + }, - rewindUpdates: (content, updates) -> - for update in updates.reverse() - try - content = DiffGenerator.rewindUpdate(content, update) - catch e - e.attempted_update = update # keep a record of the attempted update - throw e # rethrow the exception - return content + rewindUpdates(content, updates) { + for (let update of Array.from(updates.reverse())) { + try { + content = DiffGenerator.rewindUpdate(content, update); + } catch (e) { + e.attempted_update = update; // keep a record of the attempted update + throw e; // rethrow the exception + } + } + return content; + }, - buildDiff: (initialContent, updates) -> - diff = [ u: initialContent ] - for update in updates - diff = DiffGenerator.applyUpdateToDiff diff, update - diff = DiffGenerator.compressDiff diff - return diff + buildDiff(initialContent, updates) { + let diff = [ {u: initialContent} ]; + for (let update of Array.from(updates)) { + diff = DiffGenerator.applyUpdateToDiff(diff, update); + } + diff = DiffGenerator.compressDiff(diff); + return diff; + }, - compressDiff: (diff) -> - newDiff = [] - for part in diff - lastPart = newDiff[newDiff.length - 1] - if lastPart? and lastPart.meta?.user? and part.meta?.user? - if lastPart.i? and part.i? and lastPart.meta.user.id == part.meta.user.id - lastPart.i += part.i - lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) - lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) - else if lastPart.d? and part.d? and lastPart.meta.user.id == part.meta.user.id - lastPart.d += part.d - lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts) - lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts) - else - newDiff.push part - else - newDiff.push part - return newDiff + compressDiff(diff) { + const newDiff = []; + for (let part of Array.from(diff)) { + const lastPart = newDiff[newDiff.length - 1]; + if ((lastPart != null) && ((lastPart.meta != null ? lastPart.meta.user : undefined) != null) && ((part.meta != null ? part.meta.user : undefined) != null)) { + if ((lastPart.i != null) && (part.i != null) && (lastPart.meta.user.id === part.meta.user.id)) { + lastPart.i += part.i; + lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts); + lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts); + } else if ((lastPart.d != null) && (part.d != null) && (lastPart.meta.user.id === part.meta.user.id)) { + lastPart.d += part.d; + lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts); + lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts); + } else { + newDiff.push(part); + } + } else { + newDiff.push(part); + } + } + return newDiff; + }, - applyOpToDiff: (diff, op, meta) -> - position = 0 + applyOpToDiff(diff, op, meta) { + let consumedDiff; + const position = 0; - remainingDiff = diff.slice() - {consumedDiff, remainingDiff} = DiffGenerator._consumeToOffset(remainingDiff, op.p) - newDiff = consumedDiff + let remainingDiff = diff.slice(); + ({consumedDiff, remainingDiff} = DiffGenerator._consumeToOffset(remainingDiff, op.p)); + const newDiff = consumedDiff; - if op.i? - newDiff.push - i: op.i - meta: meta - else if op.d? - {consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteOp remainingDiff, op, meta - newDiff.push(consumedDiff...) - - newDiff.push(remainingDiff...) - - return newDiff - - applyUpdateToDiff: (diff, update) -> - for op in update.op when op.broken isnt true - diff = DiffGenerator.applyOpToDiff diff, op, update.meta - return diff - - _consumeToOffset: (remainingDiff, totalOffset) -> - consumedDiff = [] - position = 0 - while part = remainingDiff.shift() - length = DiffGenerator._getLengthOfDiffPart part - if part.d? - consumedDiff.push part - else if position + length >= totalOffset - partOffset = totalOffset - position - if partOffset > 0 - consumedDiff.push DiffGenerator._slicePart part, 0, partOffset - if partOffset < length - remainingDiff.unshift DiffGenerator._slicePart part, partOffset - break - else - position += length - consumedDiff.push part - - return { - consumedDiff: consumedDiff - remainingDiff: remainingDiff + if (op.i != null) { + newDiff.push({ + i: op.i, + meta + }); + } else if (op.d != null) { + ({consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteOp(remainingDiff, op, meta)); + newDiff.push(...Array.from(consumedDiff || [])); } - _consumeDiffAffectedByDeleteOp: (remainingDiff, deleteOp, meta) -> - consumedDiff = [] - remainingOp = deleteOp - while remainingOp and remainingDiff.length > 0 - {newPart, remainingDiff, remainingOp} = DiffGenerator._consumeDeletedPart remainingDiff, remainingOp, meta - consumedDiff.push newPart if newPart? - return { - consumedDiff: consumedDiff - remainingDiff: remainingDiff + newDiff.push(...Array.from(remainingDiff || [])); + + return newDiff; + }, + + applyUpdateToDiff(diff, update) { + for (let op of Array.from(update.op)) { + if (op.broken !== true) { + diff = DiffGenerator.applyOpToDiff(diff, op, update.meta); + } + } + return diff; + }, + + _consumeToOffset(remainingDiff, totalOffset) { + let part; + const consumedDiff = []; + let position = 0; + while ((part = remainingDiff.shift())) { + const length = DiffGenerator._getLengthOfDiffPart(part); + if (part.d != null) { + consumedDiff.push(part); + } else if ((position + length) >= totalOffset) { + const partOffset = totalOffset - position; + if (partOffset > 0) { + consumedDiff.push(DiffGenerator._slicePart(part, 0, partOffset)); + } + if (partOffset < length) { + remainingDiff.unshift(DiffGenerator._slicePart(part, partOffset)); + } + break; + } else { + position += length; + consumedDiff.push(part); + } } - _consumeDeletedPart: (remainingDiff, op, meta) -> - part = remainingDiff.shift() - partLength = DiffGenerator._getLengthOfDiffPart part + return { + consumedDiff, + remainingDiff + }; + }, - if part.d? - # Skip existing deletes - remainingOp = op - newPart = part + _consumeDiffAffectedByDeleteOp(remainingDiff, deleteOp, meta) { + const consumedDiff = []; + let remainingOp = deleteOp; + while (remainingOp && (remainingDiff.length > 0)) { + let newPart; + ({newPart, remainingDiff, remainingOp} = DiffGenerator._consumeDeletedPart(remainingDiff, remainingOp, meta)); + if (newPart != null) { consumedDiff.push(newPart); } + } + return { + consumedDiff, + remainingDiff + }; + }, - else if partLength > op.d.length - # Only the first bit of the part has been deleted - remainingPart = DiffGenerator._slicePart part, op.d.length - remainingDiff.unshift remainingPart + _consumeDeletedPart(remainingDiff, op, meta) { + let deletedContent, newPart, remainingOp; + const part = remainingDiff.shift(); + const partLength = DiffGenerator._getLengthOfDiffPart(part); - deletedContent = DiffGenerator._getContentOfPart(part).slice(0, op.d.length) - if deletedContent != op.d - throw new ConsistencyError("deleted content, '#{deletedContent}', does not match delete op, '#{op.d}'") + if (part.d != null) { + // Skip existing deletes + remainingOp = op; + newPart = part; - if part.u? - newPart = - d: op.d - meta: meta - else if part.i? - newPart = null + } else if (partLength > op.d.length) { + // Only the first bit of the part has been deleted + const remainingPart = DiffGenerator._slicePart(part, op.d.length); + remainingDiff.unshift(remainingPart); - remainingOp = null + deletedContent = DiffGenerator._getContentOfPart(part).slice(0, op.d.length); + if (deletedContent !== op.d) { + throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${op.d}'`); + } - else if partLength == op.d.length - # The entire part has been deleted, but it is the last part + if (part.u != null) { + newPart = { + d: op.d, + meta + }; + } else if (part.i != null) { + newPart = null; + } - deletedContent = DiffGenerator._getContentOfPart(part) - if deletedContent != op.d - throw new ConsistencyError("deleted content, '#{deletedContent}', does not match delete op, '#{op.d}'") + remainingOp = null; - if part.u? - newPart = - d: op.d - meta: meta - else if part.i? - newPart = null + } else if (partLength === op.d.length) { + // The entire part has been deleted, but it is the last part - remainingOp = null + deletedContent = DiffGenerator._getContentOfPart(part); + if (deletedContent !== op.d) { + throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${op.d}'`); + } - else if partLength < op.d.length - # The entire part has been deleted and there is more + if (part.u != null) { + newPart = { + d: op.d, + meta + }; + } else if (part.i != null) { + newPart = null; + } - deletedContent = DiffGenerator._getContentOfPart(part) - opContent = op.d.slice(0, deletedContent.length) - if deletedContent != opContent - throw new ConsistencyError("deleted content, '#{deletedContent}', does not match delete op, '#{opContent}'") + remainingOp = null; - if part.u - newPart = - d: part.u - meta: meta - else if part.i? - newPart = null + } else if (partLength < op.d.length) { + // The entire part has been deleted and there is more + + deletedContent = DiffGenerator._getContentOfPart(part); + const opContent = op.d.slice(0, deletedContent.length); + if (deletedContent !== opContent) { + throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${opContent}'`); + } + + if (part.u) { + newPart = { + d: part.u, + meta + }; + } else if (part.i != null) { + newPart = null; + } remainingOp = - p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)) - - return { - newPart: newPart - remainingDiff: remainingDiff - remainingOp: remainingOp + {p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part))}; } - _slicePart: (basePart, from, to) -> - if basePart.u? - part = { u: basePart.u.slice(from, to) } - else if basePart.i? - part = { i: basePart.i.slice(from, to) } - if basePart.meta? - part.meta = basePart.meta - return part + return { + newPart, + remainingDiff, + remainingOp + }; + }, - _getLengthOfDiffPart: (part) -> - (part.u or part.d or part.i or '').length + _slicePart(basePart, from, to) { + let part; + if (basePart.u != null) { + part = { u: basePart.u.slice(from, to) }; + } else if (basePart.i != null) { + part = { i: basePart.i.slice(from, to) }; + } + if (basePart.meta != null) { + part.meta = basePart.meta; + } + return part; + }, - _getContentOfPart: (part) -> - part.u or part.d or part.i or '' + _getLengthOfDiffPart(part) { + return (part.u || part.d || part.i || '').length; + }, + + _getContentOfPart(part) { + return part.u || part.d || part.i || ''; + } +}); diff --git a/services/track-changes/app/coffee/DiffManager.js b/services/track-changes/app/coffee/DiffManager.js index af1cfaf03f..bfd28400a3 100644 --- a/services/track-changes/app/coffee/DiffManager.js +++ b/services/track-changes/app/coffee/DiffManager.js @@ -1,88 +1,128 @@ -UpdatesManager = require "./UpdatesManager" -DocumentUpdaterManager = require "./DocumentUpdaterManager" -DiffGenerator = require "./DiffGenerator" -logger = require "logger-sharelatex" +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let DiffManager; +const UpdatesManager = require("./UpdatesManager"); +const DocumentUpdaterManager = require("./DocumentUpdaterManager"); +const DiffGenerator = require("./DiffGenerator"); +const logger = require("logger-sharelatex"); -module.exports = DiffManager = - getLatestDocAndUpdates: (project_id, doc_id, fromVersion, callback = (error, content, version, updates) ->) -> - # Get updates last, since then they must be ahead and it - # might be possible to rewind to the same version as the doc. - DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) -> - return callback(error) if error? - if !fromVersion? # If we haven't been given a version, just return lastest doc and no updates - return callback(null, content, version, []) - UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, (error, updates) -> - return callback(error) if error? - callback(null, content, version, updates) +module.exports = (DiffManager = { + getLatestDocAndUpdates(project_id, doc_id, fromVersion, callback) { + // Get updates last, since then they must be ahead and it + // might be possible to rewind to the same version as the doc. + if (callback == null) { callback = function(error, content, version, updates) {}; } + return DocumentUpdaterManager.getDocument(project_id, doc_id, function(error, content, version) { + if (error != null) { return callback(error); } + if ((fromVersion == null)) { // If we haven't been given a version, just return lastest doc and no updates + return callback(null, content, version, []); + } + return UpdatesManager.getDocUpdatesWithUserInfo(project_id, doc_id, {from: fromVersion}, function(error, updates) { + if (error != null) { return callback(error); } + return callback(null, content, version, updates); + }); + }); + }, - getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) -> - DiffManager.getDocumentBeforeVersion project_id, doc_id, fromVersion, (error, startingContent, updates) -> - if error? - if error.message == "broken-history" - return callback(null, "history unavailable") - else - return callback(error) + getDiff(project_id, doc_id, fromVersion, toVersion, callback) { + if (callback == null) { callback = function(error, diff) {}; } + return DiffManager.getDocumentBeforeVersion(project_id, doc_id, fromVersion, function(error, startingContent, updates) { + let diff; + if (error != null) { + if (error.message === "broken-history") { + return callback(null, "history unavailable"); + } else { + return callback(error); + } + } - updatesToApply = [] - for update in updates.slice().reverse() - if update.v <= toVersion - updatesToApply.push update + const updatesToApply = []; + for (let update of Array.from(updates.slice().reverse())) { + if (update.v <= toVersion) { + updatesToApply.push(update); + } + } - try - diff = DiffGenerator.buildDiff startingContent, updatesToApply - catch e - return callback(e) + try { + diff = DiffGenerator.buildDiff(startingContent, updatesToApply); + } catch (e) { + return callback(e); + } - callback(null, diff) + return callback(null, diff); + }); + }, - getDocumentBeforeVersion: (project_id, doc_id, version, _callback = (error, document, rewoundUpdates) ->) -> - # Whichever order we get the latest document and the latest updates, - # there is potential for updates to be applied between them so that - # they do not return the same 'latest' versions. - # If this happens, we just retry and hopefully get them at the compatible - # versions. - retries = 3 - callback = (error, args...) -> - if error? - if error.retry and retries > 0 - logger.warn {error, project_id, doc_id, version, retries}, "retrying getDocumentBeforeVersion" - retry() - else - _callback(error) - else - _callback(null, args...) + getDocumentBeforeVersion(project_id, doc_id, version, _callback) { + // Whichever order we get the latest document and the latest updates, + // there is potential for updates to be applied between them so that + // they do not return the same 'latest' versions. + // If this happens, we just retry and hopefully get them at the compatible + // versions. + let retry; + if (_callback == null) { _callback = function(error, document, rewoundUpdates) {}; } + let retries = 3; + const callback = function(error, ...args) { + if (error != null) { + if (error.retry && (retries > 0)) { + logger.warn({error, project_id, doc_id, version, retries}, "retrying getDocumentBeforeVersion"); + return retry(); + } else { + return _callback(error); + } + } else { + return _callback(null, ...Array.from(args)); + } + }; - do retry = () -> - retries-- - DiffManager._tryGetDocumentBeforeVersion(project_id, doc_id, version, callback) + return (retry = function() { + retries--; + return DiffManager._tryGetDocumentBeforeVersion(project_id, doc_id, version, callback); + })(); + }, - _tryGetDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) -> - logger.log project_id: project_id, doc_id: doc_id, version: version, "getting document before version" - DiffManager.getLatestDocAndUpdates project_id, doc_id, version, (error, content, version, updates) -> - return callback(error) if error? + _tryGetDocumentBeforeVersion(project_id, doc_id, version, callback) { + if (callback == null) { callback = function(error, document, rewoundUpdates) {}; } + logger.log({project_id, doc_id, version}, "getting document before version"); + return DiffManager.getLatestDocAndUpdates(project_id, doc_id, version, function(error, content, version, updates) { + let startingContent; + if (error != null) { return callback(error); } - # bail out if we hit a broken update - for u in updates when u.broken - return callback new Error "broken-history" + // bail out if we hit a broken update + for (let u of Array.from(updates)) { + if (u.broken) { + return callback(new Error("broken-history")); + } + } - # discard any updates which are ahead of this document version - while updates[0]?.v >= version - updates.shift() + // discard any updates which are ahead of this document version + while ((updates[0] != null ? updates[0].v : undefined) >= version) { + updates.shift(); + } - lastUpdate = updates[0] - if lastUpdate? and lastUpdate.v != version - 1 - error = new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}") - error.retry = true - return callback error + const lastUpdate = updates[0]; + if ((lastUpdate != null) && (lastUpdate.v !== (version - 1))) { + error = new Error(`latest update version, ${lastUpdate.v}, does not match doc version, ${version}`); + error.retry = true; + return callback(error); + } - logger.log {docVersion: version, lastUpdateVersion: lastUpdate?.v, updateCount: updates.length}, "rewinding updates" + logger.log({docVersion: version, lastUpdateVersion: (lastUpdate != null ? lastUpdate.v : undefined), updateCount: updates.length}, "rewinding updates"); - tryUpdates = updates.slice().reverse() + const tryUpdates = updates.slice().reverse(); - try - startingContent = DiffGenerator.rewindUpdates content, tryUpdates - # tryUpdates is reversed, and any unapplied ops are marked as broken - catch e - return callback(e) + try { + startingContent = DiffGenerator.rewindUpdates(content, tryUpdates); + // tryUpdates is reversed, and any unapplied ops are marked as broken + } catch (e) { + return callback(e); + } - callback(null, startingContent, tryUpdates) + return callback(null, startingContent, tryUpdates); + }); + } +}); diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.js b/services/track-changes/app/coffee/DocumentUpdaterManager.js index a9fc4981c5..9c0cd66067 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.js +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.js @@ -1,42 +1,63 @@ -request = require "request" -logger = require "logger-sharelatex" -Settings = require "settings-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let DocumentUpdaterManager; +const request = require("request"); +const logger = require("logger-sharelatex"); +const Settings = require("settings-sharelatex"); -module.exports = DocumentUpdaterManager = - getDocument: (project_id, doc_id, callback = (error, content, version) ->) -> - url = "#{Settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}" - logger.log project_id:project_id, doc_id: doc_id, "getting doc from document updater" - request.get url, (error, res, body)-> - if error? - return callback(error) - if res.statusCode >= 200 and res.statusCode < 300 - try - body = JSON.parse(body) - catch error - return callback(error) - logger.log {project_id, doc_id, version: body.version}, "got doc from document updater" - callback null, body.lines.join("\n"), body.version - else - error = new Error("doc updater returned a non-success status code: #{res.statusCode}") - logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" - callback error +module.exports = (DocumentUpdaterManager = { + getDocument(project_id, doc_id, callback) { + if (callback == null) { callback = function(error, content, version) {}; } + const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}`; + logger.log({project_id, doc_id}, "getting doc from document updater"); + return request.get(url, function(error, res, body){ + if (error != null) { + return callback(error); + } + if ((res.statusCode >= 200) && (res.statusCode < 300)) { + try { + body = JSON.parse(body); + } catch (error1) { + error = error1; + return callback(error); + } + logger.log({project_id, doc_id, version: body.version}, "got doc from document updater"); + return callback(null, body.lines.join("\n"), body.version); + } else { + error = new Error(`doc updater returned a non-success status code: ${res.statusCode}`); + logger.error({err: error, project_id, doc_id, url}, "error accessing doc updater"); + return callback(error); + } + }); + }, - setDocument: (project_id, doc_id, content, user_id, callback = (error) ->) -> - url = "#{Settings.apis.documentupdater.url}/project/#{project_id}/doc/#{doc_id}" - logger.log project_id:project_id, doc_id: doc_id, "setting doc in document updater" - request.post { - url: url - json: - lines: content.split("\n") - source: "restore" - user_id: user_id + setDocument(project_id, doc_id, content, user_id, callback) { + if (callback == null) { callback = function(error) {}; } + const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}`; + logger.log({project_id, doc_id}, "setting doc in document updater"); + return request.post({ + url, + json: { + lines: content.split("\n"), + source: "restore", + user_id, undoing: true - }, (error, res, body)-> - if error? - return callback(error) - if res.statusCode >= 200 and res.statusCode < 300 - callback null - else - error = new Error("doc updater returned a non-success status code: #{res.statusCode}") - logger.error err: error, project_id:project_id, doc_id:doc_id, url: url, "error accessing doc updater" - callback error \ No newline at end of file + } + }, function(error, res, body){ + if (error != null) { + return callback(error); + } + if ((res.statusCode >= 200) && (res.statusCode < 300)) { + return callback(null); + } else { + error = new Error(`doc updater returned a non-success status code: ${res.statusCode}`); + logger.error({err: error, project_id, doc_id, url}, "error accessing doc updater"); + return callback(error); + } + }); + } +}); \ No newline at end of file diff --git a/services/track-changes/app/coffee/HealthChecker.js b/services/track-changes/app/coffee/HealthChecker.js index 76165c9954..ae008adfe6 100644 --- a/services/track-changes/app/coffee/HealthChecker.js +++ b/services/track-changes/app/coffee/HealthChecker.js @@ -1,46 +1,64 @@ -ObjectId = require("mongojs").ObjectId -request = require("request") -async = require("async") -settings = require("settings-sharelatex") -port = settings.internal.trackchanges.port -logger = require "logger-sharelatex" -LockManager = require "./LockManager" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const { ObjectId } = require("mongojs"); +const request = require("request"); +const async = require("async"); +const settings = require("settings-sharelatex"); +const { port } = settings.internal.trackchanges; +const logger = require("logger-sharelatex"); +const LockManager = require("./LockManager"); -module.exports = - check : (callback)-> - project_id = ObjectId(settings.trackchanges.healthCheck.project_id) - url = "http://localhost:#{port}/project/#{project_id}" - logger.log project_id:project_id, "running health check" - jobs = [ - (cb)-> - request.get {url:"http://localhost:#{port}/check_lock", timeout:3000}, (err, res, body) -> - if err? - logger.err err:err, project_id:project_id, "error checking lock for health check" - cb(err) - else if res?.statusCode != 200 - cb("status code not 200, it's #{res.statusCode}") - else - cb() - (cb)-> - request.post {url:"#{url}/flush", timeout:10000}, (err, res, body) -> - if err? - logger.err err:err, project_id:project_id, "error flushing for health check" - cb(err) - else if res?.statusCode != 204 - cb("status code not 204, it's #{res.statusCode}") - else - cb() - (cb)-> - request.get {url:"#{url}/updates", timeout:10000}, (err, res, body)-> - if err? - logger.err err:err, project_id:project_id, "error getting updates for health check" - cb(err) - else if res?.statusCode != 200 - cb("status code not 200, it's #{res.statusCode}") - else - cb() - ] - async.series jobs, callback +module.exports = { + check(callback){ + const project_id = ObjectId(settings.trackchanges.healthCheck.project_id); + const url = `http://localhost:${port}/project/${project_id}`; + logger.log({project_id}, "running health check"); + const jobs = [ + cb=> + request.get({url:`http://localhost:${port}/check_lock`, timeout:3000}, function(err, res, body) { + if (err != null) { + logger.err({err, project_id}, "error checking lock for health check"); + return cb(err); + } else if ((res != null ? res.statusCode : undefined) !== 200) { + return cb(`status code not 200, it's ${res.statusCode}`); + } else { + return cb(); + } + }) + , + cb=> + request.post({url:`${url}/flush`, timeout:10000}, function(err, res, body) { + if (err != null) { + logger.err({err, project_id}, "error flushing for health check"); + return cb(err); + } else if ((res != null ? res.statusCode : undefined) !== 204) { + return cb(`status code not 204, it's ${res.statusCode}`); + } else { + return cb(); + } + }) + , + cb=> + request.get({url:`${url}/updates`, timeout:10000}, function(err, res, body){ + if (err != null) { + logger.err({err, project_id}, "error getting updates for health check"); + return cb(err); + } else if ((res != null ? res.statusCode : undefined) !== 200) { + return cb(`status code not 200, it's ${res.statusCode}`); + } else { + return cb(); + } + }) + + ]; + return async.series(jobs, callback); + }, - checkLock: (callback) -> - LockManager.healthCheck callback + checkLock(callback) { + return LockManager.healthCheck(callback); + } +}; diff --git a/services/track-changes/app/coffee/HttpController.js b/services/track-changes/app/coffee/HttpController.js index b931c41026..1526c0ce4c 100644 --- a/services/track-changes/app/coffee/HttpController.js +++ b/services/track-changes/app/coffee/HttpController.js @@ -1,137 +1,195 @@ -UpdatesManager = require "./UpdatesManager" -DiffManager = require "./DiffManager" -PackManager = require "./PackManager" -RestoreManager = require "./RestoreManager" -logger = require "logger-sharelatex" -HealthChecker = require "./HealthChecker" -_ = require "underscore" +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let HttpController; +const UpdatesManager = require("./UpdatesManager"); +const DiffManager = require("./DiffManager"); +const PackManager = require("./PackManager"); +const RestoreManager = require("./RestoreManager"); +const logger = require("logger-sharelatex"); +const HealthChecker = require("./HealthChecker"); +const _ = require("underscore"); -module.exports = HttpController = - flushDoc: (req, res, next = (error) ->) -> - doc_id = req.params.doc_id - project_id = req.params.project_id - logger.log project_id: project_id, doc_id: doc_id, "compressing doc history" - UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> - return next(error) if error? - res.send 204 +module.exports = (HttpController = { + flushDoc(req, res, next) { + if (next == null) { next = function(error) {}; } + const { doc_id } = req.params; + const { project_id } = req.params; + logger.log({project_id, doc_id}, "compressing doc history"); + return UpdatesManager.processUncompressedUpdatesWithLock(project_id, doc_id, function(error) { + if (error != null) { return next(error); } + return res.send(204); + }); + }, - flushProject: (req, res, next = (error) ->) -> - project_id = req.params.project_id - logger.log project_id: project_id, "compressing project history" - UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> - return next(error) if error? - res.send 204 + flushProject(req, res, next) { + if (next == null) { next = function(error) {}; } + const { project_id } = req.params; + logger.log({project_id}, "compressing project history"); + return UpdatesManager.processUncompressedUpdatesForProject(project_id, function(error) { + if (error != null) { return next(error); } + return res.send(204); + }); + }, - flushAll: (req, res, next = (error) ->) -> - # limit on projects to flush or -1 for all (default) - limit = if req.query.limit? then parseInt(req.query.limit, 10) else -1 - logger.log {limit: limit}, "flushing all projects" - UpdatesManager.flushAll limit, (error, result) -> - return next(error) if error? - {failed, succeeded, all} = result - status = "#{succeeded.length} succeeded, #{failed.length} failed" - if limit == 0 - res.status(200).send "#{status}\nwould flush:\n#{all.join('\n')}\n" - else if failed.length > 0 - logger.log {failed: failed, succeeded: succeeded}, "error flushing projects" - res.status(500).send "#{status}\nfailed to flush:\n#{failed.join('\n')}\n" - else - res.status(200).send "#{status}\nflushed #{succeeded.length} projects of #{all.length}\n" - - checkDanglingUpdates: (req, res, next = (error) ->) -> - logger.log "checking dangling updates" - UpdatesManager.getDanglingUpdates (error, result) -> - return next(error) if error? - if result.length > 0 - logger.log {dangling: result}, "found dangling updates" - res.status(500).send "dangling updates:\n#{result.join('\n')}\n" - else - res.status(200).send "no dangling updates found\n" - - checkDoc: (req, res, next = (error) ->) -> - doc_id = req.params.doc_id - project_id = req.params.project_id - logger.log project_id: project_id, doc_id: doc_id, "checking doc history" - DiffManager.getDocumentBeforeVersion project_id, doc_id, 1, (error, document, rewoundUpdates) -> - return next(error) if error? - broken = [] - for update in rewoundUpdates - for op in update.op when op.broken is true - broken.push op - if broken.length > 0 - res.send broken - else - res.send 204 - - getDiff: (req, res, next = (error) ->) -> - doc_id = req.params.doc_id - project_id = req.params.project_id - - if req.query.from? - from = parseInt(req.query.from, 10) - else - from = null - if req.query.to? - to = parseInt(req.query.to, 10) - else - to = null - - logger.log {project_id, doc_id, from, to}, "getting diff" - DiffManager.getDiff project_id, doc_id, from, to, (error, diff) -> - return next(error) if error? - res.json {diff: diff} - - getUpdates: (req, res, next = (error) ->) -> - project_id = req.params.project_id - - if req.query.before? - before = parseInt(req.query.before, 10) - if req.query.min_count? - min_count = parseInt(req.query.min_count, 10) - - UpdatesManager.getSummarizedProjectUpdates project_id, before: before, min_count: min_count, (error, updates, nextBeforeTimestamp) -> - return next(error) if error? - res.json { - updates: updates - nextBeforeTimestamp: nextBeforeTimestamp + flushAll(req, res, next) { + // limit on projects to flush or -1 for all (default) + if (next == null) { next = function(error) {}; } + const limit = (req.query.limit != null) ? parseInt(req.query.limit, 10) : -1; + logger.log({limit}, "flushing all projects"); + return UpdatesManager.flushAll(limit, function(error, result) { + if (error != null) { return next(error); } + const {failed, succeeded, all} = result; + const status = `${succeeded.length} succeeded, ${failed.length} failed`; + if (limit === 0) { + return res.status(200).send(`${status}\nwould flush:\n${all.join('\n')}\n`); + } else if (failed.length > 0) { + logger.log({failed, succeeded}, "error flushing projects"); + return res.status(500).send(`${status}\nfailed to flush:\n${failed.join('\n')}\n`); + } else { + return res.status(200).send(`${status}\nflushed ${succeeded.length} projects of ${all.length}\n`); } + }); + }, - restore: (req, res, next = (error) ->) -> - {doc_id, project_id, version} = req.params - user_id = req.headers["x-user-id"] - version = parseInt(version, 10) - RestoreManager.restoreToBeforeVersion project_id, doc_id, version, user_id, (error) -> - return next(error) if error? - res.send 204 + checkDanglingUpdates(req, res, next) { + if (next == null) { next = function(error) {}; } + logger.log("checking dangling updates"); + return UpdatesManager.getDanglingUpdates(function(error, result) { + if (error != null) { return next(error); } + if (result.length > 0) { + logger.log({dangling: result}, "found dangling updates"); + return res.status(500).send(`dangling updates:\n${result.join('\n')}\n`); + } else { + return res.status(200).send("no dangling updates found\n"); + } + }); + }, - pushDocHistory: (req, res, next = (error) ->) -> - project_id = req.params.project_id - doc_id = req.params.doc_id - logger.log {project_id, doc_id}, "pushing all finalised changes to s3" - PackManager.pushOldPacks project_id, doc_id, (error) -> - return next(error) if error? - res.send 204 + checkDoc(req, res, next) { + if (next == null) { next = function(error) {}; } + const { doc_id } = req.params; + const { project_id } = req.params; + logger.log({project_id, doc_id}, "checking doc history"); + return DiffManager.getDocumentBeforeVersion(project_id, doc_id, 1, function(error, document, rewoundUpdates) { + if (error != null) { return next(error); } + const broken = []; + for (let update of Array.from(rewoundUpdates)) { + for (let op of Array.from(update.op)) { + if (op.broken === true) { + broken.push(op); + } + } + } + if (broken.length > 0) { + return res.send(broken); + } else { + return res.send(204); + } + }); + }, - pullDocHistory: (req, res, next = (error) ->) -> - project_id = req.params.project_id - doc_id = req.params.doc_id - logger.log {project_id, doc_id}, "pulling all packs from s3" - PackManager.pullOldPacks project_id, doc_id, (error) -> - return next(error) if error? - res.send 204 + getDiff(req, res, next) { + let from, to; + if (next == null) { next = function(error) {}; } + const { doc_id } = req.params; + const { project_id } = req.params; - healthCheck: (req, res)-> - HealthChecker.check (err)-> - if err? - logger.err err:err, "error performing health check" - res.send 500 - else - res.send 200 + if (req.query.from != null) { + from = parseInt(req.query.from, 10); + } else { + from = null; + } + if (req.query.to != null) { + to = parseInt(req.query.to, 10); + } else { + to = null; + } - checkLock: (req, res)-> - HealthChecker.checkLock (err) -> - if err? - logger.err err:err, "error performing lock check" - res.send 500 - else - res.send 200 + logger.log({project_id, doc_id, from, to}, "getting diff"); + return DiffManager.getDiff(project_id, doc_id, from, to, function(error, diff) { + if (error != null) { return next(error); } + return res.json({diff}); + }); + }, + + getUpdates(req, res, next) { + let before, min_count; + if (next == null) { next = function(error) {}; } + const { project_id } = req.params; + + if (req.query.before != null) { + before = parseInt(req.query.before, 10); + } + if (req.query.min_count != null) { + min_count = parseInt(req.query.min_count, 10); + } + + return UpdatesManager.getSummarizedProjectUpdates(project_id, {before, min_count}, function(error, updates, nextBeforeTimestamp) { + if (error != null) { return next(error); } + return res.json({ + updates, + nextBeforeTimestamp + }); + }); + }, + + restore(req, res, next) { + if (next == null) { next = function(error) {}; } + let {doc_id, project_id, version} = req.params; + const user_id = req.headers["x-user-id"]; + version = parseInt(version, 10); + return RestoreManager.restoreToBeforeVersion(project_id, doc_id, version, user_id, function(error) { + if (error != null) { return next(error); } + return res.send(204); + }); + }, + + pushDocHistory(req, res, next) { + if (next == null) { next = function(error) {}; } + const { project_id } = req.params; + const { doc_id } = req.params; + logger.log({project_id, doc_id}, "pushing all finalised changes to s3"); + return PackManager.pushOldPacks(project_id, doc_id, function(error) { + if (error != null) { return next(error); } + return res.send(204); + }); + }, + + pullDocHistory(req, res, next) { + if (next == null) { next = function(error) {}; } + const { project_id } = req.params; + const { doc_id } = req.params; + logger.log({project_id, doc_id}, "pulling all packs from s3"); + return PackManager.pullOldPacks(project_id, doc_id, function(error) { + if (error != null) { return next(error); } + return res.send(204); + }); + }, + + healthCheck(req, res){ + return HealthChecker.check(function(err){ + if (err != null) { + logger.err({err}, "error performing health check"); + return res.send(500); + } else { + return res.send(200); + } + }); + }, + + checkLock(req, res){ + return HealthChecker.checkLock(function(err) { + if (err != null) { + logger.err({err}, "error performing lock check"); + return res.send(500); + } else { + return res.send(200); + } + }); + } +}); diff --git a/services/track-changes/app/coffee/LockManager.js b/services/track-changes/app/coffee/LockManager.js index fe4afaeba9..51770dbb32 100644 --- a/services/track-changes/app/coffee/LockManager.js +++ b/services/track-changes/app/coffee/LockManager.js @@ -1,85 +1,119 @@ -Settings = require "settings-sharelatex" -redis = require("redis-sharelatex") -rclient = redis.createClient(Settings.redis.lock) -os = require "os" -crypto = require "crypto" -logger = require "logger-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let LockManager; +const Settings = require("settings-sharelatex"); +const redis = require("redis-sharelatex"); +const rclient = redis.createClient(Settings.redis.lock); +const os = require("os"); +const crypto = require("crypto"); +const logger = require("logger-sharelatex"); -HOST = os.hostname() -PID = process.pid -RND = crypto.randomBytes(4).toString('hex') -COUNT = 0 +const HOST = os.hostname(); +const PID = process.pid; +const RND = crypto.randomBytes(4).toString('hex'); +let COUNT = 0; -module.exports = LockManager = - LOCK_TEST_INTERVAL: 50 # 50ms between each test of the lock - MAX_LOCK_WAIT_TIME: 10000 # 10s maximum time to spend trying to get the lock - LOCK_TTL: 300 # seconds (allow 5 minutes for any operation to complete) +module.exports = (LockManager = { + LOCK_TEST_INTERVAL: 50, // 50ms between each test of the lock + MAX_LOCK_WAIT_TIME: 10000, // 10s maximum time to spend trying to get the lock + LOCK_TTL: 300, // seconds (allow 5 minutes for any operation to complete) - # Use a signed lock value as described in - # http://redis.io/topics/distlock#correct-implementation-with-a-single-instance - # to prevent accidental unlocking by multiple processes - randomLock : () -> - time = Date.now() - return "locked:host=#{HOST}:pid=#{PID}:random=#{RND}:time=#{time}:count=#{COUNT++}" + // Use a signed lock value as described in + // http://redis.io/topics/distlock#correct-implementation-with-a-single-instance + // to prevent accidental unlocking by multiple processes + randomLock() { + const time = Date.now(); + return `locked:host=${HOST}:pid=${PID}:random=${RND}:time=${time}:count=${COUNT++}`; + }, - unlockScript: 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end'; + unlockScript: 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end', - tryLock : (key, callback = (err, gotLock) ->) -> - lockValue = LockManager.randomLock() - rclient.set key, lockValue, "EX", @LOCK_TTL, "NX", (err, gotLock)-> - return callback(err) if err? - if gotLock == "OK" - callback err, true, lockValue - else - callback err, false + tryLock(key, callback) { + if (callback == null) { callback = function(err, gotLock) {}; } + const lockValue = LockManager.randomLock(); + return rclient.set(key, lockValue, "EX", this.LOCK_TTL, "NX", function(err, gotLock){ + if (err != null) { return callback(err); } + if (gotLock === "OK") { + return callback(err, true, lockValue); + } else { + return callback(err, false); + } + }); + }, - getLock: (key, callback = (error) ->) -> - startTime = Date.now() - do attempt = () -> - if Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME - e = new Error("Timeout") - e.key = key - return callback(e) + getLock(key, callback) { + let attempt; + if (callback == null) { callback = function(error) {}; } + const startTime = Date.now(); + return (attempt = function() { + if ((Date.now() - startTime) > LockManager.MAX_LOCK_WAIT_TIME) { + const e = new Error("Timeout"); + e.key = key; + return callback(e); + } - LockManager.tryLock key, (error, gotLock, lockValue) -> - return callback(error) if error? - if gotLock - callback(null, lockValue) - else - setTimeout attempt, LockManager.LOCK_TEST_INTERVAL + return LockManager.tryLock(key, function(error, gotLock, lockValue) { + if (error != null) { return callback(error); } + if (gotLock) { + return callback(null, lockValue); + } else { + return setTimeout(attempt, LockManager.LOCK_TEST_INTERVAL); + } + }); + })(); + }, - checkLock: (key, callback = (err, isFree) ->) -> - rclient.exists key, (err, exists) -> - return callback(err) if err? - exists = parseInt exists - if exists == 1 - callback err, false - else - callback err, true + checkLock(key, callback) { + if (callback == null) { callback = function(err, isFree) {}; } + return rclient.exists(key, function(err, exists) { + if (err != null) { return callback(err); } + exists = parseInt(exists); + if (exists === 1) { + return callback(err, false); + } else { + return callback(err, true); + } + }); + }, - releaseLock: (key, lockValue, callback) -> - rclient.eval LockManager.unlockScript, 1, key, lockValue, (err, result) -> - if err? - return callback(err) - if result? and result isnt 1 # successful unlock should release exactly one key - logger.error {key:key, lockValue:lockValue, redis_err:err, redis_result:result}, "unlocking error" - return callback(new Error("tried to release timed out lock")) - callback(err,result) + releaseLock(key, lockValue, callback) { + return rclient.eval(LockManager.unlockScript, 1, key, lockValue, function(err, result) { + if (err != null) { + return callback(err); + } + if ((result != null) && (result !== 1)) { // successful unlock should release exactly one key + logger.error({key, lockValue, redis_err:err, redis_result:result}, "unlocking error"); + return callback(new Error("tried to release timed out lock")); + } + return callback(err,result); + }); + }, - runWithLock: (key, runner, callback = ( (error) -> )) -> - LockManager.getLock key, (error, lockValue) -> - return callback(error) if error? - runner (error1) -> - LockManager.releaseLock key, lockValue, (error2) -> - error = error1 or error2 - return callback(error) if error? - callback() + runWithLock(key, runner, callback) { + if (callback == null) { callback = function(error) {}; } + return LockManager.getLock(key, function(error, lockValue) { + if (error != null) { return callback(error); } + return runner(error1 => + LockManager.releaseLock(key, lockValue, function(error2) { + error = error1 || error2; + if (error != null) { return callback(error); } + return callback(); + }) + ); + }); + }, - healthCheck: (callback) -> - action = (releaseLock) -> - releaseLock() - LockManager.runWithLock "HistoryLock:HealthCheck:host=#{HOST}:pid=#{PID}:random=#{RND}", action, callback + healthCheck(callback) { + const action = releaseLock => releaseLock(); + return LockManager.runWithLock(`HistoryLock:HealthCheck:host=${HOST}:pid=${PID}:random=${RND}`, action, callback); + }, - close: (callback) -> - rclient.quit() - rclient.once 'end', callback + close(callback) { + rclient.quit(); + return rclient.once('end', callback); + } +}); diff --git a/services/track-changes/app/coffee/MongoAWS.js b/services/track-changes/app/coffee/MongoAWS.js index 0223a89981..5abd255322 100644 --- a/services/track-changes/app/coffee/MongoAWS.js +++ b/services/track-changes/app/coffee/MongoAWS.js @@ -1,118 +1,141 @@ -settings = require "settings-sharelatex" -logger = require "logger-sharelatex" -AWS = require 'aws-sdk' -S3S = require 's3-streams' -{db, ObjectId} = require "./mongojs" -JSONStream = require "JSONStream" -ReadlineStream = require "byline" -zlib = require "zlib" -Metrics = require "metrics-sharelatex" +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let MongoAWS; +const settings = require("settings-sharelatex"); +const logger = require("logger-sharelatex"); +const AWS = require('aws-sdk'); +const S3S = require('s3-streams'); +const {db, ObjectId} = require("./mongojs"); +const JSONStream = require("JSONStream"); +const ReadlineStream = require("byline"); +const zlib = require("zlib"); +const Metrics = require("metrics-sharelatex"); -DAYS = 24 * 3600 * 1000 # one day in milliseconds +const DAYS = 24 * 3600 * 1000; // one day in milliseconds -createStream = (streamConstructor, project_id, doc_id, pack_id) -> - AWS_CONFIG = - accessKeyId: settings.trackchanges.s3.key - secretAccessKey: settings.trackchanges.s3.secret - endpoint: settings.trackchanges.s3.endpoint +const createStream = function(streamConstructor, project_id, doc_id, pack_id) { + const AWS_CONFIG = { + accessKeyId: settings.trackchanges.s3.key, + secretAccessKey: settings.trackchanges.s3.secret, + endpoint: settings.trackchanges.s3.endpoint, s3ForcePathStyle: settings.trackchanges.s3.pathStyle + }; - return streamConstructor new AWS.S3(AWS_CONFIG), { + return streamConstructor(new AWS.S3(AWS_CONFIG), { "Bucket": settings.trackchanges.stores.doc_history, "Key": project_id+"/changes-"+doc_id+"/pack-"+pack_id - } + }); +}; -module.exports = MongoAWS = +module.exports = (MongoAWS = { - archivePack: (project_id, doc_id, pack_id, _callback = (error) ->) -> + archivePack(project_id, doc_id, pack_id, _callback) { - callback = (args...) -> - _callback(args...) - _callback = () -> + if (_callback == null) { _callback = function(error) {}; } + const callback = function(...args) { + _callback(...Array.from(args || [])); + return _callback = function() {}; + }; - query = { - _id: ObjectId(pack_id) + const query = { + _id: ObjectId(pack_id), doc_id: ObjectId(doc_id) - } + }; - return callback new Error("invalid project id") if not project_id? - return callback new Error("invalid doc id") if not doc_id? - return callback new Error("invalid pack id") if not pack_id? + if ((project_id == null)) { return callback(new Error("invalid project id")); } + if ((doc_id == null)) { return callback(new Error("invalid doc id")); } + if ((pack_id == null)) { return callback(new Error("invalid pack id")); } - logger.log {project_id, doc_id, pack_id}, "uploading data to s3" + logger.log({project_id, doc_id, pack_id}, "uploading data to s3"); - upload = createStream S3S.WriteStream, project_id, doc_id, pack_id + const upload = createStream(S3S.WriteStream, project_id, doc_id, pack_id); - db.docHistory.findOne query, (err, result) -> - return callback(err) if err? - return callback new Error("cannot find pack to send to s3") if not result? - return callback new Error("refusing to send pack with TTL to s3") if result.expiresAt? - uncompressedData = JSON.stringify(result) - if uncompressedData.indexOf("\u0000") != -1 - error = new Error("null bytes found in upload") - logger.error err: error, project_id: project_id, doc_id: doc_id, pack_id: pack_id, error.message - return callback(error) - zlib.gzip uncompressedData, (err, buf) -> - logger.log {project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack" - return callback(err) if err? - upload.on 'error', (err) -> - callback(err) - upload.on 'finish', () -> - Metrics.inc("archive-pack") - logger.log {project_id, doc_id, pack_id}, "upload to s3 completed" - callback(null) - upload.write buf - upload.end() + return db.docHistory.findOne(query, function(err, result) { + if (err != null) { return callback(err); } + if ((result == null)) { return callback(new Error("cannot find pack to send to s3")); } + if (result.expiresAt != null) { return callback(new Error("refusing to send pack with TTL to s3")); } + const uncompressedData = JSON.stringify(result); + if (uncompressedData.indexOf("\u0000") !== -1) { + const error = new Error("null bytes found in upload"); + logger.error({err: error, project_id, doc_id, pack_id}, error.message); + return callback(error); + } + return zlib.gzip(uncompressedData, function(err, buf) { + logger.log({project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack"); + if (err != null) { return callback(err); } + upload.on('error', err => callback(err)); + upload.on('finish', function() { + Metrics.inc("archive-pack"); + logger.log({project_id, doc_id, pack_id}, "upload to s3 completed"); + return callback(null); + }); + upload.write(buf); + return upload.end(); + }); + }); + }, - readArchivedPack: (project_id, doc_id, pack_id, _callback = (error, result) ->) -> - callback = (args...) -> - _callback(args...) - _callback = () -> + readArchivedPack(project_id, doc_id, pack_id, _callback) { + if (_callback == null) { _callback = function(error, result) {}; } + const callback = function(...args) { + _callback(...Array.from(args || [])); + return _callback = function() {}; + }; - return callback new Error("invalid project id") if not project_id? - return callback new Error("invalid doc id") if not doc_id? - return callback new Error("invalid pack id") if not pack_id? + if ((project_id == null)) { return callback(new Error("invalid project id")); } + if ((doc_id == null)) { return callback(new Error("invalid doc id")); } + if ((pack_id == null)) { return callback(new Error("invalid pack id")); } - logger.log {project_id, doc_id, pack_id}, "downloading data from s3" + logger.log({project_id, doc_id, pack_id}, "downloading data from s3"); - download = createStream S3S.ReadStream, project_id, doc_id, pack_id + const download = createStream(S3S.ReadStream, project_id, doc_id, pack_id); - inputStream = download - .on 'open', (obj) -> - return 1 - .on 'error', (err) -> - callback(err) + const inputStream = download + .on('open', obj => 1).on('error', err => callback(err)); - gunzip = zlib.createGunzip() - gunzip.setEncoding('utf8') - gunzip.on 'error', (err) -> - logger.log {project_id, doc_id, pack_id, err}, "error uncompressing gzip stream" - callback(err) + const gunzip = zlib.createGunzip(); + gunzip.setEncoding('utf8'); + gunzip.on('error', function(err) { + logger.log({project_id, doc_id, pack_id, err}, "error uncompressing gzip stream"); + return callback(err); + }); - outputStream = inputStream.pipe gunzip - parts = [] - outputStream.on 'error', (err) -> - return callback(err) - outputStream.on 'end', () -> - logger.log {project_id, doc_id, pack_id}, "download from s3 completed" - try - object = JSON.parse parts.join('') - catch e - return callback(e) - object._id = ObjectId(object._id) - object.doc_id = ObjectId(object.doc_id) - object.project_id = ObjectId(object.project_id) - for op in object.pack - op._id = ObjectId(op._id) if op._id? - callback null, object - outputStream.on 'data', (data) -> - parts.push data + const outputStream = inputStream.pipe(gunzip); + const parts = []; + outputStream.on('error', err => callback(err)); + outputStream.on('end', function() { + let object; + logger.log({project_id, doc_id, pack_id}, "download from s3 completed"); + try { + object = JSON.parse(parts.join('')); + } catch (e) { + return callback(e); + } + object._id = ObjectId(object._id); + object.doc_id = ObjectId(object.doc_id); + object.project_id = ObjectId(object.project_id); + for (let op of Array.from(object.pack)) { + if (op._id != null) { op._id = ObjectId(op._id); } + } + return callback(null, object); + }); + return outputStream.on('data', data => parts.push(data)); + }, - unArchivePack: (project_id, doc_id, pack_id, callback = (error) ->) -> - MongoAWS.readArchivedPack project_id, doc_id, pack_id, (err, object) -> - return callback(err) if err? - Metrics.inc("unarchive-pack") - # allow the object to expire, we can always retrieve it again - object.expiresAt = new Date(Date.now() + 7 * DAYS) - logger.log {project_id, doc_id, pack_id}, "inserting object from s3" - db.docHistory.insert object, callback + unArchivePack(project_id, doc_id, pack_id, callback) { + if (callback == null) { callback = function(error) {}; } + return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function(err, object) { + if (err != null) { return callback(err); } + Metrics.inc("unarchive-pack"); + // allow the object to expire, we can always retrieve it again + object.expiresAt = new Date(Date.now() + (7 * DAYS)); + logger.log({project_id, doc_id, pack_id}, "inserting object from s3"); + return db.docHistory.insert(object, callback); + }); + } +}); diff --git a/services/track-changes/app/coffee/MongoManager.js b/services/track-changes/app/coffee/MongoManager.js index 201c2a95c6..3828e9ad80 100644 --- a/services/track-changes/app/coffee/MongoManager.js +++ b/services/track-changes/app/coffee/MongoManager.js @@ -1,104 +1,131 @@ -{db, ObjectId} = require "./mongojs" -PackManager = require "./PackManager" -async = require "async" -_ = require "underscore" -metrics = require 'metrics-sharelatex' -logger = require 'logger-sharelatex' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let MongoManager; +const {db, ObjectId} = require("./mongojs"); +const PackManager = require("./PackManager"); +const async = require("async"); +const _ = require("underscore"); +const metrics = require('metrics-sharelatex'); +const logger = require('logger-sharelatex'); -module.exports = MongoManager = - getLastCompressedUpdate: (doc_id, callback = (error, update) ->) -> - db.docHistory - .find(doc_id: ObjectId(doc_id.toString()), {pack: {$slice:-1}}) # only return the last entry in a pack - .sort( v: -1 ) +module.exports = (MongoManager = { + getLastCompressedUpdate(doc_id, callback) { + if (callback == null) { callback = function(error, update) {}; } + return db.docHistory + .find({doc_id: ObjectId(doc_id.toString())}, {pack: {$slice:-1}}) // only return the last entry in a pack + .sort({ v: -1 }) .limit(1) - .toArray (error, compressedUpdates) -> - return callback(error) if error? - callback null, compressedUpdates[0] or null + .toArray(function(error, compressedUpdates) { + if (error != null) { return callback(error); } + return callback(null, compressedUpdates[0] || null); + }); + }, - peekLastCompressedUpdate: (doc_id, callback = (error, update, version) ->) -> - # under normal use we pass back the last update as - # callback(null,update,version). - # - # when we have an existing last update but want to force a new one - # to start, we pass it back as callback(null,null,version), just - # giving the version so we can check consistency. - MongoManager.getLastCompressedUpdate doc_id, (error, update) -> - return callback(error) if error? - if update? - if update.broken # marked as broken so we will force a new op - return callback null, null - else if update.pack? - if update.finalised # no more ops can be appended - return callback null, null, update.pack[0]?.v - else - return callback null, update, update.pack[0]?.v - else - return callback null, update, update.v - else - PackManager.getLastPackFromIndex doc_id, (error, pack) -> - return callback(error) if error? - return callback(null, null, pack.v_end) if pack?.inS3? and pack?.v_end? - callback null, null + peekLastCompressedUpdate(doc_id, callback) { + // under normal use we pass back the last update as + // callback(null,update,version). + // + // when we have an existing last update but want to force a new one + // to start, we pass it back as callback(null,null,version), just + // giving the version so we can check consistency. + if (callback == null) { callback = function(error, update, version) {}; } + return MongoManager.getLastCompressedUpdate(doc_id, function(error, update) { + if (error != null) { return callback(error); } + if (update != null) { + if (update.broken) { // marked as broken so we will force a new op + return callback(null, null); + } else if (update.pack != null) { + if (update.finalised) { // no more ops can be appended + return callback(null, null, update.pack[0] != null ? update.pack[0].v : undefined); + } else { + return callback(null, update, update.pack[0] != null ? update.pack[0].v : undefined); + } + } else { + return callback(null, update, update.v); + } + } else { + return PackManager.getLastPackFromIndex(doc_id, function(error, pack) { + if (error != null) { return callback(error); } + if (((pack != null ? pack.inS3 : undefined) != null) && ((pack != null ? pack.v_end : undefined) != null)) { return callback(null, null, pack.v_end); } + return callback(null, null); + }); + } + }); + }, - backportProjectId: (project_id, doc_id, callback = (error) ->) -> - db.docHistory.update { - doc_id: ObjectId(doc_id.toString()) + backportProjectId(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return db.docHistory.update({ + doc_id: ObjectId(doc_id.toString()), project_id: { $exists: false } }, { $set: { project_id: ObjectId(project_id.toString()) } }, { multi: true - }, callback + }, callback); + }, - getProjectMetaData: (project_id, callback = (error, metadata) ->) -> - db.projectHistoryMetaData.find { + getProjectMetaData(project_id, callback) { + if (callback == null) { callback = function(error, metadata) {}; } + return db.projectHistoryMetaData.find({ project_id: ObjectId(project_id.toString()) - }, (error, results) -> - return callback(error) if error? - callback null, results[0] + }, function(error, results) { + if (error != null) { return callback(error); } + return callback(null, results[0]); + }); + }, - setProjectMetaData: (project_id, metadata, callback = (error) ->) -> - db.projectHistoryMetaData.update { + setProjectMetaData(project_id, metadata, callback) { + if (callback == null) { callback = function(error) {}; } + return db.projectHistoryMetaData.update({ project_id: ObjectId(project_id) }, { $set: metadata }, { upsert: true - }, callback + }, callback); + }, - upgradeHistory: (project_id, callback = (error) ->) -> - # preserve the project's existing history - db.docHistory.update { - project_id: ObjectId(project_id) - temporary: true + upgradeHistory(project_id, callback) { + // preserve the project's existing history + if (callback == null) { callback = function(error) {}; } + return db.docHistory.update({ + project_id: ObjectId(project_id), + temporary: true, expiresAt: {$exists: true} }, { - $set: {temporary: false} + $set: {temporary: false}, $unset: {expiresAt: ""} }, { multi: true - }, callback + }, callback); + }, - ensureIndices: () -> - # For finding all updates that go into a diff for a doc - db.docHistory.ensureIndex { doc_id: 1, v: 1 }, { background: true } - # For finding all updates that affect a project - db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1 }, { background: true } - # For finding updates that don't yet have a project_id and need it inserting - db.docHistory.ensureIndex { doc_id: 1, project_id: 1 }, { background: true } - # For finding project meta-data - db.projectHistoryMetaData.ensureIndex { project_id: 1 }, { background: true } - # TTL index for auto deleting week old temporary ops - db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0, background: true } - # For finding packs to be checked for archiving - db.docHistory.ensureIndex { last_checked: 1 }, { background: true } - # For finding archived packs - db.docHistoryIndex.ensureIndex { project_id: 1 }, { background: true } + ensureIndices() { + // For finding all updates that go into a diff for a doc + db.docHistory.ensureIndex({ doc_id: 1, v: 1 }, { background: true }); + // For finding all updates that affect a project + db.docHistory.ensureIndex({ project_id: 1, "meta.end_ts": 1 }, { background: true }); + // For finding updates that don't yet have a project_id and need it inserting + db.docHistory.ensureIndex({ doc_id: 1, project_id: 1 }, { background: true }); + // For finding project meta-data + db.projectHistoryMetaData.ensureIndex({ project_id: 1 }, { background: true }); + // TTL index for auto deleting week old temporary ops + db.docHistory.ensureIndex({ expiresAt: 1 }, { expireAfterSeconds: 0, background: true }); + // For finding packs to be checked for archiving + db.docHistory.ensureIndex({ last_checked: 1 }, { background: true }); + // For finding archived packs + return db.docHistoryIndex.ensureIndex({ project_id: 1 }, { background: true }); + } +}); [ 'getLastCompressedUpdate', 'getProjectMetaData', 'setProjectMetaData' -].map (method) -> - metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger) +].map(method => metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger)); diff --git a/services/track-changes/app/coffee/PackManager.js b/services/track-changes/app/coffee/PackManager.js index d33978229d..d1359b470d 100644 --- a/services/track-changes/app/coffee/PackManager.js +++ b/services/track-changes/app/coffee/PackManager.js @@ -1,540 +1,706 @@ -async = require "async" -_ = require "underscore" -{db, ObjectId, BSON} = require "./mongojs" -logger = require "logger-sharelatex" -LockManager = require "./LockManager" -MongoAWS = require "./MongoAWS" -Metrics = require "metrics-sharelatex" -ProjectIterator = require "./ProjectIterator" -Settings = require "settings-sharelatex" -keys = Settings.redis.lock.key_schema +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let PackManager; +const async = require("async"); +const _ = require("underscore"); +const {db, ObjectId, BSON} = require("./mongojs"); +const logger = require("logger-sharelatex"); +const LockManager = require("./LockManager"); +const MongoAWS = require("./MongoAWS"); +const Metrics = require("metrics-sharelatex"); +const ProjectIterator = require("./ProjectIterator"); +const Settings = require("settings-sharelatex"); +const keys = Settings.redis.lock.key_schema; -# Sharejs operations are stored in a 'pack' object -# -# e.g. a single sharejs update looks like -# -# { -# "doc_id" : 549dae9e0a2a615c0c7f0c98, -# "project_id" : 549dae9c0a2a615c0c7f0c8c, -# "op" : [ {"p" : 6981, "d" : "?" } ], -# "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, -# "v" : 17082 -# } -# -# and a pack looks like this -# -# { -# "doc_id" : 549dae9e0a2a615c0c7f0c98, -# "project_id" : 549dae9c0a2a615c0c7f0c8c, -# "pack" : [ U1, U2, U3, ...., UN], -# "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, -# "v" : 17082 -# "v_end" : ... -# } -# -# where U1, U2, U3, .... are single updates stripped of their -# doc_id and project_id fields (which are the same for all the -# updates in the pack). -# -# The pack itself has v and meta fields, this makes it possible to -# treat packs and single updates in a similar way. -# -# The v field of the pack itself is from the first entry U1, the -# v_end field from UN. The meta.end_ts field of the pack itself is -# from the last entry UN, the meta.start_ts field from U1. +// Sharejs operations are stored in a 'pack' object +// +// e.g. a single sharejs update looks like +// +// { +// "doc_id" : 549dae9e0a2a615c0c7f0c98, +// "project_id" : 549dae9c0a2a615c0c7f0c8c, +// "op" : [ {"p" : 6981, "d" : "?" } ], +// "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, +// "v" : 17082 +// } +// +// and a pack looks like this +// +// { +// "doc_id" : 549dae9e0a2a615c0c7f0c98, +// "project_id" : 549dae9c0a2a615c0c7f0c8c, +// "pack" : [ U1, U2, U3, ...., UN], +// "meta" : { "user_id" : 52933..., "start_ts" : 1422310693931, "end_ts" : 1422310693931 }, +// "v" : 17082 +// "v_end" : ... +// } +// +// where U1, U2, U3, .... are single updates stripped of their +// doc_id and project_id fields (which are the same for all the +// updates in the pack). +// +// The pack itself has v and meta fields, this makes it possible to +// treat packs and single updates in a similar way. +// +// The v field of the pack itself is from the first entry U1, the +// v_end field from UN. The meta.end_ts field of the pack itself is +// from the last entry UN, the meta.start_ts field from U1. -DAYS = 24 * 3600 * 1000 # one day in milliseconds +const DAYS = 24 * 3600 * 1000; // one day in milliseconds -module.exports = PackManager = +module.exports = (PackManager = { - MAX_SIZE: 1024*1024 # make these configurable parameters - MAX_COUNT: 1024 + MAX_SIZE: 1024*1024, // make these configurable parameters + MAX_COUNT: 1024, - insertCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> - return callback() if newUpdates.length == 0 + insertCompressedUpdates(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) { + if (callback == null) { callback = function(error) {}; } + if (newUpdates.length === 0) { return callback(); } - # never append permanent ops to a pack that will expire - lastUpdate = null if lastUpdate?.expiresAt? and not temporary + // never append permanent ops to a pack that will expire + if (((lastUpdate != null ? lastUpdate.expiresAt : undefined) != null) && !temporary) { lastUpdate = null; } - updatesToFlush = [] - updatesRemaining = newUpdates.slice() + const updatesToFlush = []; + const updatesRemaining = newUpdates.slice(); - n = lastUpdate?.n || 0 - sz = lastUpdate?.sz || 0 + let n = (lastUpdate != null ? lastUpdate.n : undefined) || 0; + let sz = (lastUpdate != null ? lastUpdate.sz : undefined) || 0; - while updatesRemaining.length and n < PackManager.MAX_COUNT and sz < PackManager.MAX_SIZE - nextUpdate = updatesRemaining[0] - nextUpdateSize = BSON.calculateObjectSize(nextUpdate) - if nextUpdateSize + sz > PackManager.MAX_SIZE and n > 0 - break - n++ - sz += nextUpdateSize - updatesToFlush.push updatesRemaining.shift() + while (updatesRemaining.length && (n < PackManager.MAX_COUNT) && (sz < PackManager.MAX_SIZE)) { + const nextUpdate = updatesRemaining[0]; + const nextUpdateSize = BSON.calculateObjectSize(nextUpdate); + if (((nextUpdateSize + sz) > PackManager.MAX_SIZE) && (n > 0)) { + break; + } + n++; + sz += nextUpdateSize; + updatesToFlush.push(updatesRemaining.shift()); + } - PackManager.flushCompressedUpdates project_id, doc_id, lastUpdate, updatesToFlush, temporary, (error) -> - return callback(error) if error? - PackManager.insertCompressedUpdates project_id, doc_id, null, updatesRemaining, temporary, callback + return PackManager.flushCompressedUpdates(project_id, doc_id, lastUpdate, updatesToFlush, temporary, function(error) { + if (error != null) { return callback(error); } + return PackManager.insertCompressedUpdates(project_id, doc_id, null, updatesRemaining, temporary, callback); + }); + }, - flushCompressedUpdates: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> - return callback() if newUpdates.length == 0 + flushCompressedUpdates(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) { + if (callback == null) { callback = function(error) {}; } + if (newUpdates.length === 0) { return callback(); } - canAppend = false - # check if it is safe to append to an existing pack - if lastUpdate? - if not temporary and not lastUpdate.expiresAt? - # permanent pack appends to permanent pack - canAppend = true - age = Date.now() - lastUpdate.meta?.start_ts - if temporary and lastUpdate.expiresAt? and age < 1 * DAYS - # temporary pack appends to temporary pack if same day - canAppend = true + let canAppend = false; + // check if it is safe to append to an existing pack + if (lastUpdate != null) { + if (!temporary && (lastUpdate.expiresAt == null)) { + // permanent pack appends to permanent pack + canAppend = true; + } + const age = Date.now() - (lastUpdate.meta != null ? lastUpdate.meta.start_ts : undefined); + if (temporary && (lastUpdate.expiresAt != null) && (age < (1 * DAYS))) { + // temporary pack appends to temporary pack if same day + canAppend = true; + } + } - if canAppend - PackManager.appendUpdatesToExistingPack project_id, doc_id, lastUpdate, newUpdates, temporary, callback - else - PackManager.insertUpdatesIntoNewPack project_id, doc_id, newUpdates, temporary, callback + if (canAppend) { + return PackManager.appendUpdatesToExistingPack(project_id, doc_id, lastUpdate, newUpdates, temporary, callback); + } else { + return PackManager.insertUpdatesIntoNewPack(project_id, doc_id, newUpdates, temporary, callback); + } + }, - insertUpdatesIntoNewPack: (project_id, doc_id, newUpdates, temporary, callback = (error) ->) -> - first = newUpdates[0] - last = newUpdates[newUpdates.length - 1] - n = newUpdates.length - sz = BSON.calculateObjectSize(newUpdates) - newPack = - project_id: ObjectId(project_id.toString()) - doc_id: ObjectId(doc_id.toString()) - pack: newUpdates - n: n - sz: sz - meta: - start_ts: first.meta.start_ts + insertUpdatesIntoNewPack(project_id, doc_id, newUpdates, temporary, callback) { + if (callback == null) { callback = function(error) {}; } + const first = newUpdates[0]; + const last = newUpdates[newUpdates.length - 1]; + const n = newUpdates.length; + const sz = BSON.calculateObjectSize(newUpdates); + const newPack = { + project_id: ObjectId(project_id.toString()), + doc_id: ObjectId(doc_id.toString()), + pack: newUpdates, + n, + sz, + meta: { + start_ts: first.meta.start_ts, end_ts: last.meta.end_ts - v: first.v - v_end: last.v - temporary: temporary - if temporary - newPack.expiresAt = new Date(Date.now() + 7 * DAYS) - newPack.last_checked = new Date(Date.now() + 30 * DAYS) # never check temporary packs - logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack" - db.docHistory.save newPack, (err, result) -> - return callback(err) if err? - Metrics.inc("insert-pack-" + if temporary then "temporary" else "permanent") - if temporary - return callback() - else - PackManager.updateIndex project_id, doc_id, callback + }, + v: first.v, + v_end: last.v, + temporary + }; + if (temporary) { + newPack.expiresAt = new Date(Date.now() + (7 * DAYS)); + newPack.last_checked = new Date(Date.now() + (30 * DAYS)); // never check temporary packs + } + logger.log({project_id, doc_id, newUpdates}, "inserting updates into new pack"); + return db.docHistory.save(newPack, function(err, result) { + if (err != null) { return callback(err); } + Metrics.inc(`insert-pack-${temporary ? "temporary" : "permanent"}`); + if (temporary) { + return callback(); + } else { + return PackManager.updateIndex(project_id, doc_id, callback); + } + }); + }, - appendUpdatesToExistingPack: (project_id, doc_id, lastUpdate, newUpdates, temporary, callback = (error) ->) -> - first = newUpdates[0] - last = newUpdates[newUpdates.length - 1] - n = newUpdates.length - sz = BSON.calculateObjectSize(newUpdates) - query = - _id: lastUpdate._id - project_id: ObjectId(project_id.toString()) - doc_id: ObjectId(doc_id.toString()) + appendUpdatesToExistingPack(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) { + if (callback == null) { callback = function(error) {}; } + const first = newUpdates[0]; + const last = newUpdates[newUpdates.length - 1]; + const n = newUpdates.length; + const sz = BSON.calculateObjectSize(newUpdates); + const query = { + _id: lastUpdate._id, + project_id: ObjectId(project_id.toString()), + doc_id: ObjectId(doc_id.toString()), pack: {$exists: true} - update = - $push: + }; + const update = { + $push: { "pack": {$each: newUpdates} - $inc: - "n": n + }, + $inc: { + "n": n, "sz": sz - $set: - "meta.end_ts": last.meta.end_ts + }, + $set: { + "meta.end_ts": last.meta.end_ts, "v_end": last.v - if lastUpdate.expiresAt and temporary - update.$set.expiresAt = new Date(Date.now() + 7 * DAYS) - logger.log {project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack" - Metrics.inc("append-pack-" + if temporary then "temporary" else "permanent") - db.docHistory.findAndModify {query, update, new:true, fields:{meta:1,v_end:1}}, callback + } + }; + if (lastUpdate.expiresAt && temporary) { + update.$set.expiresAt = new Date(Date.now() + (7 * DAYS)); + } + logger.log({project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack"); + Metrics.inc(`append-pack-${temporary ? "temporary" : "permanent"}`); + return db.docHistory.findAndModify({query, update, new:true, fields:{meta:1,v_end:1}}, callback); + }, - # Retrieve all changes for a document + // Retrieve all changes for a document - getOpsByVersionRange: (project_id, doc_id, fromVersion, toVersion, callback = (error, updates) ->) -> - PackManager.loadPacksByVersionRange project_id, doc_id, fromVersion, toVersion, (error) -> - query = {doc_id:ObjectId(doc_id.toString())} - query.v = {$lte:toVersion} if toVersion? - query.v_end = {$gte:fromVersion} if fromVersion? - #console.log "query:", query - db.docHistory.find(query).sort {v:-1}, (err, result) -> - return callback(err) if err? - #console.log "getOpsByVersionRange:", err, result - updates = [] - opInRange = (op, from, to) -> - return false if fromVersion? and op.v < fromVersion - return false if toVersion? and op.v > toVersion - return true - for docHistory in result - #console.log 'adding', docHistory.pack - for op in docHistory.pack.reverse() when opInRange(op, fromVersion, toVersion) - op.project_id = docHistory.project_id - op.doc_id = docHistory.doc_id - #console.log "added op", op.v, fromVersion, toVersion - updates.push op - callback(null, updates) + getOpsByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) { + if (callback == null) { callback = function(error, updates) {}; } + return PackManager.loadPacksByVersionRange(project_id, doc_id, fromVersion, toVersion, function(error) { + const query = {doc_id:ObjectId(doc_id.toString())}; + if (toVersion != null) { query.v = {$lte:toVersion}; } + if (fromVersion != null) { query.v_end = {$gte:fromVersion}; } + //console.log "query:", query + return db.docHistory.find(query).sort({v:-1}, function(err, result) { + if (err != null) { return callback(err); } + //console.log "getOpsByVersionRange:", err, result + const updates = []; + const opInRange = function(op, from, to) { + if ((fromVersion != null) && (op.v < fromVersion)) { return false; } + if ((toVersion != null) && (op.v > toVersion)) { return false; } + return true; + }; + for (let docHistory of Array.from(result)) { + //console.log 'adding', docHistory.pack + for (let op of Array.from(docHistory.pack.reverse())) { + if (opInRange(op, fromVersion, toVersion)) { + op.project_id = docHistory.project_id; + op.doc_id = docHistory.doc_id; + //console.log "added op", op.v, fromVersion, toVersion + updates.push(op); + } + } + } + return callback(null, updates); + }); + }); + }, - loadPacksByVersionRange: (project_id, doc_id, fromVersion, toVersion, callback) -> - PackManager.getIndex doc_id, (err, indexResult) -> - return callback(err) if err? - indexPacks = indexResult?.packs or [] - packInRange = (pack, from, to) -> - return false if fromVersion? and pack.v_end < fromVersion - return false if toVersion? and pack.v > toVersion - return true - neededIds = (pack._id for pack in indexPacks when packInRange(pack, fromVersion, toVersion)) - if neededIds.length - PackManager.fetchPacksIfNeeded project_id, doc_id, neededIds, callback - else - callback() + loadPacksByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) { + return PackManager.getIndex(doc_id, function(err, indexResult) { + let pack; + if (err != null) { return callback(err); } + const indexPacks = (indexResult != null ? indexResult.packs : undefined) || []; + const packInRange = function(pack, from, to) { + if ((fromVersion != null) && (pack.v_end < fromVersion)) { return false; } + if ((toVersion != null) && (pack.v > toVersion)) { return false; } + return true; + }; + const neededIds = ((() => { + const result = []; + for (pack of Array.from(indexPacks)) { if (packInRange(pack, fromVersion, toVersion)) { + result.push(pack._id); + } + } + return result; + })()); + if (neededIds.length) { + return PackManager.fetchPacksIfNeeded(project_id, doc_id, neededIds, callback); + } else { + return callback(); + } + }); + }, - fetchPacksIfNeeded: (project_id, doc_id, pack_ids, callback) -> - db.docHistory.find {_id: {$in: (ObjectId(id) for id in pack_ids)}}, {_id:1}, (err, loadedPacks) -> - return callback(err) if err? - allPackIds = (id.toString() for id in pack_ids) - loadedPackIds = (pack._id.toString() for pack in loadedPacks) - packIdsToFetch = _.difference allPackIds, loadedPackIds - logger.log {project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs" - return callback() if packIdsToFetch.length is 0 - async.eachLimit packIdsToFetch, 4, (pack_id, cb) -> - MongoAWS.unArchivePack project_id, doc_id, pack_id, cb - , (err) -> - return callback(err) if err? - logger.log {project_id, doc_id}, "done unarchiving" - callback() + fetchPacksIfNeeded(project_id, doc_id, pack_ids, callback) { + let id; + return db.docHistory.find({_id: {$in: ((() => { + const result = []; + for (id of Array.from(pack_ids)) { result.push(ObjectId(id)); + } + return result; + })())}}, {_id:1}, function(err, loadedPacks) { + if (err != null) { return callback(err); } + const allPackIds = ((() => { + const result1 = []; + for (id of Array.from(pack_ids)) { result1.push(id.toString()); + } + return result1; + })()); + const loadedPackIds = (Array.from(loadedPacks).map((pack) => pack._id.toString())); + const packIdsToFetch = _.difference(allPackIds, loadedPackIds); + logger.log({project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs"); + if (packIdsToFetch.length === 0) { return callback(); } + return async.eachLimit(packIdsToFetch, 4, (pack_id, cb) => MongoAWS.unArchivePack(project_id, doc_id, pack_id, cb) + , function(err) { + if (err != null) { return callback(err); } + logger.log({project_id, doc_id}, "done unarchiving"); + return callback(); + }); + }); + }, - # Retrieve all changes across a project + // Retrieve all changes across a project - makeProjectIterator: (project_id, before, callback) -> - # get all the docHistory Entries - db.docHistory.find({project_id: ObjectId(project_id)},{pack:false}).sort {"meta.end_ts":-1}, (err, packs) -> - return callback(err) if err? - allPacks = [] - seenIds = {} - for pack in packs - allPacks.push pack - seenIds[pack._id] = true - db.docHistoryIndex.find {project_id: ObjectId(project_id)}, (err, indexes) -> - return callback(err) if err? - for index in indexes - for pack in index.packs when not seenIds[pack._id] - pack.project_id = index.project_id - pack.doc_id = index._id - pack.fromIndex = true - allPacks.push pack - seenIds[pack._id] = true - callback(null, new ProjectIterator(allPacks, before, PackManager.getPackById)) + makeProjectIterator(project_id, before, callback) { + // get all the docHistory Entries + return db.docHistory.find({project_id: ObjectId(project_id)},{pack:false}).sort({"meta.end_ts":-1}, function(err, packs) { + let pack; + if (err != null) { return callback(err); } + const allPacks = []; + const seenIds = {}; + for (pack of Array.from(packs)) { + allPacks.push(pack); + seenIds[pack._id] = true; + } + return db.docHistoryIndex.find({project_id: ObjectId(project_id)}, function(err, indexes) { + if (err != null) { return callback(err); } + for (let index of Array.from(indexes)) { + for (pack of Array.from(index.packs)) { + if (!seenIds[pack._id]) { + pack.project_id = index.project_id; + pack.doc_id = index._id; + pack.fromIndex = true; + allPacks.push(pack); + seenIds[pack._id] = true; + } + } + } + return callback(null, new ProjectIterator(allPacks, before, PackManager.getPackById)); + }); + }); + }, - getPackById: (project_id, doc_id, pack_id, callback) -> - db.docHistory.findOne {_id: pack_id}, (err, pack) -> - return callback(err) if err? - if not pack? - MongoAWS.unArchivePack project_id, doc_id, pack_id, callback - else if pack.expiresAt? and pack.temporary is false - # we only need to touch the TTL when listing the changes in the project - # because diffs on individual documents are always done after that - PackManager.increaseTTL pack, callback - # only do this for cached packs, not temporary ones to avoid older packs - # being kept longer than newer ones (which messes up the last update version) - else - callback(null, pack) + getPackById(project_id, doc_id, pack_id, callback) { + return db.docHistory.findOne({_id: pack_id}, function(err, pack) { + if (err != null) { return callback(err); } + if ((pack == null)) { + return MongoAWS.unArchivePack(project_id, doc_id, pack_id, callback); + } else if ((pack.expiresAt != null) && (pack.temporary === false)) { + // we only need to touch the TTL when listing the changes in the project + // because diffs on individual documents are always done after that + return PackManager.increaseTTL(pack, callback); + // only do this for cached packs, not temporary ones to avoid older packs + // being kept longer than newer ones (which messes up the last update version) + } else { + return callback(null, pack); + } + }); + }, - increaseTTL: (pack, callback) -> - if pack.expiresAt < new Date(Date.now() + 6 * DAYS) - # update cache expiry since we are using this pack - db.docHistory.findAndModify { - query: {_id: pack._id} - update: {$set: {expiresAt: new Date(Date.now() + 7 * DAYS)}} - }, (err) -> - return callback(err, pack) - else - callback(null, pack) + increaseTTL(pack, callback) { + if (pack.expiresAt < new Date(Date.now() + (6 * DAYS))) { + // update cache expiry since we are using this pack + return db.docHistory.findAndModify({ + query: {_id: pack._id}, + update: {$set: {expiresAt: new Date(Date.now() + (7 * DAYS))}} + }, err => callback(err, pack)); + } else { + return callback(null, pack); + } + }, - # Manage docHistoryIndex collection + // Manage docHistoryIndex collection - getIndex: (doc_id, callback) -> - db.docHistoryIndex.findOne {_id:ObjectId(doc_id.toString())}, callback + getIndex(doc_id, callback) { + return db.docHistoryIndex.findOne({_id:ObjectId(doc_id.toString())}, callback); + }, - getPackFromIndex: (doc_id, pack_id, callback) -> - db.docHistoryIndex.findOne {_id:ObjectId(doc_id.toString()), "packs._id": pack_id}, {"packs.$":1}, callback + getPackFromIndex(doc_id, pack_id, callback) { + return db.docHistoryIndex.findOne({_id:ObjectId(doc_id.toString()), "packs._id": pack_id}, {"packs.$":1}, callback); + }, - getLastPackFromIndex: (doc_id, callback) -> - db.docHistoryIndex.findOne {_id: ObjectId(doc_id.toString())}, {packs:{$slice:-1}}, (err, indexPack) -> - return callback(err) if err? - return callback() if not indexPack? - callback(null,indexPack[0]) + getLastPackFromIndex(doc_id, callback) { + return db.docHistoryIndex.findOne({_id: ObjectId(doc_id.toString())}, {packs:{$slice:-1}}, function(err, indexPack) { + if (err != null) { return callback(err); } + if ((indexPack == null)) { return callback(); } + return callback(null,indexPack[0]); + }); + }, - getIndexWithKeys: (doc_id, callback) -> - PackManager.getIndex doc_id, (err, index) -> - return callback(err) if err? - return callback() if not index? - for pack in index?.packs or [] - index[pack._id] = pack - callback(null, index) + getIndexWithKeys(doc_id, callback) { + return PackManager.getIndex(doc_id, function(err, index) { + if (err != null) { return callback(err); } + if ((index == null)) { return callback(); } + for (let pack of Array.from((index != null ? index.packs : undefined) || [])) { + index[pack._id] = pack; + } + return callback(null, index); + }); + }, - initialiseIndex: (project_id, doc_id, callback) -> - PackManager.findCompletedPacks project_id, doc_id, (err, packs) -> - #console.log 'err', err, 'packs', packs, packs?.length - return callback(err) if err? - return callback() if not packs? - PackManager.insertPacksIntoIndexWithLock project_id, doc_id, packs, callback + initialiseIndex(project_id, doc_id, callback) { + return PackManager.findCompletedPacks(project_id, doc_id, function(err, packs) { + //console.log 'err', err, 'packs', packs, packs?.length + if (err != null) { return callback(err); } + if ((packs == null)) { return callback(); } + return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, packs, callback); + }); + }, - updateIndex: (project_id, doc_id, callback) -> - # find all packs prior to current pack - PackManager.findUnindexedPacks project_id, doc_id, (err, newPacks) -> - return callback(err) if err? - return callback() if not newPacks? or newPacks.length is 0 - PackManager.insertPacksIntoIndexWithLock project_id, doc_id, newPacks, (err) -> - return callback(err) if err? - logger.log {project_id, doc_id, newPacks}, "added new packs to index" - callback() + updateIndex(project_id, doc_id, callback) { + // find all packs prior to current pack + return PackManager.findUnindexedPacks(project_id, doc_id, function(err, newPacks) { + if (err != null) { return callback(err); } + if ((newPacks == null) || (newPacks.length === 0)) { return callback(); } + return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, function(err) { + if (err != null) { return callback(err); } + logger.log({project_id, doc_id, newPacks}, "added new packs to index"); + return callback(); + }); + }); + }, - findCompletedPacks: (project_id, doc_id, callback) -> - query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} } - db.docHistory.find(query, {pack:false}).sort {v:1}, (err, packs) -> - return callback(err) if err? - return callback() if not packs? - return callback() if not packs?.length - last = packs.pop() # discard the last pack, if it's still in progress - packs.push(last) if last.finalised # it's finalised so we push it back to archive it - callback(null, packs) + findCompletedPacks(project_id, doc_id, callback) { + const query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} }; + return db.docHistory.find(query, {pack:false}).sort({v:1}, function(err, packs) { + if (err != null) { return callback(err); } + if ((packs == null)) { return callback(); } + if (!(packs != null ? packs.length : undefined)) { return callback(); } + const last = packs.pop(); // discard the last pack, if it's still in progress + if (last.finalised) { packs.push(last); } // it's finalised so we push it back to archive it + return callback(null, packs); + }); + }, - findPacks: (project_id, doc_id, callback) -> - query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} } - db.docHistory.find(query, {pack:false}).sort {v:1}, (err, packs) -> - return callback(err) if err? - return callback() if not packs? - return callback() if not packs?.length - callback(null, packs) + findPacks(project_id, doc_id, callback) { + const query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} }; + return db.docHistory.find(query, {pack:false}).sort({v:1}, function(err, packs) { + if (err != null) { return callback(err); } + if ((packs == null)) { return callback(); } + if (!(packs != null ? packs.length : undefined)) { return callback(); } + return callback(null, packs); + }); + }, - findUnindexedPacks: (project_id, doc_id, callback) -> - PackManager.getIndexWithKeys doc_id, (err, indexResult) -> - return callback(err) if err? - PackManager.findCompletedPacks project_id, doc_id, (err, historyPacks) -> - return callback(err) if err? - return callback() if not historyPacks? - # select only the new packs not already in the index - newPacks = (pack for pack in historyPacks when not indexResult?[pack._id]?) - newPacks = (_.omit(pack, 'doc_id', 'project_id', 'n', 'sz', 'last_checked', 'finalised') for pack in newPacks) - if newPacks.length - logger.log {project_id, doc_id, n: newPacks.length}, "found new packs" - callback(null, newPacks) + findUnindexedPacks(project_id, doc_id, callback) { + return PackManager.getIndexWithKeys(doc_id, function(err, indexResult) { + if (err != null) { return callback(err); } + return PackManager.findCompletedPacks(project_id, doc_id, function(err, historyPacks) { + let pack; + if (err != null) { return callback(err); } + if ((historyPacks == null)) { return callback(); } + // select only the new packs not already in the index + let newPacks = ((() => { + const result = []; + for (pack of Array.from(historyPacks)) { if (((indexResult != null ? indexResult[pack._id] : undefined) == null)) { + result.push(pack); + } + } + return result; + })()); + newPacks = ((() => { + const result1 = []; + for (pack of Array.from(newPacks)) { result1.push(_.omit(pack, 'doc_id', 'project_id', 'n', 'sz', 'last_checked', 'finalised')); + } + return result1; + })()); + if (newPacks.length) { + logger.log({project_id, doc_id, n: newPacks.length}, "found new packs"); + } + return callback(null, newPacks); + }); + }); + }, - insertPacksIntoIndexWithLock: (project_id, doc_id, newPacks, callback) -> - LockManager.runWithLock( + insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, callback) { + return LockManager.runWithLock( keys.historyIndexLock({doc_id}), - (releaseLock) -> - PackManager._insertPacksIntoIndex project_id, doc_id, newPacks, releaseLock + releaseLock => PackManager._insertPacksIntoIndex(project_id, doc_id, newPacks, releaseLock), callback - ) + ); + }, - _insertPacksIntoIndex: (project_id, doc_id, newPacks, callback) -> - db.docHistoryIndex.findAndModify { - query: {_id:ObjectId(doc_id.toString())} - update: - $setOnInsert: project_id: ObjectId(project_id.toString()) - $push: + _insertPacksIntoIndex(project_id, doc_id, newPacks, callback) { + return db.docHistoryIndex.findAndModify({ + query: {_id:ObjectId(doc_id.toString())}, + update: { + $setOnInsert: { project_id: ObjectId(project_id.toString()) + }, + $push: { packs: {$each: newPacks, $sort: {v: 1}} + } + }, upsert: true - }, callback + }, callback); + }, - # Archiving packs to S3 + // Archiving packs to S3 - archivePack: (project_id, doc_id, pack_id, callback) -> - clearFlagOnError = (err, cb) -> - if err? # clear the inS3 flag on error - PackManager.clearPackAsArchiveInProgress project_id, doc_id, pack_id, (err2) -> - return cb(err2) if err2? - return cb(err) - else - cb() - async.series [ - (cb) -> - PackManager.checkArchiveNotInProgress project_id, doc_id, pack_id, cb - (cb) -> - PackManager.markPackAsArchiveInProgress project_id, doc_id, pack_id, cb - (cb) -> - MongoAWS.archivePack project_id, doc_id, pack_id, (err) -> - clearFlagOnError(err, cb) - (cb) -> - PackManager.checkArchivedPack project_id, doc_id, pack_id, (err) -> - clearFlagOnError(err, cb) - (cb) -> - PackManager.markPackAsArchived project_id, doc_id, pack_id, cb - (cb) -> - PackManager.setTTLOnArchivedPack project_id, doc_id, pack_id, callback - ], callback + archivePack(project_id, doc_id, pack_id, callback) { + const clearFlagOnError = function(err, cb) { + if (err != null) { // clear the inS3 flag on error + return PackManager.clearPackAsArchiveInProgress(project_id, doc_id, pack_id, function(err2) { + if (err2 != null) { return cb(err2); } + return cb(err); + }); + } else { + return cb(); + } + }; + return async.series([ + cb => PackManager.checkArchiveNotInProgress(project_id, doc_id, pack_id, cb), + cb => PackManager.markPackAsArchiveInProgress(project_id, doc_id, pack_id, cb), + cb => + MongoAWS.archivePack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb)) + , + cb => + PackManager.checkArchivedPack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb)) + , + cb => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), + cb => PackManager.setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) + ], callback); + }, - checkArchivedPack: (project_id, doc_id, pack_id, callback) -> - db.docHistory.findOne {_id: pack_id}, (err, pack) -> - return callback(err) if err? - return callback new Error("pack not found") if not pack? - MongoAWS.readArchivedPack project_id, doc_id, pack_id, (err, result) -> - delete result.last_checked - delete pack.last_checked - # need to compare ids as ObjectIds with .equals() - for key in ['_id', 'project_id', 'doc_id'] - result[key] = pack[key] if result[key].equals(pack[key]) - for op, i in result.pack - op._id = pack.pack[i]._id if op._id? and op._id.equals(pack.pack[i]._id) - if _.isEqual pack, result - callback() - else - logger.err {pack, result, jsondiff: JSON.stringify(pack) is JSON.stringify(result)}, "difference when comparing packs" - callback new Error("pack retrieved from s3 does not match pack in mongo") - # Extra methods to test archive/unarchive for a doc_id + checkArchivedPack(project_id, doc_id, pack_id, callback) { + return db.docHistory.findOne({_id: pack_id}, function(err, pack) { + if (err != null) { return callback(err); } + if ((pack == null)) { return callback(new Error("pack not found")); } + return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function(err, result) { + delete result.last_checked; + delete pack.last_checked; + // need to compare ids as ObjectIds with .equals() + for (let key of ['_id', 'project_id', 'doc_id']) { + if (result[key].equals(pack[key])) { result[key] = pack[key]; } + } + for (let i = 0; i < result.pack.length; i++) { + const op = result.pack[i]; + if ((op._id != null) && op._id.equals(pack.pack[i]._id)) { op._id = pack.pack[i]._id; } + } + if (_.isEqual(pack, result)) { + return callback(); + } else { + logger.err({pack, result, jsondiff: JSON.stringify(pack) === JSON.stringify(result)}, "difference when comparing packs"); + return callback(new Error("pack retrieved from s3 does not match pack in mongo")); + } + }); + }); + }, + // Extra methods to test archive/unarchive for a doc_id - pushOldPacks: (project_id, doc_id, callback) -> - PackManager.findPacks project_id, doc_id, (err, packs) -> - return callback(err) if err? - return callback() if not packs?.length - PackManager.processOldPack project_id, doc_id, packs[0]._id, callback + pushOldPacks(project_id, doc_id, callback) { + return PackManager.findPacks(project_id, doc_id, function(err, packs) { + if (err != null) { return callback(err); } + if (!(packs != null ? packs.length : undefined)) { return callback(); } + return PackManager.processOldPack(project_id, doc_id, packs[0]._id, callback); + }); + }, - pullOldPacks: (project_id, doc_id, callback) -> - PackManager.loadPacksByVersionRange project_id, doc_id, null, null, callback + pullOldPacks(project_id, doc_id, callback) { + return PackManager.loadPacksByVersionRange(project_id, doc_id, null, null, callback); + }, - # Processing old packs via worker + // Processing old packs via worker - processOldPack: (project_id, doc_id, pack_id, callback) -> - markAsChecked = (err) -> - PackManager.markPackAsChecked project_id, doc_id, pack_id, (err2) -> - return callback(err2) if err2? - callback(err) - logger.log {project_id, doc_id}, "processing old packs" - db.docHistory.findOne {_id:pack_id}, (err, pack) -> - return markAsChecked(err) if err? - return markAsChecked() if not pack? - return callback() if pack.expiresAt? # return directly - PackManager.finaliseIfNeeded project_id, doc_id, pack._id, pack, (err) -> - return markAsChecked(err) if err? - PackManager.updateIndexIfNeeded project_id, doc_id, (err) -> - return markAsChecked(err) if err? - PackManager.findUnarchivedPacks project_id, doc_id, (err, unarchivedPacks) -> - return markAsChecked(err) if err? - if not unarchivedPacks?.length - logger.log {project_id, doc_id}, "no packs need archiving" - return markAsChecked() - async.eachSeries unarchivedPacks, (pack, cb) -> - PackManager.archivePack project_id, doc_id, pack._id, cb - , (err) -> - return markAsChecked(err) if err? - logger.log {project_id, doc_id}, "done processing" - markAsChecked() + processOldPack(project_id, doc_id, pack_id, callback) { + const markAsChecked = err => + PackManager.markPackAsChecked(project_id, doc_id, pack_id, function(err2) { + if (err2 != null) { return callback(err2); } + return callback(err); + }) + ; + logger.log({project_id, doc_id}, "processing old packs"); + return db.docHistory.findOne({_id:pack_id}, function(err, pack) { + if (err != null) { return markAsChecked(err); } + if ((pack == null)) { return markAsChecked(); } + if (pack.expiresAt != null) { return callback(); } // return directly + return PackManager.finaliseIfNeeded(project_id, doc_id, pack._id, pack, function(err) { + if (err != null) { return markAsChecked(err); } + return PackManager.updateIndexIfNeeded(project_id, doc_id, function(err) { + if (err != null) { return markAsChecked(err); } + return PackManager.findUnarchivedPacks(project_id, doc_id, function(err, unarchivedPacks) { + if (err != null) { return markAsChecked(err); } + if (!(unarchivedPacks != null ? unarchivedPacks.length : undefined)) { + logger.log({project_id, doc_id}, "no packs need archiving"); + return markAsChecked(); + } + return async.eachSeries(unarchivedPacks, (pack, cb) => PackManager.archivePack(project_id, doc_id, pack._id, cb) + , function(err) { + if (err != null) { return markAsChecked(err); } + logger.log({project_id, doc_id}, "done processing"); + return markAsChecked(); + }); + }); + }); + }); + }); + }, - finaliseIfNeeded: (project_id, doc_id, pack_id, pack, callback) -> - sz = pack.sz / (1024 * 1024) # in fractions of a megabyte - n = pack.n / 1024 # in fraction of 1024 ops - age = (Date.now() - pack.meta.end_ts) / DAYS - if age < 30 # always keep if less than 1 month old - logger.log {project_id, doc_id, pack_id, age}, "less than 30 days old" - return callback() - # compute an archiving threshold which decreases for each month of age - archive_threshold = 30 / age - if sz > archive_threshold or n > archive_threshold or age > 90 - logger.log {project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "meets archive threshold" - PackManager.markPackAsFinalisedWithLock project_id, doc_id, pack_id, callback - else - logger.log {project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "does not meet archive threshold" - callback() + finaliseIfNeeded(project_id, doc_id, pack_id, pack, callback) { + const sz = pack.sz / (1024 * 1024); // in fractions of a megabyte + const n = pack.n / 1024; // in fraction of 1024 ops + const age = (Date.now() - pack.meta.end_ts) / DAYS; + if (age < 30) { // always keep if less than 1 month old + logger.log({project_id, doc_id, pack_id, age}, "less than 30 days old"); + return callback(); + } + // compute an archiving threshold which decreases for each month of age + const archive_threshold = 30 / age; + if ((sz > archive_threshold) || (n > archive_threshold) || (age > 90)) { + logger.log({project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "meets archive threshold"); + return PackManager.markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback); + } else { + logger.log({project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "does not meet archive threshold"); + return callback(); + } + }, - markPackAsFinalisedWithLock: (project_id, doc_id, pack_id, callback) -> - LockManager.runWithLock( + markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback) { + return LockManager.runWithLock( keys.historyLock({doc_id}), - (releaseLock) -> - PackManager._markPackAsFinalised project_id, doc_id, pack_id, releaseLock + releaseLock => PackManager._markPackAsFinalised(project_id, doc_id, pack_id, releaseLock), callback - ) + ); + }, - _markPackAsFinalised: (project_id, doc_id, pack_id, callback) -> - logger.log {project_id, doc_id, pack_id}, "marking pack as finalised" - db.docHistory.findAndModify { - query: {_id: pack_id} + _markPackAsFinalised(project_id, doc_id, pack_id, callback) { + logger.log({project_id, doc_id, pack_id}, "marking pack as finalised"); + return db.docHistory.findAndModify({ + query: {_id: pack_id}, update: {$set: {finalised: true}} - }, callback + }, callback); + }, - updateIndexIfNeeded: (project_id, doc_id, callback) -> - logger.log {project_id, doc_id}, "archiving old packs" - PackManager.getIndexWithKeys doc_id, (err, index) -> - return callback(err) if err? - if not index? - PackManager.initialiseIndex project_id, doc_id, callback - else - PackManager.updateIndex project_id, doc_id, callback + updateIndexIfNeeded(project_id, doc_id, callback) { + logger.log({project_id, doc_id}, "archiving old packs"); + return PackManager.getIndexWithKeys(doc_id, function(err, index) { + if (err != null) { return callback(err); } + if ((index == null)) { + return PackManager.initialiseIndex(project_id, doc_id, callback); + } else { + return PackManager.updateIndex(project_id, doc_id, callback); + } + }); + }, - markPackAsChecked: (project_id, doc_id, pack_id, callback) -> - logger.log {project_id, doc_id, pack_id}, "marking pack as checked" - db.docHistory.findAndModify { - query: {_id: pack_id} + markPackAsChecked(project_id, doc_id, pack_id, callback) { + logger.log({project_id, doc_id, pack_id}, "marking pack as checked"); + return db.docHistory.findAndModify({ + query: {_id: pack_id}, update: {$currentDate: {"last_checked":true}} - }, callback + }, callback); + }, - findUnarchivedPacks: (project_id, doc_id, callback) -> - PackManager.getIndex doc_id, (err, indexResult) -> - return callback(err) if err? - indexPacks = indexResult?.packs or [] - unArchivedPacks = (pack for pack in indexPacks when not pack.inS3?) - if unArchivedPacks.length - logger.log {project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs" - callback(null, unArchivedPacks) + findUnarchivedPacks(project_id, doc_id, callback) { + return PackManager.getIndex(doc_id, function(err, indexResult) { + if (err != null) { return callback(err); } + const indexPacks = (indexResult != null ? indexResult.packs : undefined) || []; + const unArchivedPacks = ((() => { + const result = []; + for (let pack of Array.from(indexPacks)) { if ((pack.inS3 == null)) { + result.push(pack); + } + } + return result; + })()); + if (unArchivedPacks.length) { + logger.log({project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs"); + } + return callback(null, unArchivedPacks); + }); + }, - # Archive locking flags + // Archive locking flags - checkArchiveNotInProgress: (project_id, doc_id, pack_id, callback) -> - logger.log {project_id, doc_id, pack_id}, "checking if archive in progress" - PackManager.getPackFromIndex doc_id, pack_id, (err, result) -> - return callback(err) if err? - return callback new Error("pack not found in index") if not result? - if result.inS3 - return callback new Error("pack archiving already done") - else if result.inS3? - return callback new Error("pack archiving already in progress") - else - return callback() + checkArchiveNotInProgress(project_id, doc_id, pack_id, callback) { + logger.log({project_id, doc_id, pack_id}, "checking if archive in progress"); + return PackManager.getPackFromIndex(doc_id, pack_id, function(err, result) { + if (err != null) { return callback(err); } + if ((result == null)) { return callback(new Error("pack not found in index")); } + if (result.inS3) { + return callback(new Error("pack archiving already done")); + } else if (result.inS3 != null) { + return callback(new Error("pack archiving already in progress")); + } else { + return callback(); + } + }); + }, - markPackAsArchiveInProgress: (project_id, doc_id, pack_id, callback) -> - logger.log {project_id, doc_id}, "marking pack as archive in progress status" - db.docHistoryIndex.findAndModify { - query: {_id:ObjectId(doc_id.toString()), packs: {$elemMatch: {"_id": pack_id, inS3: {$exists:false}}}} - fields: { "packs.$": 1 } + markPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { + logger.log({project_id, doc_id}, "marking pack as archive in progress status"); + return db.docHistoryIndex.findAndModify({ + query: {_id:ObjectId(doc_id.toString()), packs: {$elemMatch: {"_id": pack_id, inS3: {$exists:false}}}}, + fields: { "packs.$": 1 }, update: {$set: {"packs.$.inS3":false}} - }, (err, result) -> - return callback(err) if err? - return callback new Error("archive is already in progress") if not result? - logger.log {project_id, doc_id, pack_id}, "marked as archive in progress" - callback() + }, function(err, result) { + if (err != null) { return callback(err); } + if ((result == null)) { return callback(new Error("archive is already in progress")); } + logger.log({project_id, doc_id, pack_id}, "marked as archive in progress"); + return callback(); + }); + }, - clearPackAsArchiveInProgress: (project_id, doc_id, pack_id, callback) -> - logger.log {project_id, doc_id, pack_id}, "clearing as archive in progress" - db.docHistoryIndex.findAndModify { - query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}} - fields: { "packs.$": 1 } + clearPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { + logger.log({project_id, doc_id, pack_id}, "clearing as archive in progress"); + return db.docHistoryIndex.findAndModify({ + query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}}, + fields: { "packs.$": 1 }, update: {$unset: {"packs.$.inS3":true}} - }, callback + }, callback); + }, - markPackAsArchived: (project_id, doc_id, pack_id, callback) -> - logger.log {project_id, doc_id, pack_id}, "marking pack as archived" - db.docHistoryIndex.findAndModify { - query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}} - fields: { "packs.$": 1 } + markPackAsArchived(project_id, doc_id, pack_id, callback) { + logger.log({project_id, doc_id, pack_id}, "marking pack as archived"); + return db.docHistoryIndex.findAndModify({ + query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}}, + fields: { "packs.$": 1 }, update: {$set: {"packs.$.inS3":true}} - }, (err, result) -> - return callback(err) if err? - return callback new Error("archive is not marked as progress") if not result? - logger.log {project_id, doc_id, pack_id}, "marked as archived" - callback() + }, function(err, result) { + if (err != null) { return callback(err); } + if ((result == null)) { return callback(new Error("archive is not marked as progress")); } + logger.log({project_id, doc_id, pack_id}, "marked as archived"); + return callback(); + }); + }, - setTTLOnArchivedPack: (project_id, doc_id, pack_id, callback) -> - db.docHistory.findAndModify { - query: {_id: pack_id} - update: {$set: {expiresAt: new Date(Date.now() + 1*DAYS)}} - }, (err) -> - logger.log {project_id, doc_id, pack_id}, "set expiry on pack" - callback() + setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) { + return db.docHistory.findAndModify({ + query: {_id: pack_id}, + update: {$set: {expiresAt: new Date(Date.now() + (1*DAYS))}} + }, function(err) { + logger.log({project_id, doc_id, pack_id}, "set expiry on pack"); + return callback(); + }); + } +}); -# _getOneDayInFutureWithRandomDelay: -> -# thirtyMins = 1000 * 60 * 30 -# randomThirtyMinMax = Math.ceil(Math.random() * thirtyMins) -# return new Date(Date.now() + randomThirtyMinMax + 1*DAYS) +// _getOneDayInFutureWithRandomDelay: -> +// thirtyMins = 1000 * 60 * 30 +// randomThirtyMinMax = Math.ceil(Math.random() * thirtyMins) +// return new Date(Date.now() + randomThirtyMinMax + 1*DAYS) diff --git a/services/track-changes/app/coffee/PackWorker.js b/services/track-changes/app/coffee/PackWorker.js index e4c31c3b4a..10431a85a8 100644 --- a/services/track-changes/app/coffee/PackWorker.js +++ b/services/track-changes/app/coffee/PackWorker.js @@ -1,139 +1,183 @@ -Settings = require "settings-sharelatex" -async = require "async" -_ = require "underscore" -{db, ObjectId, BSON} = require "./mongojs" -fs = require "fs" -Metrics = require "metrics-sharelatex" -Metrics.initialize("track-changes") -logger = require "logger-sharelatex" -logger.initialize("track-changes-packworker") -if Settings.sentry?.dsn? - logger.initializeErrorReporting(Settings.sentry.dsn) +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let LIMIT, pending; +let project_id, doc_id; +const Settings = require("settings-sharelatex"); +const async = require("async"); +const _ = require("underscore"); +const {db, ObjectId, BSON} = require("./mongojs"); +const fs = require("fs"); +const Metrics = require("metrics-sharelatex"); +Metrics.initialize("track-changes"); +const logger = require("logger-sharelatex"); +logger.initialize("track-changes-packworker"); +if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) { + logger.initializeErrorReporting(Settings.sentry.dsn); +} -DAYS = 24 * 3600 * 1000 +const DAYS = 24 * 3600 * 1000; -LockManager = require "./LockManager" -PackManager = require "./PackManager" +const LockManager = require("./LockManager"); +const PackManager = require("./PackManager"); -# this worker script is forked by the main process to look for -# document histories which can be archived +// this worker script is forked by the main process to look for +// document histories which can be archived -source = process.argv[2] -DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 -TIMEOUT = Number(process.argv[4]) || 30*60*1000 -COUNT = 0 # number processed -TOTAL = 0 # total number to process +const source = process.argv[2]; +const DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000; +const TIMEOUT = Number(process.argv[4]) || (30*60*1000); +let COUNT = 0; // number processed +let TOTAL = 0; // total number to process -if !source.match(/^[0-9]+$/) - file = fs.readFileSync source - result = for line in file.toString().split('\n') - [project_id, doc_id] = line.split(' ') - {doc_id, project_id} - pending = _.filter result, (row) -> row?.doc_id?.match(/^[a-f0-9]{24}$/) -else - LIMIT = Number(process.argv[2]) || 1000 +if (!source.match(/^[0-9]+$/)) { + const file = fs.readFileSync(source); + const result = (() => { + const result1 = []; + for (let line of Array.from(file.toString().split('\n'))) { + [project_id, doc_id] = Array.from(line.split(' ')); + result1.push({doc_id, project_id}); + } + return result1; + })(); + pending = _.filter(result, row => __guard__(row != null ? row.doc_id : undefined, x => x.match(/^[a-f0-9]{24}$/))); +} else { + LIMIT = Number(process.argv[2]) || 1000; +} -shutDownRequested = false -shutDownTimer = setTimeout () -> - logger.log "pack timed out, requesting shutdown" - # start the shutdown on the next pack - shutDownRequested = true - # do a hard shutdown after a further 5 minutes - hardTimeout = setTimeout () -> - logger.error "HARD TIMEOUT in pack archive worker" - process.exit() - , 5*60*1000 - hardTimeout.unref() -, TIMEOUT +let shutDownRequested = false; +const shutDownTimer = setTimeout(function() { + logger.log("pack timed out, requesting shutdown"); + // start the shutdown on the next pack + shutDownRequested = true; + // do a hard shutdown after a further 5 minutes + const hardTimeout = setTimeout(function() { + logger.error("HARD TIMEOUT in pack archive worker"); + return process.exit(); + } + , 5*60*1000); + return hardTimeout.unref(); +} +, TIMEOUT); -logger.log "checking for updates, limit=#{LIMIT}, delay=#{DOCUMENT_PACK_DELAY}, timeout=#{TIMEOUT}" +logger.log(`checking for updates, limit=${LIMIT}, delay=${DOCUMENT_PACK_DELAY}, timeout=${TIMEOUT}`); -# work around for https://github.com/mafintosh/mongojs/issues/224 -db.close = (callback) -> - this._getServer (err, server) -> - return callback(err) if err? - server = if server.destroy? then server else server.topology - server.destroy(true, true) - callback() +// work around for https://github.com/mafintosh/mongojs/issues/224 +db.close = function(callback) { + return this._getServer(function(err, server) { + if (err != null) { return callback(err); } + server = (server.destroy != null) ? server : server.topology; + server.destroy(true, true); + return callback(); + }); +}; -finish = () -> - if shutDownTimer? - logger.log 'cancelling timeout' - clearTimeout shutDownTimer - logger.log 'closing db' - db.close () -> - logger.log 'closing LockManager Redis Connection' - LockManager.close () -> - logger.log {processedCount: COUNT, allCount: TOTAL}, 'ready to exit from pack archive worker' - hardTimeout = setTimeout () -> - logger.error 'hard exit from pack archive worker' - process.exit(1) - , 5*1000 - hardTimeout.unref() +const finish = function() { + if (shutDownTimer != null) { + logger.log('cancelling timeout'); + clearTimeout(shutDownTimer); + } + logger.log('closing db'); + return db.close(function() { + logger.log('closing LockManager Redis Connection'); + return LockManager.close(function() { + logger.log({processedCount: COUNT, allCount: TOTAL}, 'ready to exit from pack archive worker'); + const hardTimeout = setTimeout(function() { + logger.error('hard exit from pack archive worker'); + return process.exit(1); + } + , 5*1000); + return hardTimeout.unref(); + }); + }); +}; -process.on 'exit', (code) -> - logger.log {code}, 'pack archive worker exited' +process.on('exit', code => logger.log({code}, 'pack archive worker exited')); -processUpdates = (pending) -> - async.eachSeries pending, (result, callback) -> - {_id, project_id, doc_id} = result - COUNT++ - logger.log {project_id, doc_id}, "processing #{COUNT}/#{TOTAL}" - if not project_id? or not doc_id? - logger.log {project_id, doc_id}, "skipping pack, missing project/doc id" - return callback() - handler = (err, result) -> - if err? and err.code is "InternalError" and err.retryable - logger.warn {err, result}, "ignoring S3 error in pack archive worker" - # Ignore any s3 errors due to random problems - err = null - if err? - logger.error {err, result}, "error in pack archive worker" - return callback(err) - if shutDownRequested - logger.warn "shutting down pack archive worker" - return callback(new Error("shutdown")) - setTimeout () -> - callback(err, result) - , DOCUMENT_PACK_DELAY - if not _id? - PackManager.pushOldPacks project_id, doc_id, handler - else - PackManager.processOldPack project_id, doc_id, _id, handler - , (err, results) -> - if err? and err.message != "shutdown" - logger.error {err}, 'error in pack archive worker processUpdates' - finish() +const processUpdates = pending => + async.eachSeries(pending, function(result, callback) { + let _id; + ({_id, project_id, doc_id} = result); + COUNT++; + logger.log({project_id, doc_id}, `processing ${COUNT}/${TOTAL}`); + if ((project_id == null) || (doc_id == null)) { + logger.log({project_id, doc_id}, "skipping pack, missing project/doc id"); + return callback(); + } + const handler = function(err, result) { + if ((err != null) && (err.code === "InternalError") && err.retryable) { + logger.warn({err, result}, "ignoring S3 error in pack archive worker"); + // Ignore any s3 errors due to random problems + err = null; + } + if (err != null) { + logger.error({err, result}, "error in pack archive worker"); + return callback(err); + } + if (shutDownRequested) { + logger.warn("shutting down pack archive worker"); + return callback(new Error("shutdown")); + } + return setTimeout(() => callback(err, result) + , DOCUMENT_PACK_DELAY); + }; + if ((_id == null)) { + return PackManager.pushOldPacks(project_id, doc_id, handler); + } else { + return PackManager.processOldPack(project_id, doc_id, _id, handler); + } + } + , function(err, results) { + if ((err != null) && (err.message !== "shutdown")) { + logger.error({err}, 'error in pack archive worker processUpdates'); + } + return finish(); + }) +; -# find the packs which can be archived +// find the packs which can be archived -ObjectIdFromDate = (date) -> - id = Math.floor(date.getTime() / 1000).toString(16) + "0000000000000000"; - return ObjectId(id) +const ObjectIdFromDate = function(date) { + const id = Math.floor(date.getTime() / 1000).toString(16) + "0000000000000000"; + return ObjectId(id); +}; -# new approach, two passes -# find packs to be marked as finalised:true, those which have a newer pack present -# then only consider finalised:true packs for archiving +// new approach, two passes +// find packs to be marked as finalised:true, those which have a newer pack present +// then only consider finalised:true packs for archiving -if pending? - logger.log "got #{pending.length} entries from #{source}" - processUpdates pending -else - oneWeekAgo = new Date(Date.now() - 7 * DAYS) +if (pending != null) { + logger.log(`got ${pending.length} entries from ${source}`); + processUpdates(pending); +} else { + const oneWeekAgo = new Date(Date.now() - (7 * DAYS)); db.docHistory.find({ - expiresAt: {$exists: false} - project_id: {$exists: true} - v_end: {$exists: true} - _id: {$lt: ObjectIdFromDate(oneWeekAgo)} + expiresAt: {$exists: false}, + project_id: {$exists: true}, + v_end: {$exists: true}, + _id: {$lt: ObjectIdFromDate(oneWeekAgo)}, last_checked: {$lt: oneWeekAgo} }, {_id:1, doc_id:1, project_id:1}).sort({ last_checked:1 - }).limit LIMIT, (err, results) -> - if err? - logger.log {err}, 'error checking for updates' - finish() - return - pending = _.uniq results, false, (result) -> result.doc_id.toString() - TOTAL = pending.length - logger.log "found #{TOTAL} documents to archive" - processUpdates pending + }).limit(LIMIT, function(err, results) { + if (err != null) { + logger.log({err}, 'error checking for updates'); + finish(); + return; + } + pending = _.uniq(results, false, result => result.doc_id.toString()); + TOTAL = pending.length; + logger.log(`found ${TOTAL} documents to archive`); + return processUpdates(pending); + }); +} + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/app/coffee/ProjectIterator.js b/services/track-changes/app/coffee/ProjectIterator.js index d64fc260e8..5768282960 100644 --- a/services/track-changes/app/coffee/ProjectIterator.js +++ b/services/track-changes/app/coffee/ProjectIterator.js @@ -1,62 +1,84 @@ -Heap = require "heap" +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let ProjectIterator; +const Heap = require("heap"); -module.exports = ProjectIterator = +module.exports = (ProjectIterator = - class ProjectIterator - constructor: (packs, @before, @getPackByIdFn) -> - byEndTs = (a,b) -> (b.meta.end_ts - a.meta.end_ts) || (a.fromIndex - b.fromIndex) - @packs = packs.slice().sort byEndTs - @queue = new Heap(byEndTs) + (ProjectIterator = class ProjectIterator { + constructor(packs, before, getPackByIdFn) { + this.before = before; + this.getPackByIdFn = getPackByIdFn; + const byEndTs = (a,b) => (b.meta.end_ts - a.meta.end_ts) || (a.fromIndex - b.fromIndex); + this.packs = packs.slice().sort(byEndTs); + this.queue = new Heap(byEndTs); + } - next: (callback) -> - # what's up next - #console.log ">>> top item", iterator.packs[0] - iterator = this - before = @before - queue = iterator.queue - opsToReturn = [] - nextPack = iterator.packs[0] - lowWaterMark = nextPack?.meta.end_ts || 0 - nextItem = queue.peek() + next(callback) { + // what's up next + //console.log ">>> top item", iterator.packs[0] + const iterator = this; + const { before } = this; + const { queue } = iterator; + const opsToReturn = []; + let nextPack = iterator.packs[0]; + let lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0; + let nextItem = queue.peek(); - #console.log "queue empty?", queue.empty() - #console.log "nextItem", nextItem - #console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts - #console.log "lowWaterMark", lowWaterMark + //console.log "queue empty?", queue.empty() + //console.log "nextItem", nextItem + //console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts + //console.log "lowWaterMark", lowWaterMark - while before? and nextPack?.meta.start_ts > before - # discard pack that is outside range - iterator.packs.shift() - nextPack = iterator.packs[0] - lowWaterMark = nextPack?.meta.end_ts || 0 + while ((before != null) && ((nextPack != null ? nextPack.meta.start_ts : undefined) > before)) { + // discard pack that is outside range + iterator.packs.shift(); + nextPack = iterator.packs[0]; + lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0; + } - if (queue.empty() or nextItem?.meta.end_ts <= lowWaterMark) and nextPack? - # retrieve the next pack and populate the queue - return @getPackByIdFn nextPack.project_id, nextPack.doc_id, nextPack._id, (err, pack) -> - return callback(err) if err? - iterator.packs.shift() # have now retrieved this pack, remove it - #console.log "got pack", pack - for op in pack.pack when (not before? or op.meta.end_ts < before) - #console.log "adding op", op - op.doc_id = nextPack.doc_id - op.project_id = nextPack.project_id - queue.push op - # now try again - return iterator.next(callback) + if ((queue.empty() || ((nextItem != null ? nextItem.meta.end_ts : undefined) <= lowWaterMark)) && (nextPack != null)) { + // retrieve the next pack and populate the queue + return this.getPackByIdFn(nextPack.project_id, nextPack.doc_id, nextPack._id, function(err, pack) { + if (err != null) { return callback(err); } + iterator.packs.shift(); // have now retrieved this pack, remove it + //console.log "got pack", pack + for (let op of Array.from(pack.pack)) { + //console.log "adding op", op + if ((before == null) || (op.meta.end_ts < before)) { + op.doc_id = nextPack.doc_id; + op.project_id = nextPack.project_id; + queue.push(op); + } + } + // now try again + return iterator.next(callback); + }); + } - #console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark - while nextItem? and (nextItem?.meta.end_ts > lowWaterMark) - opsToReturn.push nextItem - queue.pop() - nextItem = queue.peek() + //console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark + while ((nextItem != null) && ((nextItem != null ? nextItem.meta.end_ts : undefined) > lowWaterMark)) { + opsToReturn.push(nextItem); + queue.pop(); + nextItem = queue.peek(); + } - #console.log "queue empty?", queue.empty() - #console.log "nextPack", nextPack? + //console.log "queue empty?", queue.empty() + //console.log "nextPack", nextPack? - if queue.empty() and not nextPack? # got everything - iterator._done = true + if (queue.empty() && (nextPack == null)) { // got everything + iterator._done = true; + } - callback(null, opsToReturn) + return callback(null, opsToReturn); + } - done: () -> - return @_done + done() { + return this._done; + } + })); diff --git a/services/track-changes/app/coffee/RedisManager.js b/services/track-changes/app/coffee/RedisManager.js index b23a308fc5..a75cef3394 100644 --- a/services/track-changes/app/coffee/RedisManager.js +++ b/services/track-changes/app/coffee/RedisManager.js @@ -1,80 +1,121 @@ -Settings = require "settings-sharelatex" -redis = require("redis-sharelatex") -rclient = redis.createClient(Settings.redis.history) -Keys = Settings.redis.history.key_schema -async = require "async" +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let RedisManager; +const Settings = require("settings-sharelatex"); +const redis = require("redis-sharelatex"); +const rclient = redis.createClient(Settings.redis.history); +const Keys = Settings.redis.history.key_schema; +const async = require("async"); -module.exports = RedisManager = +module.exports = (RedisManager = { - getOldestDocUpdates: (doc_id, batchSize, callback = (error, jsonUpdates) ->) -> - key = Keys.uncompressedHistoryOps({doc_id}) - rclient.lrange key, 0, batchSize - 1, callback + getOldestDocUpdates(doc_id, batchSize, callback) { + if (callback == null) { callback = function(error, jsonUpdates) {}; } + const key = Keys.uncompressedHistoryOps({doc_id}); + return rclient.lrange(key, 0, batchSize - 1, callback); + }, - expandDocUpdates: (jsonUpdates, callback = (error, rawUpdates) ->) -> - try - rawUpdates = ( JSON.parse(update) for update in jsonUpdates or [] ) - catch e - return callback(e) - callback null, rawUpdates + expandDocUpdates(jsonUpdates, callback) { + let rawUpdates; + if (callback == null) { callback = function(error, rawUpdates) {}; } + try { + rawUpdates = ( Array.from(jsonUpdates || []).map((update) => JSON.parse(update)) ); + } catch (e) { + return callback(e); + } + return callback(null, rawUpdates); + }, - deleteAppliedDocUpdates: (project_id, doc_id, docUpdates, callback = (error) ->) -> - multi = rclient.multi() - # Delete all the updates which have been applied (exact match) - for update in docUpdates or [] - multi.lrem Keys.uncompressedHistoryOps({doc_id}), 1, update - multi.exec (error, results) -> - return callback(error) if error? - # It's ok to delete the doc_id from the set here. Even though the list - # of updates may not be empty, we will continue to process it until it is. - rclient.srem Keys.docsWithHistoryOps({project_id}), doc_id, (error) -> - return callback(error) if error? - callback null + deleteAppliedDocUpdates(project_id, doc_id, docUpdates, callback) { + if (callback == null) { callback = function(error) {}; } + const multi = rclient.multi(); + // Delete all the updates which have been applied (exact match) + for (let update of Array.from(docUpdates || [])) { + multi.lrem(Keys.uncompressedHistoryOps({doc_id}), 1, update); + } + return multi.exec(function(error, results) { + if (error != null) { return callback(error); } + // It's ok to delete the doc_id from the set here. Even though the list + // of updates may not be empty, we will continue to process it until it is. + return rclient.srem(Keys.docsWithHistoryOps({project_id}), doc_id, function(error) { + if (error != null) { return callback(error); } + return callback(null); + }); + }); + }, - getDocIdsWithHistoryOps: (project_id, callback = (error, doc_ids) ->) -> - rclient.smembers Keys.docsWithHistoryOps({project_id}), callback + getDocIdsWithHistoryOps(project_id, callback) { + if (callback == null) { callback = function(error, doc_ids) {}; } + return rclient.smembers(Keys.docsWithHistoryOps({project_id}), callback); + }, - # iterate over keys asynchronously using redis scan (non-blocking) - # handle all the cluster nodes or single redis server - _getKeys: (pattern, callback) -> - nodes = rclient.nodes?('master') || [ rclient ]; - doKeyLookupForNode = (node, cb) -> - RedisManager._getKeysFromNode node, pattern, cb - async.concatSeries nodes, doKeyLookupForNode, callback + // iterate over keys asynchronously using redis scan (non-blocking) + // handle all the cluster nodes or single redis server + _getKeys(pattern, callback) { + const nodes = (typeof rclient.nodes === 'function' ? rclient.nodes('master') : undefined) || [ rclient ]; + const doKeyLookupForNode = (node, cb) => RedisManager._getKeysFromNode(node, pattern, cb); + return async.concatSeries(nodes, doKeyLookupForNode, callback); + }, - _getKeysFromNode: (node, pattern, callback) -> - cursor = 0 # redis iterator - keySet = {} # use hash to avoid duplicate results - # scan over all keys looking for pattern - doIteration = (cb) -> - node.scan cursor, "MATCH", pattern, "COUNT", 1000, (error, reply) -> - return callback(error) if error? - [cursor, keys] = reply - for key in keys - keySet[key] = true - if cursor == '0' # note redis returns string result not numeric - return callback(null, Object.keys(keySet)) - else - doIteration() - doIteration() + _getKeysFromNode(node, pattern, callback) { + let cursor = 0; // redis iterator + const keySet = {}; // use hash to avoid duplicate results + // scan over all keys looking for pattern + var doIteration = cb => + node.scan(cursor, "MATCH", pattern, "COUNT", 1000, function(error, reply) { + let keys; + if (error != null) { return callback(error); } + [cursor, keys] = Array.from(reply); + for (let key of Array.from(keys)) { + keySet[key] = true; + } + if (cursor === '0') { // note redis returns string result not numeric + return callback(null, Object.keys(keySet)); + } else { + return doIteration(); + } + }) + ; + return doIteration(); + }, - # extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b - # or DocsWithHistoryOps:{57fd0b1f53a8396d22b2c24b} (for redis cluster) - _extractIds: (keyList) -> - ids = for key in keyList - m = key.match(/:\{?([0-9a-f]{24})\}?/) # extract object id - m[1] - return ids + // extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b + // or DocsWithHistoryOps:{57fd0b1f53a8396d22b2c24b} (for redis cluster) + _extractIds(keyList) { + const ids = (() => { + const result = []; + for (let key of Array.from(keyList)) { + const m = key.match(/:\{?([0-9a-f]{24})\}?/); // extract object id + result.push(m[1]); + } + return result; + })(); + return ids; + }, - getProjectIdsWithHistoryOps: (callback = (error, project_ids) ->) -> - RedisManager._getKeys Keys.docsWithHistoryOps({project_id:"*"}), (error, project_keys) -> - return callback(error) if error? - project_ids = RedisManager._extractIds project_keys - callback(error, project_ids) + getProjectIdsWithHistoryOps(callback) { + if (callback == null) { callback = function(error, project_ids) {}; } + return RedisManager._getKeys(Keys.docsWithHistoryOps({project_id:"*"}), function(error, project_keys) { + if (error != null) { return callback(error); } + const project_ids = RedisManager._extractIds(project_keys); + return callback(error, project_ids); + }); + }, - getAllDocIdsWithHistoryOps: (callback = (error, doc_ids) ->) -> - # return all the docids, to find dangling history entries after - # everything is flushed. - RedisManager._getKeys Keys.uncompressedHistoryOps({doc_id:"*"}), (error, doc_keys) -> - return callback(error) if error? - doc_ids = RedisManager._extractIds doc_keys - callback(error, doc_ids) + getAllDocIdsWithHistoryOps(callback) { + // return all the docids, to find dangling history entries after + // everything is flushed. + if (callback == null) { callback = function(error, doc_ids) {}; } + return RedisManager._getKeys(Keys.uncompressedHistoryOps({doc_id:"*"}), function(error, doc_keys) { + if (error != null) { return callback(error); } + const doc_ids = RedisManager._extractIds(doc_keys); + return callback(error, doc_ids); + }); + } +}); diff --git a/services/track-changes/app/coffee/RestoreManager.js b/services/track-changes/app/coffee/RestoreManager.js index cfabca2fb7..62c2698b68 100644 --- a/services/track-changes/app/coffee/RestoreManager.js +++ b/services/track-changes/app/coffee/RestoreManager.js @@ -1,12 +1,24 @@ -DocumentUpdaterManager = require "./DocumentUpdaterManager" -DiffManager = require "./DiffManager" -logger = require "logger-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let RestoreManager; +const DocumentUpdaterManager = require("./DocumentUpdaterManager"); +const DiffManager = require("./DiffManager"); +const logger = require("logger-sharelatex"); -module.exports = RestoreManager = - restoreToBeforeVersion: (project_id, doc_id, version, user_id, callback = (error) ->) -> - logger.log project_id: project_id, doc_id: doc_id, version: version, user_id: user_id, "restoring document" - DiffManager.getDocumentBeforeVersion project_id, doc_id, version, (error, content) -> - return callback(error) if error? - DocumentUpdaterManager.setDocument project_id, doc_id, content, user_id, (error) -> - return callback(error) if error? - callback() +module.exports = (RestoreManager = { + restoreToBeforeVersion(project_id, doc_id, version, user_id, callback) { + if (callback == null) { callback = function(error) {}; } + logger.log({project_id, doc_id, version, user_id}, "restoring document"); + return DiffManager.getDocumentBeforeVersion(project_id, doc_id, version, function(error, content) { + if (error != null) { return callback(error); } + return DocumentUpdaterManager.setDocument(project_id, doc_id, content, user_id, function(error) { + if (error != null) { return callback(error); } + return callback(); + }); + }); + } +}); diff --git a/services/track-changes/app/coffee/UpdateCompressor.js b/services/track-changes/app/coffee/UpdateCompressor.js index 9e11b281e6..c9716a3024 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.js +++ b/services/track-changes/app/coffee/UpdateCompressor.js @@ -1,218 +1,278 @@ -strInject = (s1, pos, s2) -> s1[...pos] + s2 + s1[pos..] -strRemove = (s1, pos, length) -> s1[...pos] + s1[(pos + length)..] +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS103: Rewrite code to no longer use __guard__ + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let oneMinute, twoMegabytes, UpdateCompressor; +const strInject = (s1, pos, s2) => s1.slice(0, pos) + s2 + s1.slice(pos); +const strRemove = (s1, pos, length) => s1.slice(0, pos) + s1.slice((pos + length)); -diff_match_patch = require("../lib/diff_match_patch").diff_match_patch -dmp = new diff_match_patch() +const { diff_match_patch } = require("../lib/diff_match_patch"); +const dmp = new diff_match_patch(); -module.exports = UpdateCompressor = - NOOP: "noop" +module.exports = (UpdateCompressor = { + NOOP: "noop", - # Updates come from the doc updater in format - # { - # op: [ { ... op1 ... }, { ... op2 ... } ] - # meta: { ts: ..., user_id: ... } - # } - # but it's easier to work with on op per update, so convert these updates to - # our compressed format - # [{ - # op: op1 - # meta: { start_ts: ... , end_ts: ..., user_id: ... } - # }, { - # op: op2 - # meta: { start_ts: ... , end_ts: ..., user_id: ... } - # }] - convertToSingleOpUpdates: (updates) -> - splitUpdates = [] - for update in updates - # Reject any non-insert or delete ops, i.e. comments - ops = update.op.filter (o) -> o.i? or o.d? - if ops.length == 0 - splitUpdates.push - op: UpdateCompressor.NOOP - meta: - start_ts: update.meta.start_ts or update.meta.ts - end_ts: update.meta.end_ts or update.meta.ts + // Updates come from the doc updater in format + // { + // op: [ { ... op1 ... }, { ... op2 ... } ] + // meta: { ts: ..., user_id: ... } + // } + // but it's easier to work with on op per update, so convert these updates to + // our compressed format + // [{ + // op: op1 + // meta: { start_ts: ... , end_ts: ..., user_id: ... } + // }, { + // op: op2 + // meta: { start_ts: ... , end_ts: ..., user_id: ... } + // }] + convertToSingleOpUpdates(updates) { + const splitUpdates = []; + for (let update of Array.from(updates)) { + // Reject any non-insert or delete ops, i.e. comments + const ops = update.op.filter(o => (o.i != null) || (o.d != null)); + if (ops.length === 0) { + splitUpdates.push({ + op: UpdateCompressor.NOOP, + meta: { + start_ts: update.meta.start_ts || update.meta.ts, + end_ts: update.meta.end_ts || update.meta.ts, user_id: update.meta.user_id + }, v: update.v - else - for op in ops - splitUpdates.push - op: op - meta: - start_ts: update.meta.start_ts or update.meta.ts - end_ts: update.meta.end_ts or update.meta.ts + }); + } else { + for (let op of Array.from(ops)) { + splitUpdates.push({ + op, + meta: { + start_ts: update.meta.start_ts || update.meta.ts, + end_ts: update.meta.end_ts || update.meta.ts, user_id: update.meta.user_id + }, v: update.v - return splitUpdates + }); + } + } + } + return splitUpdates; + }, - concatUpdatesWithSameVersion: (updates) -> - concattedUpdates = [] - for update in updates - lastUpdate = concattedUpdates[concattedUpdates.length - 1] - if lastUpdate? and lastUpdate.v == update.v - lastUpdate.op.push update.op unless update.op == UpdateCompressor.NOOP - else - nextUpdate = - op: [] - meta: update.meta + concatUpdatesWithSameVersion(updates) { + const concattedUpdates = []; + for (let update of Array.from(updates)) { + const lastUpdate = concattedUpdates[concattedUpdates.length - 1]; + if ((lastUpdate != null) && (lastUpdate.v === update.v)) { + if (update.op !== UpdateCompressor.NOOP) { lastUpdate.op.push(update.op); } + } else { + const nextUpdate = { + op: [], + meta: update.meta, v: update.v - nextUpdate.op.push update.op unless update.op == UpdateCompressor.NOOP - concattedUpdates.push nextUpdate - return concattedUpdates + }; + if (update.op !== UpdateCompressor.NOOP) { nextUpdate.op.push(update.op); } + concattedUpdates.push(nextUpdate); + } + } + return concattedUpdates; + }, - compressRawUpdates: (lastPreviousUpdate, rawUpdates) -> - if lastPreviousUpdate?.op?.length > 1 - # if the last previous update was an array op, don't compress onto it. - # The avoids cases where array length changes but version number doesn't - return [lastPreviousUpdate].concat UpdateCompressor.compressRawUpdates(null,rawUpdates) - if lastPreviousUpdate? - rawUpdates = [lastPreviousUpdate].concat(rawUpdates) - updates = UpdateCompressor.convertToSingleOpUpdates(rawUpdates) - updates = UpdateCompressor.compressUpdates(updates) - return UpdateCompressor.concatUpdatesWithSameVersion(updates) + compressRawUpdates(lastPreviousUpdate, rawUpdates) { + if (__guard__(lastPreviousUpdate != null ? lastPreviousUpdate.op : undefined, x => x.length) > 1) { + // if the last previous update was an array op, don't compress onto it. + // The avoids cases where array length changes but version number doesn't + return [lastPreviousUpdate].concat(UpdateCompressor.compressRawUpdates(null,rawUpdates)); + } + if (lastPreviousUpdate != null) { + rawUpdates = [lastPreviousUpdate].concat(rawUpdates); + } + let updates = UpdateCompressor.convertToSingleOpUpdates(rawUpdates); + updates = UpdateCompressor.compressUpdates(updates); + return UpdateCompressor.concatUpdatesWithSameVersion(updates); + }, - compressUpdates: (updates) -> - return [] if updates.length == 0 + compressUpdates(updates) { + if (updates.length === 0) { return []; } - compressedUpdates = [updates.shift()] - for update in updates - lastCompressedUpdate = compressedUpdates.pop() - if lastCompressedUpdate? - compressedUpdates = compressedUpdates.concat UpdateCompressor._concatTwoUpdates lastCompressedUpdate, update - else - compressedUpdates.push update + let compressedUpdates = [updates.shift()]; + for (let update of Array.from(updates)) { + const lastCompressedUpdate = compressedUpdates.pop(); + if (lastCompressedUpdate != null) { + compressedUpdates = compressedUpdates.concat(UpdateCompressor._concatTwoUpdates(lastCompressedUpdate, update)); + } else { + compressedUpdates.push(update); + } + } - return compressedUpdates + return compressedUpdates; + }, - MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000 - MAX_UPDATE_SIZE: twoMegabytes = 2* 1024 * 1024 + MAX_TIME_BETWEEN_UPDATES: (oneMinute = 60 * 1000), + MAX_UPDATE_SIZE: (twoMegabytes = 2* 1024 * 1024), - _concatTwoUpdates: (firstUpdate, secondUpdate) -> - firstUpdate = - op: firstUpdate.op - meta: - user_id: firstUpdate.meta.user_id or null - start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts - end_ts: firstUpdate.meta.end_ts or firstUpdate.meta.ts + _concatTwoUpdates(firstUpdate, secondUpdate) { + let offset; + firstUpdate = { + op: firstUpdate.op, + meta: { + user_id: firstUpdate.meta.user_id || null, + start_ts: firstUpdate.meta.start_ts || firstUpdate.meta.ts, + end_ts: firstUpdate.meta.end_ts || firstUpdate.meta.ts + }, v: firstUpdate.v - secondUpdate = - op: secondUpdate.op - meta: - user_id: secondUpdate.meta.user_id or null - start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts - end_ts: secondUpdate.meta.end_ts or secondUpdate.meta.ts + }; + secondUpdate = { + op: secondUpdate.op, + meta: { + user_id: secondUpdate.meta.user_id || null, + start_ts: secondUpdate.meta.start_ts || secondUpdate.meta.ts, + end_ts: secondUpdate.meta.end_ts || secondUpdate.meta.ts + }, v: secondUpdate.v + }; - if firstUpdate.meta.user_id != secondUpdate.meta.user_id - return [firstUpdate, secondUpdate] + if (firstUpdate.meta.user_id !== secondUpdate.meta.user_id) { + return [firstUpdate, secondUpdate]; + } - if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > UpdateCompressor.MAX_TIME_BETWEEN_UPDATES - return [firstUpdate, secondUpdate] + if ((secondUpdate.meta.start_ts - firstUpdate.meta.end_ts) > UpdateCompressor.MAX_TIME_BETWEEN_UPDATES) { + return [firstUpdate, secondUpdate]; + } - firstOp = firstUpdate.op - secondOp = secondUpdate.op + const firstOp = firstUpdate.op; + const secondOp = secondUpdate.op; - firstSize = firstOp.i?.length or firstOp.d?.length - secondSize = secondOp.i?.length or secondOp.d?.length + const firstSize = (firstOp.i != null ? firstOp.i.length : undefined) || (firstOp.d != null ? firstOp.d.length : undefined); + const secondSize = (secondOp.i != null ? secondOp.i.length : undefined) || (secondOp.d != null ? secondOp.d.length : undefined); - # Two inserts - if firstOp.i? and secondOp.i? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) and firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE - return [ - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts + // Two inserts + if ((firstOp.i != null) && (secondOp.i != null) && (firstOp.p <= secondOp.p && secondOp.p <= (firstOp.p + firstOp.i.length)) && ((firstSize + secondSize) < UpdateCompressor.MAX_UPDATE_SIZE)) { + return [{ + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, user_id: firstUpdate.meta.user_id - op: - p: firstOp.p + }, + op: { + p: firstOp.p, i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) + }, v: secondUpdate.v - ] - # Two deletes - else if firstOp.d? and secondOp.d? and secondOp.p <= firstOp.p <= (secondOp.p + secondOp.d.length) and firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE - return [ - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts + } + ]; + // Two deletes + } else if ((firstOp.d != null) && (secondOp.d != null) && (secondOp.p <= firstOp.p && firstOp.p <= (secondOp.p + secondOp.d.length)) && ((firstSize + secondSize) < UpdateCompressor.MAX_UPDATE_SIZE)) { + return [{ + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, user_id: firstUpdate.meta.user_id - op: - p: secondOp.p + }, + op: { + p: secondOp.p, d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) + }, v: secondUpdate.v - ] - # An insert and then a delete - else if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length) - offset = secondOp.p - firstOp.p - insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) - # Only trim the insert when the delete is fully contained within in it - if insertedText == secondOp.d - insert = strRemove(firstOp.i, offset, secondOp.d.length) - return [ - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts + } + ]; + // An insert and then a delete + } else if ((firstOp.i != null) && (secondOp.d != null) && (firstOp.p <= secondOp.p && secondOp.p <= (firstOp.p + firstOp.i.length))) { + offset = secondOp.p - firstOp.p; + const insertedText = firstOp.i.slice(offset, offset + secondOp.d.length); + // Only trim the insert when the delete is fully contained within in it + if (insertedText === secondOp.d) { + const insert = strRemove(firstOp.i, offset, secondOp.d.length); + return [{ + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, user_id: firstUpdate.meta.user_id - op: - p: firstOp.p + }, + op: { + p: firstOp.p, i: insert + }, v: secondUpdate.v - ] - else - # This will only happen if the delete extends outside the insert - return [firstUpdate, secondUpdate] + } + ]; + } else { + // This will only happen if the delete extends outside the insert + return [firstUpdate, secondUpdate]; + } - # A delete then an insert at the same place, likely a copy-paste of a chunk of content - else if firstOp.d? and secondOp.i? and firstOp.p == secondOp.p - offset = firstOp.p - diff_ops = @diffAsShareJsOps(firstOp.d, secondOp.i) - if diff_ops.length == 0 - return [{ # Noop - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts + // A delete then an insert at the same place, likely a copy-paste of a chunk of content + } else if ((firstOp.d != null) && (secondOp.i != null) && (firstOp.p === secondOp.p)) { + offset = firstOp.p; + const diff_ops = this.diffAsShareJsOps(firstOp.d, secondOp.i); + if (diff_ops.length === 0) { + return [{ // Noop + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, user_id: firstUpdate.meta.user_id - op: - p: firstOp.p + }, + op: { + p: firstOp.p, i: "" + }, v: secondUpdate.v - }] - else - return diff_ops.map (op) -> - op.p += offset + }]; + } else { + return diff_ops.map(function(op) { + op.p += offset; return { - meta: - start_ts: firstUpdate.meta.start_ts - end_ts: secondUpdate.meta.end_ts + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, user_id: firstUpdate.meta.user_id - op: op + }, + op, v: secondUpdate.v - } + };}); + } - else - return [firstUpdate, secondUpdate] + } else { + return [firstUpdate, secondUpdate]; + } + }, - ADDED: 1 - REMOVED: -1 - UNCHANGED: 0 - diffAsShareJsOps: (before, after, callback = (error, ops) ->) -> - diffs = dmp.diff_main(before, after) - dmp.diff_cleanupSemantic(diffs) + ADDED: 1, + REMOVED: -1, + UNCHANGED: 0, + diffAsShareJsOps(before, after, callback) { + if (callback == null) { callback = function(error, ops) {}; } + const diffs = dmp.diff_main(before, after); + dmp.diff_cleanupSemantic(diffs); - ops = [] - position = 0 - for diff in diffs - type = diff[0] - content = diff[1] - if type == @ADDED - ops.push - i: content + const ops = []; + let position = 0; + for (let diff of Array.from(diffs)) { + const type = diff[0]; + const content = diff[1]; + if (type === this.ADDED) { + ops.push({ + i: content, p: position - position += content.length - else if type == @REMOVED - ops.push - d: content + }); + position += content.length; + } else if (type === this.REMOVED) { + ops.push({ + d: content, p: position - else if type == @UNCHANGED - position += content.length - else - throw "Unknown type" - return ops + }); + } else if (type === this.UNCHANGED) { + position += content.length; + } else { + throw "Unknown type"; + } + } + return ops; + } +}); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/app/coffee/UpdateTrimmer.js b/services/track-changes/app/coffee/UpdateTrimmer.js index c7464eb6c3..ab81e1b1f0 100644 --- a/services/track-changes/app/coffee/UpdateTrimmer.js +++ b/services/track-changes/app/coffee/UpdateTrimmer.js @@ -1,23 +1,44 @@ -MongoManager = require "./MongoManager" -WebApiManager = require "./WebApiManager" -logger = require "logger-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let UpdateTrimmer; +const MongoManager = require("./MongoManager"); +const WebApiManager = require("./WebApiManager"); +const logger = require("logger-sharelatex"); -module.exports = UpdateTrimmer = - shouldTrimUpdates: (project_id, callback = (error, shouldTrim) ->) -> - MongoManager.getProjectMetaData project_id, (error, metadata) -> - return callback(error) if error? - if metadata?.preserveHistory - return callback null, false - else - WebApiManager.getProjectDetails project_id, (error, details) -> - return callback(error) if error? - logger.log project_id: project_id, details: details, "got details" - if details?.features?.versioning - MongoManager.setProjectMetaData project_id, preserveHistory: true, (error) -> - return callback(error) if error? - MongoManager.upgradeHistory project_id, (error) -> - return callback(error) if error? - callback null, false - else - callback null, true +module.exports = (UpdateTrimmer = { + shouldTrimUpdates(project_id, callback) { + if (callback == null) { callback = function(error, shouldTrim) {}; } + return MongoManager.getProjectMetaData(project_id, function(error, metadata) { + if (error != null) { return callback(error); } + if (metadata != null ? metadata.preserveHistory : undefined) { + return callback(null, false); + } else { + return WebApiManager.getProjectDetails(project_id, function(error, details) { + if (error != null) { return callback(error); } + logger.log({project_id, details}, "got details"); + if (__guard__(details != null ? details.features : undefined, x => x.versioning)) { + return MongoManager.setProjectMetaData(project_id, {preserveHistory: true}, function(error) { + if (error != null) { return callback(error); } + return MongoManager.upgradeHistory(project_id, function(error) { + if (error != null) { return callback(error); } + return callback(null, false); + }); + }); + } else { + return callback(null, true); + } + }); + } + }); + } +}); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/app/coffee/UpdatesManager.js b/services/track-changes/app/coffee/UpdatesManager.js index 3a2cbf29c1..658eb5c7d7 100644 --- a/services/track-changes/app/coffee/UpdatesManager.js +++ b/services/track-changes/app/coffee/UpdatesManager.js @@ -1,344 +1,494 @@ -MongoManager = require "./MongoManager" -PackManager = require "./PackManager" -RedisManager = require "./RedisManager" -UpdateCompressor = require "./UpdateCompressor" -LockManager = require "./LockManager" -WebApiManager = require "./WebApiManager" -UpdateTrimmer = require "./UpdateTrimmer" -logger = require "logger-sharelatex" -async = require "async" -_ = require "underscore" -Settings = require "settings-sharelatex" -keys = Settings.redis.lock.key_schema +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let fiveMinutes, UpdatesManager; +const MongoManager = require("./MongoManager"); +const PackManager = require("./PackManager"); +const RedisManager = require("./RedisManager"); +const UpdateCompressor = require("./UpdateCompressor"); +const LockManager = require("./LockManager"); +const WebApiManager = require("./WebApiManager"); +const UpdateTrimmer = require("./UpdateTrimmer"); +const logger = require("logger-sharelatex"); +const async = require("async"); +const _ = require("underscore"); +const Settings = require("settings-sharelatex"); +const keys = Settings.redis.lock.key_schema; -module.exports = UpdatesManager = - compressAndSaveRawUpdates: (project_id, doc_id, rawUpdates, temporary, callback = (error) ->) -> - length = rawUpdates.length - if length == 0 - return callback() +module.exports = (UpdatesManager = { + compressAndSaveRawUpdates(project_id, doc_id, rawUpdates, temporary, callback) { + let i; + if (callback == null) { callback = function(error) {}; } + const { length } = rawUpdates; + if (length === 0) { + return callback(); + } - # check that ops are in the correct order - for op, i in rawUpdates when i > 0 - thisVersion = op?.v - prevVersion = rawUpdates[i-1]?.v - if not (prevVersion < thisVersion) - logger.error project_id: project_id, doc_id: doc_id, rawUpdates:rawUpdates, temporary: temporary, thisVersion:thisVersion, prevVersion:prevVersion, "op versions out of order" + // check that ops are in the correct order + for (i = 0; i < rawUpdates.length; i++) { + const op = rawUpdates[i]; + if (i > 0) { + const thisVersion = op != null ? op.v : undefined; + const prevVersion = __guard__(rawUpdates[i-1], x => x.v); + if (!(prevVersion < thisVersion)) { + logger.error({project_id, doc_id, rawUpdates, temporary, thisVersion, prevVersion}, "op versions out of order"); + } + } + } - # FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it - # CORRECTION: we do use it to log the time in case of error - MongoManager.peekLastCompressedUpdate doc_id, (error, lastCompressedUpdate, lastVersion) -> - # lastCompressedUpdate is the most recent update in Mongo, and - # lastVersion is its sharejs version number. - # - # The peekLastCompressedUpdate method may pass the update back - # as 'null' (for example if the previous compressed update has - # been archived). In this case it can still pass back the - # lastVersion from the update to allow us to check consistency. - return callback(error) if error? + // FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it + // CORRECTION: we do use it to log the time in case of error + return MongoManager.peekLastCompressedUpdate(doc_id, function(error, lastCompressedUpdate, lastVersion) { + // lastCompressedUpdate is the most recent update in Mongo, and + // lastVersion is its sharejs version number. + // + // The peekLastCompressedUpdate method may pass the update back + // as 'null' (for example if the previous compressed update has + // been archived). In this case it can still pass back the + // lastVersion from the update to allow us to check consistency. + let op; + if (error != null) { return callback(error); } - # Ensure that raw updates start where lastVersion left off - if lastVersion? - discardedUpdates = [] - rawUpdates = rawUpdates.slice(0) - while rawUpdates[0]? and rawUpdates[0].v <= lastVersion - discardedUpdates.push rawUpdates.shift() - if discardedUpdates.length - logger.error project_id: project_id, doc_id: doc_id, discardedUpdates: discardedUpdates, temporary: temporary, lastVersion: lastVersion, "discarded updates already present" + // Ensure that raw updates start where lastVersion left off + if (lastVersion != null) { + const discardedUpdates = []; + rawUpdates = rawUpdates.slice(0); + while ((rawUpdates[0] != null) && (rawUpdates[0].v <= lastVersion)) { + discardedUpdates.push(rawUpdates.shift()); + } + if (discardedUpdates.length) { + logger.error({project_id, doc_id, discardedUpdates, temporary, lastVersion}, "discarded updates already present"); + } - if rawUpdates[0]? and rawUpdates[0].v != lastVersion + 1 - ts = lastCompressedUpdate?.meta?.end_ts - last_timestamp = if ts? then new Date(ts) else 'unknown time' - error = new Error("Tried to apply raw op at version #{rawUpdates[0].v} to last compressed update with version #{lastVersion} from #{last_timestamp}") - logger.error err: error, doc_id: doc_id, project_id: project_id, prev_end_ts: ts, temporary: temporary, lastCompressedUpdate: lastCompressedUpdate, "inconsistent doc versions" - if Settings.trackchanges?.continueOnError and rawUpdates[0].v > lastVersion + 1 - # we have lost some ops - continue to write into the database, we can't recover at this point - lastCompressedUpdate = null - else - return callback error + if ((rawUpdates[0] != null) && (rawUpdates[0].v !== (lastVersion + 1))) { + const ts = __guard__(lastCompressedUpdate != null ? lastCompressedUpdate.meta : undefined, x1 => x1.end_ts); + const last_timestamp = (ts != null) ? new Date(ts) : 'unknown time'; + error = new Error(`Tried to apply raw op at version ${rawUpdates[0].v} to last compressed update with version ${lastVersion} from ${last_timestamp}`); + logger.error({err: error, doc_id, project_id, prev_end_ts: ts, temporary, lastCompressedUpdate}, "inconsistent doc versions"); + if ((Settings.trackchanges != null ? Settings.trackchanges.continueOnError : undefined) && (rawUpdates[0].v > (lastVersion + 1))) { + // we have lost some ops - continue to write into the database, we can't recover at this point + lastCompressedUpdate = null; + } else { + return callback(error); + } + } + } - if rawUpdates.length == 0 - return callback() + if (rawUpdates.length === 0) { + return callback(); + } - # some old large ops in redis need to be rejected, they predate - # the size limit that now prevents them going through the system - REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024 - for rawUpdate in rawUpdates - opSizes = ((op.i?.length || op.d?.length) for op in rawUpdate?.op or []) - size = _.max opSizes - if size > REJECT_LARGE_OP_SIZE - error = new Error("dropped op exceeding maximum allowed size of #{REJECT_LARGE_OP_SIZE}") - logger.error err: error, doc_id: doc_id, project_id: project_id, size: size, rawUpdate: rawUpdate, "dropped op - too big" - rawUpdate.op = [] + // some old large ops in redis need to be rejected, they predate + // the size limit that now prevents them going through the system + const REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024; + for (var rawUpdate of Array.from(rawUpdates)) { + const opSizes = ((() => { + const result = []; + for (op of Array.from((rawUpdate != null ? rawUpdate.op : undefined) || [])) { result.push(((op.i != null ? op.i.length : undefined) || (op.d != null ? op.d.length : undefined))); + } + return result; + })()); + const size = _.max(opSizes); + if (size > REJECT_LARGE_OP_SIZE) { + error = new Error(`dropped op exceeding maximum allowed size of ${REJECT_LARGE_OP_SIZE}`); + logger.error({err: error, doc_id, project_id, size, rawUpdate}, "dropped op - too big"); + rawUpdate.op = []; + } + } - compressedUpdates = UpdateCompressor.compressRawUpdates null, rawUpdates - PackManager.insertCompressedUpdates project_id, doc_id, lastCompressedUpdate, compressedUpdates, temporary, (error, result) -> - return callback(error) if error? - logger.log {project_id, doc_id, orig_v: lastCompressedUpdate?.v, new_v: result.v}, "inserted updates into pack" if result? - callback() + const compressedUpdates = UpdateCompressor.compressRawUpdates(null, rawUpdates); + return PackManager.insertCompressedUpdates(project_id, doc_id, lastCompressedUpdate, compressedUpdates, temporary, function(error, result) { + if (error != null) { return callback(error); } + if (result != null) { logger.log({project_id, doc_id, orig_v: (lastCompressedUpdate != null ? lastCompressedUpdate.v : undefined), new_v: result.v}, "inserted updates into pack"); } + return callback(); + }); + }); + }, - # Check whether the updates are temporary (per-project property) - _prepareProjectForUpdates: (project_id, callback = (error, temporary) ->) -> - UpdateTrimmer.shouldTrimUpdates project_id, (error, temporary) -> - return callback(error) if error? - callback(null, temporary) + // Check whether the updates are temporary (per-project property) + _prepareProjectForUpdates(project_id, callback) { + if (callback == null) { callback = function(error, temporary) {}; } + return UpdateTrimmer.shouldTrimUpdates(project_id, function(error, temporary) { + if (error != null) { return callback(error); } + return callback(null, temporary); + }); + }, - # Check for project id on document history (per-document property) - _prepareDocForUpdates: (project_id, doc_id, callback = (error) ->) -> - MongoManager.backportProjectId project_id, doc_id, (error) -> - return callback(error) if error? - callback(null) + // Check for project id on document history (per-document property) + _prepareDocForUpdates(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return MongoManager.backportProjectId(project_id, doc_id, function(error) { + if (error != null) { return callback(error); } + return callback(null); + }); + }, - # Apply updates for specific project/doc after preparing at project and doc level - REDIS_READ_BATCH_SIZE: 100 - processUncompressedUpdates: (project_id, doc_id, temporary, callback = (error) ->) -> - # get the updates as strings from redis (so we can delete them after they are applied) - RedisManager.getOldestDocUpdates doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, (error, docUpdates) -> - return callback(error) if error? - length = docUpdates.length - # parse the redis strings into ShareJs updates - RedisManager.expandDocUpdates docUpdates, (error, rawUpdates) -> - if error? - logger.err project_id: project_id, doc_id: doc_id, docUpdates: docUpdates, "failed to parse docUpdates" - return callback(error) - logger.log project_id: project_id, doc_id: doc_id, rawUpdates: rawUpdates, "retrieved raw updates from redis" - UpdatesManager.compressAndSaveRawUpdates project_id, doc_id, rawUpdates, temporary, (error) -> - return callback(error) if error? - logger.log project_id: project_id, doc_id: doc_id, "compressed and saved doc updates" - # delete the applied updates from redis - RedisManager.deleteAppliedDocUpdates project_id, doc_id, docUpdates, (error) -> - return callback(error) if error? - if length == UpdatesManager.REDIS_READ_BATCH_SIZE - # There might be more updates - logger.log project_id: project_id, doc_id: doc_id, "continuing processing updates" - setTimeout () -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, callback - , 0 - else - logger.log project_id: project_id, doc_id: doc_id, "all raw updates processed" - callback() + // Apply updates for specific project/doc after preparing at project and doc level + REDIS_READ_BATCH_SIZE: 100, + processUncompressedUpdates(project_id, doc_id, temporary, callback) { + // get the updates as strings from redis (so we can delete them after they are applied) + if (callback == null) { callback = function(error) {}; } + return RedisManager.getOldestDocUpdates(doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, function(error, docUpdates) { + if (error != null) { return callback(error); } + const { length } = docUpdates; + // parse the redis strings into ShareJs updates + return RedisManager.expandDocUpdates(docUpdates, function(error, rawUpdates) { + if (error != null) { + logger.err({project_id, doc_id, docUpdates}, "failed to parse docUpdates"); + return callback(error); + } + logger.log({project_id, doc_id, rawUpdates}, "retrieved raw updates from redis"); + return UpdatesManager.compressAndSaveRawUpdates(project_id, doc_id, rawUpdates, temporary, function(error) { + if (error != null) { return callback(error); } + logger.log({project_id, doc_id}, "compressed and saved doc updates"); + // delete the applied updates from redis + return RedisManager.deleteAppliedDocUpdates(project_id, doc_id, docUpdates, function(error) { + if (error != null) { return callback(error); } + if (length === UpdatesManager.REDIS_READ_BATCH_SIZE) { + // There might be more updates + logger.log({project_id, doc_id}, "continuing processing updates"); + return setTimeout(() => UpdatesManager.processUncompressedUpdates(project_id, doc_id, temporary, callback) + , 0); + } else { + logger.log({project_id, doc_id}, "all raw updates processed"); + return callback(); + } + }); + }); + }); + }); + }, - # Process updates for a doc when we flush it individually - processUncompressedUpdatesWithLock: (project_id, doc_id, callback = (error) ->) -> - UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> - return callback(error) if error? - UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, callback + // Process updates for a doc when we flush it individually + processUncompressedUpdatesWithLock(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return UpdatesManager._prepareProjectForUpdates(project_id, function(error, temporary) { + if (error != null) { return callback(error); } + return UpdatesManager._processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, callback); + }); + }, - # Process updates for a doc when the whole project is flushed (internal method) - _processUncompressedUpdatesForDocWithLock: (project_id, doc_id, temporary, callback = (error) ->) -> - UpdatesManager._prepareDocForUpdates project_id, doc_id, (error) -> - return callback(error) if error? - LockManager.runWithLock( + // Process updates for a doc when the whole project is flushed (internal method) + _processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, callback) { + if (callback == null) { callback = function(error) {}; } + return UpdatesManager._prepareDocForUpdates(project_id, doc_id, function(error) { + if (error != null) { return callback(error); } + return LockManager.runWithLock( keys.historyLock({doc_id}), - (releaseLock) -> - UpdatesManager.processUncompressedUpdates project_id, doc_id, temporary, releaseLock + releaseLock => UpdatesManager.processUncompressedUpdates(project_id, doc_id, temporary, releaseLock), callback - ) + ); + }); + }, - # Process all updates for a project, only check project-level information once - processUncompressedUpdatesForProject: (project_id, callback = (error) ->) -> - RedisManager.getDocIdsWithHistoryOps project_id, (error, doc_ids) -> - return callback(error) if error? - UpdatesManager._prepareProjectForUpdates project_id, (error, temporary) -> - jobs = [] - for doc_id in doc_ids - do (doc_id) -> - jobs.push (cb) -> - UpdatesManager._processUncompressedUpdatesForDocWithLock project_id, doc_id, temporary, cb - async.parallelLimit jobs, 5, callback + // Process all updates for a project, only check project-level information once + processUncompressedUpdatesForProject(project_id, callback) { + if (callback == null) { callback = function(error) {}; } + return RedisManager.getDocIdsWithHistoryOps(project_id, function(error, doc_ids) { + if (error != null) { return callback(error); } + return UpdatesManager._prepareProjectForUpdates(project_id, function(error, temporary) { + const jobs = []; + for (let doc_id of Array.from(doc_ids)) { + (doc_id => + jobs.push(cb => UpdatesManager._processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, cb)) + )(doc_id); + } + return async.parallelLimit(jobs, 5, callback); + }); + }); + }, - # flush all outstanding changes - flushAll: (limit, callback = (error, result) ->) -> - RedisManager.getProjectIdsWithHistoryOps (error, project_ids) -> - return callback(error) if error? - logger.log {count: project_ids?.length, project_ids: project_ids}, "found projects" - jobs = [] - project_ids = _.shuffle project_ids # randomise to avoid hitting same projects each time - selectedProjects = if limit < 0 then project_ids else project_ids[0...limit] - for project_id in selectedProjects - do (project_id) -> - jobs.push (cb) -> - UpdatesManager.processUncompressedUpdatesForProject project_id, (err) -> - return cb(null, {failed: err?, project_id: project_id}) - async.series jobs, (error, result) -> - return callback(error) if error? - failedProjects = (x.project_id for x in result when x.failed) - succeededProjects = (x.project_id for x in result when not x.failed) - callback(null, {failed: failedProjects, succeeded: succeededProjects, all: project_ids}) + // flush all outstanding changes + flushAll(limit, callback) { + if (callback == null) { callback = function(error, result) {}; } + return RedisManager.getProjectIdsWithHistoryOps(function(error, project_ids) { + let project_id; + if (error != null) { return callback(error); } + logger.log({count: (project_ids != null ? project_ids.length : undefined), project_ids}, "found projects"); + const jobs = []; + project_ids = _.shuffle(project_ids); // randomise to avoid hitting same projects each time + const selectedProjects = limit < 0 ? project_ids : project_ids.slice(0, limit); + for (project_id of Array.from(selectedProjects)) { + (project_id => + jobs.push(cb => + UpdatesManager.processUncompressedUpdatesForProject(project_id, err => cb(null, {failed: (err != null), project_id})) + ) + )(project_id); + } + return async.series(jobs, function(error, result) { + let x; + if (error != null) { return callback(error); } + const failedProjects = ((() => { + const result1 = []; + for (x of Array.from(result)) { if (x.failed) { + result1.push(x.project_id); + } + } + return result1; + })()); + const succeededProjects = ((() => { + const result2 = []; + for (x of Array.from(result)) { if (!x.failed) { + result2.push(x.project_id); + } + } + return result2; + })()); + return callback(null, {failed: failedProjects, succeeded: succeededProjects, all: project_ids}); + }); + }); + }, - getDanglingUpdates: (callback = (error, doc_ids) ->) -> - RedisManager.getAllDocIdsWithHistoryOps (error, all_doc_ids) -> - return callback(error) if error? - RedisManager.getProjectIdsWithHistoryOps (error, all_project_ids) -> - return callback(error) if error? - # function to get doc_ids for each project - task = (cb) -> async.concatSeries all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb - # find the dangling doc ids - task (error, project_doc_ids) -> - dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids) - logger.log {all_doc_ids: all_doc_ids, all_project_ids: all_project_ids, project_doc_ids: project_doc_ids, dangling_doc_ids: dangling_doc_ids}, "checking for dangling doc ids" - callback(null, dangling_doc_ids) + getDanglingUpdates(callback) { + if (callback == null) { callback = function(error, doc_ids) {}; } + return RedisManager.getAllDocIdsWithHistoryOps(function(error, all_doc_ids) { + if (error != null) { return callback(error); } + return RedisManager.getProjectIdsWithHistoryOps(function(error, all_project_ids) { + if (error != null) { return callback(error); } + // function to get doc_ids for each project + const task = cb => async.concatSeries(all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb); + // find the dangling doc ids + return task(function(error, project_doc_ids) { + const dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids); + logger.log({all_doc_ids, all_project_ids, project_doc_ids, dangling_doc_ids}, "checking for dangling doc ids"); + return callback(null, dangling_doc_ids); + }); + }); + }); + }, - getDocUpdates: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.processUncompressedUpdatesWithLock project_id, doc_id, (error) -> - return callback(error) if error? - #console.log "options", options - PackManager.getOpsByVersionRange project_id, doc_id, options.from, options.to, (error, updates) -> - return callback(error) if error? - callback null, updates + getDocUpdates(project_id, doc_id, options, callback) { + if (options == null) { options = {}; } + if (callback == null) { callback = function(error, updates) {}; } + return UpdatesManager.processUncompressedUpdatesWithLock(project_id, doc_id, function(error) { + if (error != null) { return callback(error); } + //console.log "options", options + return PackManager.getOpsByVersionRange(project_id, doc_id, options.from, options.to, function(error, updates) { + if (error != null) { return callback(error); } + return callback(null, updates); + }); + }); + }, - getDocUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) -> - UpdatesManager.getDocUpdates project_id, doc_id, options, (error, updates) -> - return callback(error) if error? - UpdatesManager.fillUserInfo updates, (error, updates) -> - return callback(error) if error? - callback null, updates + getDocUpdatesWithUserInfo(project_id, doc_id, options, callback) { + if (options == null) { options = {}; } + if (callback == null) { callback = function(error, updates) {}; } + return UpdatesManager.getDocUpdates(project_id, doc_id, options, function(error, updates) { + if (error != null) { return callback(error); } + return UpdatesManager.fillUserInfo(updates, function(error, updates) { + if (error != null) { return callback(error); } + return callback(null, updates); + }); + }); + }, - getSummarizedProjectUpdates: (project_id, options = {}, callback = (error, updates) ->) -> - options.min_count ||= 25 - summarizedUpdates = [] - before = options.before - nextBeforeTimestamp = null - UpdatesManager.processUncompressedUpdatesForProject project_id, (error) -> - return callback(error) if error? - PackManager.makeProjectIterator project_id, before, (err, iterator) -> - return callback(err) if err? - # repeatedly get updates and pass them through the summariser to get an final output with user info - async.whilst () -> - #console.log "checking iterator.done", iterator.done() - return summarizedUpdates.length < options.min_count and not iterator.done() - , (cb) -> - iterator.next (err, partialUpdates) -> - return callback(err) if err? - #logger.log {partialUpdates}, 'got partialUpdates' - return cb() if partialUpdates.length is 0 ## FIXME should try to avoid this happening - nextBeforeTimestamp = partialUpdates[partialUpdates.length - 1].meta.end_ts - # add the updates to the summary list - summarizedUpdates = UpdatesManager._summarizeUpdates partialUpdates, summarizedUpdates - cb() - , () -> - # finally done all updates - #console.log 'summarized Updates', summarizedUpdates - UpdatesManager.fillSummarizedUserInfo summarizedUpdates, (err, results) -> - return callback(err) if err? - callback null, results, if not iterator.done() then nextBeforeTimestamp else undefined + getSummarizedProjectUpdates(project_id, options, callback) { + if (options == null) { options = {}; } + if (callback == null) { callback = function(error, updates) {}; } + if (!options.min_count) { options.min_count = 25; } + let summarizedUpdates = []; + const { before } = options; + let nextBeforeTimestamp = null; + return UpdatesManager.processUncompressedUpdatesForProject(project_id, function(error) { + if (error != null) { return callback(error); } + return PackManager.makeProjectIterator(project_id, before, function(err, iterator) { + if (err != null) { return callback(err); } + // repeatedly get updates and pass them through the summariser to get an final output with user info + return async.whilst(() => + //console.log "checking iterator.done", iterator.done() + (summarizedUpdates.length < options.min_count) && !iterator.done() + + , cb => + iterator.next(function(err, partialUpdates) { + if (err != null) { return callback(err); } + //logger.log {partialUpdates}, 'got partialUpdates' + if (partialUpdates.length === 0) { return cb(); } //# FIXME should try to avoid this happening + nextBeforeTimestamp = partialUpdates[partialUpdates.length - 1].meta.end_ts; + // add the updates to the summary list + summarizedUpdates = UpdatesManager._summarizeUpdates(partialUpdates, summarizedUpdates); + return cb(); + }) + + , () => + // finally done all updates + //console.log 'summarized Updates', summarizedUpdates + UpdatesManager.fillSummarizedUserInfo(summarizedUpdates, function(err, results) { + if (err != null) { return callback(err); } + return callback(null, results, !iterator.done() ? nextBeforeTimestamp : undefined); + }) + ); + }); + }); + }, - fetchUserInfo: (users, callback = (error, fetchedUserInfo) ->) -> - jobs = [] - fetchedUserInfo = {} - for user_id of users - do (user_id) -> - jobs.push (callback) -> - WebApiManager.getUserInfo user_id, (error, userInfo) -> - return callback(error) if error? - fetchedUserInfo[user_id] = userInfo - callback() + fetchUserInfo(users, callback) { + if (callback == null) { callback = function(error, fetchedUserInfo) {}; } + const jobs = []; + const fetchedUserInfo = {}; + for (let user_id in users) { + (user_id => + jobs.push(callback => + WebApiManager.getUserInfo(user_id, function(error, userInfo) { + if (error != null) { return callback(error); } + fetchedUserInfo[user_id] = userInfo; + return callback(); + }) + ) + )(user_id); + } - async.series jobs, (err) -> - return callback(err) if err? - callback(null, fetchedUserInfo) + return async.series(jobs, function(err) { + if (err != null) { return callback(err); } + return callback(null, fetchedUserInfo); + }); + }, - fillUserInfo: (updates, callback = (error, updates) ->) -> - users = {} - for update in updates - user_id = update.meta.user_id - if UpdatesManager._validUserId(user_id) - users[user_id] = true + fillUserInfo(updates, callback) { + let update, user_id; + if (callback == null) { callback = function(error, updates) {}; } + const users = {}; + for (update of Array.from(updates)) { + ({ user_id } = update.meta); + if (UpdatesManager._validUserId(user_id)) { + users[user_id] = true; + } + } - UpdatesManager.fetchUserInfo users, (error, fetchedUserInfo) -> - return callback(error) if error? - for update in updates - user_id = update.meta.user_id - delete update.meta.user_id - if UpdatesManager._validUserId(user_id) - update.meta.user = fetchedUserInfo[user_id] - callback null, updates + return UpdatesManager.fetchUserInfo(users, function(error, fetchedUserInfo) { + if (error != null) { return callback(error); } + for (update of Array.from(updates)) { + ({ user_id } = update.meta); + delete update.meta.user_id; + if (UpdatesManager._validUserId(user_id)) { + update.meta.user = fetchedUserInfo[user_id]; + } + } + return callback(null, updates); + }); + }, - fillSummarizedUserInfo: (updates, callback = (error, updates) ->) -> - users = {} - for update in updates - user_ids = update.meta.user_ids or [] - for user_id in user_ids - if UpdatesManager._validUserId(user_id) - users[user_id] = true + fillSummarizedUserInfo(updates, callback) { + let update, user_id, user_ids; + if (callback == null) { callback = function(error, updates) {}; } + const users = {}; + for (update of Array.from(updates)) { + user_ids = update.meta.user_ids || []; + for (user_id of Array.from(user_ids)) { + if (UpdatesManager._validUserId(user_id)) { + users[user_id] = true; + } + } + } - UpdatesManager.fetchUserInfo users, (error, fetchedUserInfo) -> - return callback(error) if error? - for update in updates - user_ids = update.meta.user_ids or [] - update.meta.users = [] - delete update.meta.user_ids - for user_id in user_ids - if UpdatesManager._validUserId(user_id) - update.meta.users.push fetchedUserInfo[user_id] - else - update.meta.users.push null - callback null, updates + return UpdatesManager.fetchUserInfo(users, function(error, fetchedUserInfo) { + if (error != null) { return callback(error); } + for (update of Array.from(updates)) { + user_ids = update.meta.user_ids || []; + update.meta.users = []; + delete update.meta.user_ids; + for (user_id of Array.from(user_ids)) { + if (UpdatesManager._validUserId(user_id)) { + update.meta.users.push(fetchedUserInfo[user_id]); + } else { + update.meta.users.push(null); + } + } + } + return callback(null, updates); + }); + }, - _validUserId: (user_id) -> - if !user_id? - return false - else - return !!user_id.match(/^[a-f0-9]{24}$/) + _validUserId(user_id) { + if ((user_id == null)) { + return false; + } else { + return !!user_id.match(/^[a-f0-9]{24}$/); + } + }, - TIME_BETWEEN_DISTINCT_UPDATES: fiveMinutes = 5 * 60 * 1000 - SPLIT_ON_DELETE_SIZE: 16 # characters - _summarizeUpdates: (updates, existingSummarizedUpdates = []) -> - summarizedUpdates = existingSummarizedUpdates.slice() - previousUpdateWasBigDelete = false - for update in updates - earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] - shouldConcat = false + TIME_BETWEEN_DISTINCT_UPDATES: (fiveMinutes = 5 * 60 * 1000), + SPLIT_ON_DELETE_SIZE: 16, // characters + _summarizeUpdates(updates, existingSummarizedUpdates) { + if (existingSummarizedUpdates == null) { existingSummarizedUpdates = []; } + const summarizedUpdates = existingSummarizedUpdates.slice(); + let previousUpdateWasBigDelete = false; + for (let update of Array.from(updates)) { + var doc_id; + const earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1]; + let shouldConcat = false; - # If a user inserts some text, then deletes a big chunk including that text, - # the update we show might concat the insert and delete, and there will be no sign - # of that insert having happened, or be able to restore to it (restoring after a big delete is common). - # So, we split the summary on 'big' deletes. However, we've stepping backwards in time with - # most recent changes considered first, so if this update is a big delete, we want to start - # a new summarized update next timge, hence we monitor the previous update. - if previousUpdateWasBigDelete - shouldConcat = false - else if earliestUpdate and earliestUpdate.meta.end_ts - update.meta.start_ts < @TIME_BETWEEN_DISTINCT_UPDATES - # We're going backwards in time through the updates, so only combine if this update starts less than 5 minutes before - # the end of current summarized block, so no block spans more than 5 minutes. - shouldConcat = true + // If a user inserts some text, then deletes a big chunk including that text, + // the update we show might concat the insert and delete, and there will be no sign + // of that insert having happened, or be able to restore to it (restoring after a big delete is common). + // So, we split the summary on 'big' deletes. However, we've stepping backwards in time with + // most recent changes considered first, so if this update is a big delete, we want to start + // a new summarized update next timge, hence we monitor the previous update. + if (previousUpdateWasBigDelete) { + shouldConcat = false; + } else if (earliestUpdate && ((earliestUpdate.meta.end_ts - update.meta.start_ts) < this.TIME_BETWEEN_DISTINCT_UPDATES)) { + // We're going backwards in time through the updates, so only combine if this update starts less than 5 minutes before + // the end of current summarized block, so no block spans more than 5 minutes. + shouldConcat = true; + } - isBigDelete = false - for op in update.op or [] - if op.d? and op.d.length > @SPLIT_ON_DELETE_SIZE - isBigDelete = true + let isBigDelete = false; + for (let op of Array.from(update.op || [])) { + if ((op.d != null) && (op.d.length > this.SPLIT_ON_DELETE_SIZE)) { + isBigDelete = true; + } + } - previousUpdateWasBigDelete = isBigDelete + previousUpdateWasBigDelete = isBigDelete; - if shouldConcat - # check if the user in this update is already present in the earliest update, - # if not, add them to the users list of the earliest update - earliestUpdate.meta.user_ids = _.union earliestUpdate.meta.user_ids, [update.meta.user_id] + if (shouldConcat) { + // check if the user in this update is already present in the earliest update, + // if not, add them to the users list of the earliest update + earliestUpdate.meta.user_ids = _.union(earliestUpdate.meta.user_ids, [update.meta.user_id]); - doc_id = update.doc_id.toString() - doc = earliestUpdate.docs[doc_id] - if doc? - doc.fromV = Math.min(doc.fromV, update.v) - doc.toV = Math.max(doc.toV, update.v) - else - earliestUpdate.docs[doc_id] = - fromV: update.v + doc_id = update.doc_id.toString(); + const doc = earliestUpdate.docs[doc_id]; + if (doc != null) { + doc.fromV = Math.min(doc.fromV, update.v); + doc.toV = Math.max(doc.toV, update.v); + } else { + earliestUpdate.docs[doc_id] = { + fromV: update.v, toV: update.v + }; + } - earliestUpdate.meta.start_ts = Math.min(earliestUpdate.meta.start_ts, update.meta.start_ts) - earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts) - else - newUpdate = - meta: - user_ids: [] - start_ts: update.meta.start_ts + earliestUpdate.meta.start_ts = Math.min(earliestUpdate.meta.start_ts, update.meta.start_ts); + earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts); + } else { + const newUpdate = { + meta: { + user_ids: [], + start_ts: update.meta.start_ts, end_ts: update.meta.end_ts + }, docs: {} + }; - newUpdate.docs[update.doc_id.toString()] = - fromV: update.v + newUpdate.docs[update.doc_id.toString()] = { + fromV: update.v, toV: update.v - newUpdate.meta.user_ids.push update.meta.user_id - summarizedUpdates.push newUpdate + }; + newUpdate.meta.user_ids.push(update.meta.user_id); + summarizedUpdates.push(newUpdate); + } + } - return summarizedUpdates + return summarizedUpdates; + } +}); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/app/coffee/WebApiManager.js b/services/track-changes/app/coffee/WebApiManager.js index aee8d431dd..041b3170ad 100644 --- a/services/track-changes/app/coffee/WebApiManager.js +++ b/services/track-changes/app/coffee/WebApiManager.js @@ -1,69 +1,99 @@ -request = require "requestretry" # allow retry on error https://github.com/FGRibreau/node-request-retry -logger = require "logger-sharelatex" -Settings = require "settings-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let WebApiManager; +const request = require("requestretry"); // allow retry on error https://github.com/FGRibreau/node-request-retry +const logger = require("logger-sharelatex"); +const Settings = require("settings-sharelatex"); -# Don't let HTTP calls hang for a long time -MAX_HTTP_REQUEST_LENGTH = 15000 # 15 seconds +// Don't let HTTP calls hang for a long time +const MAX_HTTP_REQUEST_LENGTH = 15000; // 15 seconds -# DEPRECATED! This method of getting user details via track-changes is deprecated -# in the way we lay out our services. -# Instead, web should be responsible for collecting the raw data (user_ids) and -# filling it out with calls to other services. All API calls should create a -# tree-like structure as much as possible, with web as the root. -module.exports = WebApiManager = - sendRequest: (url, callback = (error, body) ->) -> - request.get { - url: "#{Settings.apis.web.url}#{url}" - timeout: MAX_HTTP_REQUEST_LENGTH - maxAttempts: 2 # for node-request-retry - auth: - user: Settings.apis.web.user - pass: Settings.apis.web.pass +// DEPRECATED! This method of getting user details via track-changes is deprecated +// in the way we lay out our services. +// Instead, web should be responsible for collecting the raw data (user_ids) and +// filling it out with calls to other services. All API calls should create a +// tree-like structure as much as possible, with web as the root. +module.exports = (WebApiManager = { + sendRequest(url, callback) { + if (callback == null) { callback = function(error, body) {}; } + return request.get({ + url: `${Settings.apis.web.url}${url}`, + timeout: MAX_HTTP_REQUEST_LENGTH, + maxAttempts: 2, // for node-request-retry + auth: { + user: Settings.apis.web.user, + pass: Settings.apis.web.pass, sendImmediately: true - }, (error, res, body)-> - if error? - return callback(error) - if res.statusCode == 404 - logger.log url: url, "got 404 from web api" - return callback null, null - if res.statusCode >= 200 and res.statusCode < 300 - return callback null, body - else - error = new Error("web returned a non-success status code: #{res.statusCode} (attempts: #{res.attempts})") - callback error + } + }, function(error, res, body){ + if (error != null) { + return callback(error); + } + if (res.statusCode === 404) { + logger.log({url}, "got 404 from web api"); + return callback(null, null); + } + if ((res.statusCode >= 200) && (res.statusCode < 300)) { + return callback(null, body); + } else { + error = new Error(`web returned a non-success status code: ${res.statusCode} (attempts: ${res.attempts})`); + return callback(error); + } + }); + }, - getUserInfo: (user_id, callback = (error, userInfo) ->) -> - url = "/user/#{user_id}/personal_info" - logger.log user_id: user_id, "getting user info from web" - WebApiManager.sendRequest url, (error, body) -> - if error? - logger.error err: error, user_id: user_id, url: url, "error accessing web" - return callback error - - if body == null - logger.error user_id: user_id, url: url, "no user found" - return callback null, null - try - user = JSON.parse(body) - catch error - return callback(error) - callback null, { - id: user.id - email: user.email - first_name: user.first_name - last_name: user.last_name + getUserInfo(user_id, callback) { + if (callback == null) { callback = function(error, userInfo) {}; } + const url = `/user/${user_id}/personal_info`; + logger.log({user_id}, "getting user info from web"); + return WebApiManager.sendRequest(url, function(error, body) { + let user; + if (error != null) { + logger.error({err: error, user_id, url}, "error accessing web"); + return callback(error); } - getProjectDetails: (project_id, callback = (error, details) ->) -> - url = "/project/#{project_id}/details" - logger.log project_id: project_id, "getting project details from web" - WebApiManager.sendRequest url, (error, body) -> - if error? - logger.error err: error, project_id: project_id, url: url, "error accessing web" - return callback error + if (body === null) { + logger.error({user_id, url}, "no user found"); + return callback(null, null); + } + try { + user = JSON.parse(body); + } catch (error1) { + error = error1; + return callback(error); + } + return callback(null, { + id: user.id, + email: user.email, + first_name: user.first_name, + last_name: user.last_name + }); + }); + }, - try - project = JSON.parse(body) - catch error - return callback(error) - callback null, project + getProjectDetails(project_id, callback) { + if (callback == null) { callback = function(error, details) {}; } + const url = `/project/${project_id}/details`; + logger.log({project_id}, "getting project details from web"); + return WebApiManager.sendRequest(url, function(error, body) { + let project; + if (error != null) { + logger.error({err: error, project_id, url}, "error accessing web"); + return callback(error); + } + + try { + project = JSON.parse(body); + } catch (error1) { + error = error1; + return callback(error); + } + return callback(null, project); + }); + } +}); diff --git a/services/track-changes/app/coffee/mongojs.js b/services/track-changes/app/coffee/mongojs.js index 87867330bb..2fb698bd9f 100644 --- a/services/track-changes/app/coffee/mongojs.js +++ b/services/track-changes/app/coffee/mongojs.js @@ -1,9 +1,10 @@ -Settings = require "settings-sharelatex" -mongojs = require "mongojs" -bson = require "bson" -db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryIndex"]) -module.exports = - db: db - ObjectId: mongojs.ObjectId +const Settings = require("settings-sharelatex"); +const mongojs = require("mongojs"); +const bson = require("bson"); +const db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryIndex"]); +module.exports = { + db, + ObjectId: mongojs.ObjectId, BSON: new bson.BSONPure() +}; From 65ecd31c3ae17b65077a3535267a7d13c0b65a2e Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:34:21 +0100 Subject: [PATCH 467/549] decaffeinate: Run post-processing cleanups on DiffGenerator.coffee and 17 other files --- .../track-changes/app/coffee/DiffGenerator.js | 15 +++++++--- .../track-changes/app/coffee/DiffManager.js | 10 +++++-- .../app/coffee/DocumentUpdaterManager.js | 7 +++++ .../track-changes/app/coffee/HealthChecker.js | 6 ++++ .../app/coffee/HttpController.js | 11 +++++-- .../track-changes/app/coffee/LockManager.js | 5 ++++ services/track-changes/app/coffee/MongoAWS.js | 10 ++++++- .../track-changes/app/coffee/MongoManager.js | 7 +++++ .../track-changes/app/coffee/PackManager.js | 29 ++++++++++++------- .../track-changes/app/coffee/PackWorker.js | 8 ++++- .../app/coffee/ProjectIterator.js | 28 +++++++++++------- .../track-changes/app/coffee/RedisManager.js | 12 ++++++-- .../app/coffee/RestoreManager.js | 7 +++++ .../app/coffee/UpdateCompressor.js | 19 ++++++++---- .../track-changes/app/coffee/UpdateTrimmer.js | 7 +++++ .../app/coffee/UpdatesManager.js | 25 ++++++++++------ .../track-changes/app/coffee/WebApiManager.js | 6 ++++ services/track-changes/app/coffee/mongojs.js | 2 ++ 18 files changed, 165 insertions(+), 49 deletions(-) diff --git a/services/track-changes/app/coffee/DiffGenerator.js b/services/track-changes/app/coffee/DiffGenerator.js index c23d3c19c3..4a815f6c3b 100644 --- a/services/track-changes/app/coffee/DiffGenerator.js +++ b/services/track-changes/app/coffee/DiffGenerator.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + no-proto, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -73,7 +80,7 @@ module.exports = (DiffGenerator = { }, rewindUpdates(content, updates) { - for (let update of Array.from(updates.reverse())) { + for (const update of Array.from(updates.reverse())) { try { content = DiffGenerator.rewindUpdate(content, update); } catch (e) { @@ -86,7 +93,7 @@ module.exports = (DiffGenerator = { buildDiff(initialContent, updates) { let diff = [ {u: initialContent} ]; - for (let update of Array.from(updates)) { + for (const update of Array.from(updates)) { diff = DiffGenerator.applyUpdateToDiff(diff, update); } diff = DiffGenerator.compressDiff(diff); @@ -95,7 +102,7 @@ module.exports = (DiffGenerator = { compressDiff(diff) { const newDiff = []; - for (let part of Array.from(diff)) { + for (const part of Array.from(diff)) { const lastPart = newDiff[newDiff.length - 1]; if ((lastPart != null) && ((lastPart.meta != null ? lastPart.meta.user : undefined) != null) && ((part.meta != null ? part.meta.user : undefined) != null)) { if ((lastPart.i != null) && (part.i != null) && (lastPart.meta.user.id === part.meta.user.id)) { @@ -140,7 +147,7 @@ module.exports = (DiffGenerator = { }, applyUpdateToDiff(diff, update) { - for (let op of Array.from(update.op)) { + for (const op of Array.from(update.op)) { if (op.broken !== true) { diff = DiffGenerator.applyOpToDiff(diff, op, update.meta); } diff --git a/services/track-changes/app/coffee/DiffManager.js b/services/track-changes/app/coffee/DiffManager.js index bfd28400a3..cc20de3651 100644 --- a/services/track-changes/app/coffee/DiffManager.js +++ b/services/track-changes/app/coffee/DiffManager.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -41,7 +47,7 @@ module.exports = (DiffManager = { } const updatesToApply = []; - for (let update of Array.from(updates.slice().reverse())) { + for (const update of Array.from(updates.slice().reverse())) { if (update.v <= toVersion) { updatesToApply.push(update); } @@ -93,7 +99,7 @@ module.exports = (DiffManager = { if (error != null) { return callback(error); } // bail out if we hit a broken update - for (let u of Array.from(updates)) { + for (const u of Array.from(updates)) { if (u.broken) { return callback(new Error("broken-history")); } diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.js b/services/track-changes/app/coffee/DocumentUpdaterManager.js index 9c0cd66067..6ee516f333 100644 --- a/services/track-changes/app/coffee/DocumentUpdaterManager.js +++ b/services/track-changes/app/coffee/DocumentUpdaterManager.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/HealthChecker.js b/services/track-changes/app/coffee/HealthChecker.js index ae008adfe6..68e18b5f13 100644 --- a/services/track-changes/app/coffee/HealthChecker.js +++ b/services/track-changes/app/coffee/HealthChecker.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + standard/no-callback-literal, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/HttpController.js b/services/track-changes/app/coffee/HttpController.js index 1526c0ce4c..6c3178a063 100644 --- a/services/track-changes/app/coffee/HttpController.js +++ b/services/track-changes/app/coffee/HttpController.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -78,8 +85,8 @@ module.exports = (HttpController = { return DiffManager.getDocumentBeforeVersion(project_id, doc_id, 1, function(error, document, rewoundUpdates) { if (error != null) { return next(error); } const broken = []; - for (let update of Array.from(rewoundUpdates)) { - for (let op of Array.from(update.op)) { + for (const update of Array.from(rewoundUpdates)) { + for (const op of Array.from(update.op)) { if (op.broken === true) { broken.push(op); } diff --git a/services/track-changes/app/coffee/LockManager.js b/services/track-changes/app/coffee/LockManager.js index 51770dbb32..a0e36d6509 100644 --- a/services/track-changes/app/coffee/LockManager.js +++ b/services/track-changes/app/coffee/LockManager.js @@ -1,3 +1,8 @@ +/* eslint-disable + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/MongoAWS.js b/services/track-changes/app/coffee/MongoAWS.js index 5abd255322..3f6326a7e4 100644 --- a/services/track-changes/app/coffee/MongoAWS.js +++ b/services/track-changes/app/coffee/MongoAWS.js @@ -1,3 +1,11 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -119,7 +127,7 @@ module.exports = (MongoAWS = { object._id = ObjectId(object._id); object.doc_id = ObjectId(object.doc_id); object.project_id = ObjectId(object.project_id); - for (let op of Array.from(object.pack)) { + for (const op of Array.from(object.pack)) { if (op._id != null) { op._id = ObjectId(op._id); } } return callback(null, object); diff --git a/services/track-changes/app/coffee/MongoManager.js b/services/track-changes/app/coffee/MongoManager.js index 3828e9ad80..5edbe6b6e3 100644 --- a/services/track-changes/app/coffee/MongoManager.js +++ b/services/track-changes/app/coffee/MongoManager.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/PackManager.js b/services/track-changes/app/coffee/PackManager.js index d1359b470d..1446c3421d 100644 --- a/services/track-changes/app/coffee/PackManager.js +++ b/services/track-changes/app/coffee/PackManager.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -191,23 +198,23 @@ module.exports = (PackManager = { const query = {doc_id:ObjectId(doc_id.toString())}; if (toVersion != null) { query.v = {$lte:toVersion}; } if (fromVersion != null) { query.v_end = {$gte:fromVersion}; } - //console.log "query:", query + // console.log "query:", query return db.docHistory.find(query).sort({v:-1}, function(err, result) { if (err != null) { return callback(err); } - //console.log "getOpsByVersionRange:", err, result + // console.log "getOpsByVersionRange:", err, result const updates = []; const opInRange = function(op, from, to) { if ((fromVersion != null) && (op.v < fromVersion)) { return false; } if ((toVersion != null) && (op.v > toVersion)) { return false; } return true; }; - for (let docHistory of Array.from(result)) { - //console.log 'adding', docHistory.pack - for (let op of Array.from(docHistory.pack.reverse())) { + for (const docHistory of Array.from(result)) { + // console.log 'adding', docHistory.pack + for (const op of Array.from(docHistory.pack.reverse())) { if (opInRange(op, fromVersion, toVersion)) { op.project_id = docHistory.project_id; op.doc_id = docHistory.doc_id; - //console.log "added op", op.v, fromVersion, toVersion + // console.log "added op", op.v, fromVersion, toVersion updates.push(op); } } @@ -286,7 +293,7 @@ module.exports = (PackManager = { } return db.docHistoryIndex.find({project_id: ObjectId(project_id)}, function(err, indexes) { if (err != null) { return callback(err); } - for (let index of Array.from(indexes)) { + for (const index of Array.from(indexes)) { for (pack of Array.from(index.packs)) { if (!seenIds[pack._id]) { pack.project_id = index.project_id; @@ -353,7 +360,7 @@ module.exports = (PackManager = { return PackManager.getIndex(doc_id, function(err, index) { if (err != null) { return callback(err); } if ((index == null)) { return callback(); } - for (let pack of Array.from((index != null ? index.packs : undefined) || [])) { + for (const pack of Array.from((index != null ? index.packs : undefined) || [])) { index[pack._id] = pack; } return callback(null, index); @@ -362,7 +369,7 @@ module.exports = (PackManager = { initialiseIndex(project_id, doc_id, callback) { return PackManager.findCompletedPacks(project_id, doc_id, function(err, packs) { - //console.log 'err', err, 'packs', packs, packs?.length + // console.log 'err', err, 'packs', packs, packs?.length if (err != null) { return callback(err); } if ((packs == null)) { return callback(); } return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, packs, callback); @@ -492,7 +499,7 @@ module.exports = (PackManager = { delete result.last_checked; delete pack.last_checked; // need to compare ids as ObjectIds with .equals() - for (let key of ['_id', 'project_id', 'doc_id']) { + for (const key of ['_id', 'project_id', 'doc_id']) { if (result[key].equals(pack[key])) { result[key] = pack[key]; } } for (let i = 0; i < result.pack.length; i++) { @@ -621,7 +628,7 @@ module.exports = (PackManager = { const indexPacks = (indexResult != null ? indexResult.packs : undefined) || []; const unArchivedPacks = ((() => { const result = []; - for (let pack of Array.from(indexPacks)) { if ((pack.inS3 == null)) { + for (const pack of Array.from(indexPacks)) { if ((pack.inS3 == null)) { result.push(pack); } } diff --git a/services/track-changes/app/coffee/PackWorker.js b/services/track-changes/app/coffee/PackWorker.js index 10431a85a8..ecd60a7ccd 100644 --- a/services/track-changes/app/coffee/PackWorker.js +++ b/services/track-changes/app/coffee/PackWorker.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -40,7 +46,7 @@ if (!source.match(/^[0-9]+$/)) { const file = fs.readFileSync(source); const result = (() => { const result1 = []; - for (let line of Array.from(file.toString().split('\n'))) { + for (const line of Array.from(file.toString().split('\n'))) { [project_id, doc_id] = Array.from(line.split(' ')); result1.push({doc_id, project_id}); } diff --git a/services/track-changes/app/coffee/ProjectIterator.js b/services/track-changes/app/coffee/ProjectIterator.js index 5768282960..25afa95737 100644 --- a/services/track-changes/app/coffee/ProjectIterator.js +++ b/services/track-changes/app/coffee/ProjectIterator.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-unmodified-loop-condition, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -21,7 +27,7 @@ module.exports = (ProjectIterator = next(callback) { // what's up next - //console.log ">>> top item", iterator.packs[0] + // console.log ">>> top item", iterator.packs[0] const iterator = this; const { before } = this; const { queue } = iterator; @@ -30,10 +36,10 @@ module.exports = (ProjectIterator = let lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0; let nextItem = queue.peek(); - //console.log "queue empty?", queue.empty() - //console.log "nextItem", nextItem - //console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts - //console.log "lowWaterMark", lowWaterMark + // console.log "queue empty?", queue.empty() + // console.log "nextItem", nextItem + // console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts + // console.log "lowWaterMark", lowWaterMark while ((before != null) && ((nextPack != null ? nextPack.meta.start_ts : undefined) > before)) { // discard pack that is outside range @@ -47,9 +53,9 @@ module.exports = (ProjectIterator = return this.getPackByIdFn(nextPack.project_id, nextPack.doc_id, nextPack._id, function(err, pack) { if (err != null) { return callback(err); } iterator.packs.shift(); // have now retrieved this pack, remove it - //console.log "got pack", pack - for (let op of Array.from(pack.pack)) { - //console.log "adding op", op + // console.log "got pack", pack + for (const op of Array.from(pack.pack)) { + // console.log "adding op", op if ((before == null) || (op.meta.end_ts < before)) { op.doc_id = nextPack.doc_id; op.project_id = nextPack.project_id; @@ -61,15 +67,15 @@ module.exports = (ProjectIterator = }); } - //console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark + // console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark while ((nextItem != null) && ((nextItem != null ? nextItem.meta.end_ts : undefined) > lowWaterMark)) { opsToReturn.push(nextItem); queue.pop(); nextItem = queue.peek(); } - //console.log "queue empty?", queue.empty() - //console.log "nextPack", nextPack? + // console.log "queue empty?", queue.empty() + // console.log "nextPack", nextPack? if (queue.empty() && (nextPack == null)) { // got everything iterator._done = true; diff --git a/services/track-changes/app/coffee/RedisManager.js b/services/track-changes/app/coffee/RedisManager.js index a75cef3394..8130d16710 100644 --- a/services/track-changes/app/coffee/RedisManager.js +++ b/services/track-changes/app/coffee/RedisManager.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -36,7 +42,7 @@ module.exports = (RedisManager = { if (callback == null) { callback = function(error) {}; } const multi = rclient.multi(); // Delete all the updates which have been applied (exact match) - for (let update of Array.from(docUpdates || [])) { + for (const update of Array.from(docUpdates || [])) { multi.lrem(Keys.uncompressedHistoryOps({doc_id}), 1, update); } return multi.exec(function(error, results) { @@ -72,7 +78,7 @@ module.exports = (RedisManager = { let keys; if (error != null) { return callback(error); } [cursor, keys] = Array.from(reply); - for (let key of Array.from(keys)) { + for (const key of Array.from(keys)) { keySet[key] = true; } if (cursor === '0') { // note redis returns string result not numeric @@ -90,7 +96,7 @@ module.exports = (RedisManager = { _extractIds(keyList) { const ids = (() => { const result = []; - for (let key of Array.from(keyList)) { + for (const key of Array.from(keyList)) { const m = key.match(/:\{?([0-9a-f]{24})\}?/); // extract object id result.push(m[1]); } diff --git a/services/track-changes/app/coffee/RestoreManager.js b/services/track-changes/app/coffee/RestoreManager.js index 62c2698b68..cc4c471309 100644 --- a/services/track-changes/app/coffee/RestoreManager.js +++ b/services/track-changes/app/coffee/RestoreManager.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/UpdateCompressor.js b/services/track-changes/app/coffee/UpdateCompressor.js index c9716a3024..5496889536 100644 --- a/services/track-changes/app/coffee/UpdateCompressor.js +++ b/services/track-changes/app/coffee/UpdateCompressor.js @@ -1,3 +1,12 @@ +/* eslint-disable + camelcase, + handle-callback-err, + new-cap, + no-throw-literal, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -31,7 +40,7 @@ module.exports = (UpdateCompressor = { // }] convertToSingleOpUpdates(updates) { const splitUpdates = []; - for (let update of Array.from(updates)) { + for (const update of Array.from(updates)) { // Reject any non-insert or delete ops, i.e. comments const ops = update.op.filter(o => (o.i != null) || (o.d != null)); if (ops.length === 0) { @@ -45,7 +54,7 @@ module.exports = (UpdateCompressor = { v: update.v }); } else { - for (let op of Array.from(ops)) { + for (const op of Array.from(ops)) { splitUpdates.push({ op, meta: { @@ -63,7 +72,7 @@ module.exports = (UpdateCompressor = { concatUpdatesWithSameVersion(updates) { const concattedUpdates = []; - for (let update of Array.from(updates)) { + for (const update of Array.from(updates)) { const lastUpdate = concattedUpdates[concattedUpdates.length - 1]; if ((lastUpdate != null) && (lastUpdate.v === update.v)) { if (update.op !== UpdateCompressor.NOOP) { lastUpdate.op.push(update.op); } @@ -98,7 +107,7 @@ module.exports = (UpdateCompressor = { if (updates.length === 0) { return []; } let compressedUpdates = [updates.shift()]; - for (let update of Array.from(updates)) { + for (const update of Array.from(updates)) { const lastCompressedUpdate = compressedUpdates.pop(); if (lastCompressedUpdate != null) { compressedUpdates = compressedUpdates.concat(UpdateCompressor._concatTwoUpdates(lastCompressedUpdate, update)); @@ -249,7 +258,7 @@ module.exports = (UpdateCompressor = { const ops = []; let position = 0; - for (let diff of Array.from(diffs)) { + for (const diff of Array.from(diffs)) { const type = diff[0]; const content = diff[1]; if (type === this.ADDED) { diff --git a/services/track-changes/app/coffee/UpdateTrimmer.js b/services/track-changes/app/coffee/UpdateTrimmer.js index ab81e1b1f0..393ff9ce99 100644 --- a/services/track-changes/app/coffee/UpdateTrimmer.js +++ b/services/track-changes/app/coffee/UpdateTrimmer.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/UpdatesManager.js b/services/track-changes/app/coffee/UpdatesManager.js index 658eb5c7d7..0f3ae8568c 100644 --- a/services/track-changes/app/coffee/UpdatesManager.js +++ b/services/track-changes/app/coffee/UpdatesManager.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -195,7 +202,7 @@ module.exports = (UpdatesManager = { if (error != null) { return callback(error); } return UpdatesManager._prepareProjectForUpdates(project_id, function(error, temporary) { const jobs = []; - for (let doc_id of Array.from(doc_ids)) { + for (const doc_id of Array.from(doc_ids)) { (doc_id => jobs.push(cb => UpdatesManager._processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, cb)) )(doc_id); @@ -269,7 +276,7 @@ module.exports = (UpdatesManager = { if (callback == null) { callback = function(error, updates) {}; } return UpdatesManager.processUncompressedUpdatesWithLock(project_id, doc_id, function(error) { if (error != null) { return callback(error); } - //console.log "options", options + // console.log "options", options return PackManager.getOpsByVersionRange(project_id, doc_id, options.from, options.to, function(error, updates) { if (error != null) { return callback(error); } return callback(null, updates); @@ -302,14 +309,14 @@ module.exports = (UpdatesManager = { if (err != null) { return callback(err); } // repeatedly get updates and pass them through the summariser to get an final output with user info return async.whilst(() => - //console.log "checking iterator.done", iterator.done() + // console.log "checking iterator.done", iterator.done() (summarizedUpdates.length < options.min_count) && !iterator.done() , cb => iterator.next(function(err, partialUpdates) { if (err != null) { return callback(err); } - //logger.log {partialUpdates}, 'got partialUpdates' - if (partialUpdates.length === 0) { return cb(); } //# FIXME should try to avoid this happening + // logger.log {partialUpdates}, 'got partialUpdates' + if (partialUpdates.length === 0) { return cb(); } // # FIXME should try to avoid this happening nextBeforeTimestamp = partialUpdates[partialUpdates.length - 1].meta.end_ts; // add the updates to the summary list summarizedUpdates = UpdatesManager._summarizeUpdates(partialUpdates, summarizedUpdates); @@ -318,7 +325,7 @@ module.exports = (UpdatesManager = { , () => // finally done all updates - //console.log 'summarized Updates', summarizedUpdates + // console.log 'summarized Updates', summarizedUpdates UpdatesManager.fillSummarizedUserInfo(summarizedUpdates, function(err, results) { if (err != null) { return callback(err); } return callback(null, results, !iterator.done() ? nextBeforeTimestamp : undefined); @@ -332,7 +339,7 @@ module.exports = (UpdatesManager = { if (callback == null) { callback = function(error, fetchedUserInfo) {}; } const jobs = []; const fetchedUserInfo = {}; - for (let user_id in users) { + for (const user_id in users) { (user_id => jobs.push(callback => WebApiManager.getUserInfo(user_id, function(error, userInfo) { @@ -419,7 +426,7 @@ module.exports = (UpdatesManager = { if (existingSummarizedUpdates == null) { existingSummarizedUpdates = []; } const summarizedUpdates = existingSummarizedUpdates.slice(); let previousUpdateWasBigDelete = false; - for (let update of Array.from(updates)) { + for (const update of Array.from(updates)) { var doc_id; const earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1]; let shouldConcat = false; @@ -439,7 +446,7 @@ module.exports = (UpdatesManager = { } let isBigDelete = false; - for (let op of Array.from(update.op || [])) { + for (const op of Array.from(update.op || [])) { if ((op.d != null) && (op.d.length > this.SPLIT_ON_DELETE_SIZE)) { isBigDelete = true; } diff --git a/services/track-changes/app/coffee/WebApiManager.js b/services/track-changes/app/coffee/WebApiManager.js index 041b3170ad..f31c4a649e 100644 --- a/services/track-changes/app/coffee/WebApiManager.js +++ b/services/track-changes/app/coffee/WebApiManager.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/app/coffee/mongojs.js b/services/track-changes/app/coffee/mongojs.js index 2fb698bd9f..9dc03952e5 100644 --- a/services/track-changes/app/coffee/mongojs.js +++ b/services/track-changes/app/coffee/mongojs.js @@ -1,3 +1,5 @@ +// TODO: This file was created by bulk-decaffeinate. +// Sanity-check the conversion and remove this comment. const Settings = require("settings-sharelatex"); const mongojs = require("mongojs"); const bson = require("bson"); From e79e044644719ec598aac008978f95ff6dcd387e Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:34:25 +0100 Subject: [PATCH 468/549] decaffeinate: rename app/coffee dir to app/js --- services/track-changes/app/{coffee => js}/DiffGenerator.js | 0 services/track-changes/app/{coffee => js}/DiffManager.js | 0 .../track-changes/app/{coffee => js}/DocumentUpdaterManager.js | 0 services/track-changes/app/{coffee => js}/HealthChecker.js | 0 services/track-changes/app/{coffee => js}/HttpController.js | 0 services/track-changes/app/{coffee => js}/LockManager.js | 0 services/track-changes/app/{coffee => js}/MongoAWS.js | 0 services/track-changes/app/{coffee => js}/MongoManager.js | 0 services/track-changes/app/{coffee => js}/PackManager.js | 0 services/track-changes/app/{coffee => js}/PackWorker.js | 0 services/track-changes/app/{coffee => js}/ProjectIterator.js | 0 services/track-changes/app/{coffee => js}/RedisManager.js | 0 services/track-changes/app/{coffee => js}/RestoreManager.js | 0 services/track-changes/app/{coffee => js}/UpdateCompressor.js | 0 services/track-changes/app/{coffee => js}/UpdateTrimmer.js | 0 services/track-changes/app/{coffee => js}/UpdatesManager.js | 0 services/track-changes/app/{coffee => js}/WebApiManager.js | 0 services/track-changes/app/{coffee => js}/mongojs.js | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/app/{coffee => js}/DiffGenerator.js (100%) rename services/track-changes/app/{coffee => js}/DiffManager.js (100%) rename services/track-changes/app/{coffee => js}/DocumentUpdaterManager.js (100%) rename services/track-changes/app/{coffee => js}/HealthChecker.js (100%) rename services/track-changes/app/{coffee => js}/HttpController.js (100%) rename services/track-changes/app/{coffee => js}/LockManager.js (100%) rename services/track-changes/app/{coffee => js}/MongoAWS.js (100%) rename services/track-changes/app/{coffee => js}/MongoManager.js (100%) rename services/track-changes/app/{coffee => js}/PackManager.js (100%) rename services/track-changes/app/{coffee => js}/PackWorker.js (100%) rename services/track-changes/app/{coffee => js}/ProjectIterator.js (100%) rename services/track-changes/app/{coffee => js}/RedisManager.js (100%) rename services/track-changes/app/{coffee => js}/RestoreManager.js (100%) rename services/track-changes/app/{coffee => js}/UpdateCompressor.js (100%) rename services/track-changes/app/{coffee => js}/UpdateTrimmer.js (100%) rename services/track-changes/app/{coffee => js}/UpdatesManager.js (100%) rename services/track-changes/app/{coffee => js}/WebApiManager.js (100%) rename services/track-changes/app/{coffee => js}/mongojs.js (100%) diff --git a/services/track-changes/app/coffee/DiffGenerator.js b/services/track-changes/app/js/DiffGenerator.js similarity index 100% rename from services/track-changes/app/coffee/DiffGenerator.js rename to services/track-changes/app/js/DiffGenerator.js diff --git a/services/track-changes/app/coffee/DiffManager.js b/services/track-changes/app/js/DiffManager.js similarity index 100% rename from services/track-changes/app/coffee/DiffManager.js rename to services/track-changes/app/js/DiffManager.js diff --git a/services/track-changes/app/coffee/DocumentUpdaterManager.js b/services/track-changes/app/js/DocumentUpdaterManager.js similarity index 100% rename from services/track-changes/app/coffee/DocumentUpdaterManager.js rename to services/track-changes/app/js/DocumentUpdaterManager.js diff --git a/services/track-changes/app/coffee/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js similarity index 100% rename from services/track-changes/app/coffee/HealthChecker.js rename to services/track-changes/app/js/HealthChecker.js diff --git a/services/track-changes/app/coffee/HttpController.js b/services/track-changes/app/js/HttpController.js similarity index 100% rename from services/track-changes/app/coffee/HttpController.js rename to services/track-changes/app/js/HttpController.js diff --git a/services/track-changes/app/coffee/LockManager.js b/services/track-changes/app/js/LockManager.js similarity index 100% rename from services/track-changes/app/coffee/LockManager.js rename to services/track-changes/app/js/LockManager.js diff --git a/services/track-changes/app/coffee/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js similarity index 100% rename from services/track-changes/app/coffee/MongoAWS.js rename to services/track-changes/app/js/MongoAWS.js diff --git a/services/track-changes/app/coffee/MongoManager.js b/services/track-changes/app/js/MongoManager.js similarity index 100% rename from services/track-changes/app/coffee/MongoManager.js rename to services/track-changes/app/js/MongoManager.js diff --git a/services/track-changes/app/coffee/PackManager.js b/services/track-changes/app/js/PackManager.js similarity index 100% rename from services/track-changes/app/coffee/PackManager.js rename to services/track-changes/app/js/PackManager.js diff --git a/services/track-changes/app/coffee/PackWorker.js b/services/track-changes/app/js/PackWorker.js similarity index 100% rename from services/track-changes/app/coffee/PackWorker.js rename to services/track-changes/app/js/PackWorker.js diff --git a/services/track-changes/app/coffee/ProjectIterator.js b/services/track-changes/app/js/ProjectIterator.js similarity index 100% rename from services/track-changes/app/coffee/ProjectIterator.js rename to services/track-changes/app/js/ProjectIterator.js diff --git a/services/track-changes/app/coffee/RedisManager.js b/services/track-changes/app/js/RedisManager.js similarity index 100% rename from services/track-changes/app/coffee/RedisManager.js rename to services/track-changes/app/js/RedisManager.js diff --git a/services/track-changes/app/coffee/RestoreManager.js b/services/track-changes/app/js/RestoreManager.js similarity index 100% rename from services/track-changes/app/coffee/RestoreManager.js rename to services/track-changes/app/js/RestoreManager.js diff --git a/services/track-changes/app/coffee/UpdateCompressor.js b/services/track-changes/app/js/UpdateCompressor.js similarity index 100% rename from services/track-changes/app/coffee/UpdateCompressor.js rename to services/track-changes/app/js/UpdateCompressor.js diff --git a/services/track-changes/app/coffee/UpdateTrimmer.js b/services/track-changes/app/js/UpdateTrimmer.js similarity index 100% rename from services/track-changes/app/coffee/UpdateTrimmer.js rename to services/track-changes/app/js/UpdateTrimmer.js diff --git a/services/track-changes/app/coffee/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js similarity index 100% rename from services/track-changes/app/coffee/UpdatesManager.js rename to services/track-changes/app/js/UpdatesManager.js diff --git a/services/track-changes/app/coffee/WebApiManager.js b/services/track-changes/app/js/WebApiManager.js similarity index 100% rename from services/track-changes/app/coffee/WebApiManager.js rename to services/track-changes/app/js/WebApiManager.js diff --git a/services/track-changes/app/coffee/mongojs.js b/services/track-changes/app/js/mongojs.js similarity index 100% rename from services/track-changes/app/coffee/mongojs.js rename to services/track-changes/app/js/mongojs.js From 3587b42311296ef0de58ce11025fb7be00e84ace Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:34:28 +0100 Subject: [PATCH 469/549] prettier: convert app/js decaffeinated files to Prettier format --- .../track-changes/app/js/DiffGenerator.js | 549 +++--- services/track-changes/app/js/DiffManager.js | 272 +-- .../app/js/DocumentUpdaterManager.js | 130 +- .../track-changes/app/js/HealthChecker.js | 124 +- .../track-changes/app/js/HttpController.js | 416 ++-- services/track-changes/app/js/LockManager.js | 235 ++- services/track-changes/app/js/MongoAWS.js | 276 +-- services/track-changes/app/js/MongoManager.js | 306 +-- services/track-changes/app/js/PackManager.js | 1665 +++++++++++------ services/track-changes/app/js/PackWorker.js | 302 +-- .../track-changes/app/js/ProjectIterator.js | 153 +- services/track-changes/app/js/RedisManager.js | 247 ++- .../track-changes/app/js/RestoreManager.js | 51 +- .../track-changes/app/js/UpdateCompressor.js | 552 +++--- .../track-changes/app/js/UpdateTrimmer.js | 94 +- .../track-changes/app/js/UpdatesManager.js | 1225 +++++++----- .../track-changes/app/js/WebApiManager.js | 173 +- services/track-changes/app/js/mongojs.js | 21 +- 18 files changed, 4057 insertions(+), 2734 deletions(-) diff --git a/services/track-changes/app/js/DiffGenerator.js b/services/track-changes/app/js/DiffGenerator.js index 4a815f6c3b..57e998d318 100644 --- a/services/track-changes/app/js/DiffGenerator.js +++ b/services/track-changes/app/js/DiffGenerator.js @@ -12,289 +12,334 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let DiffGenerator; +let DiffGenerator var ConsistencyError = function(message) { - const error = new Error(message); - error.name = "ConsistencyError"; - error.__proto__ = ConsistencyError.prototype; - return error; -}; -ConsistencyError.prototype.__proto__ = Error.prototype; + const error = new Error(message) + error.name = 'ConsistencyError' + error.__proto__ = ConsistencyError.prototype + return error +} +ConsistencyError.prototype.__proto__ = Error.prototype -const logger = require("logger-sharelatex"); +const logger = require('logger-sharelatex') -module.exports = (DiffGenerator = { - ConsistencyError, +module.exports = DiffGenerator = { + ConsistencyError, - rewindUpdate(content, update) { - for (let j = update.op.length - 1, i = j; j >= 0; j--, i = j) { - const op = update.op[i]; - if (op.broken !== true) { - try { - content = DiffGenerator.rewindOp(content, op); - } catch (e) { - if (e instanceof ConsistencyError && (i = update.op.length - 1)) { - // catch known case where the last op in an array has been - // merged into a later op - logger.error({err: e, update, op: JSON.stringify(op)}, "marking op as broken"); - op.broken = true; - } else { - throw e; // rethrow the execption - } - } - } - } - return content; - }, + rewindUpdate(content, update) { + for (let j = update.op.length - 1, i = j; j >= 0; j--, i = j) { + const op = update.op[i] + if (op.broken !== true) { + try { + content = DiffGenerator.rewindOp(content, op) + } catch (e) { + if (e instanceof ConsistencyError && (i = update.op.length - 1)) { + // catch known case where the last op in an array has been + // merged into a later op + logger.error( + { err: e, update, op: JSON.stringify(op) }, + 'marking op as broken' + ) + op.broken = true + } else { + throw e // rethrow the execption + } + } + } + } + return content + }, - rewindOp(content, op) { - let p; - if (op.i != null) { - // ShareJS will accept an op where p > content.length when applied, - // and it applies as though p == content.length. However, the op is - // passed to us with the original p > content.length. Detect if that - // is the case with this op, and shift p back appropriately to match - // ShareJS if so. - ({ p } = op); - const max_p = content.length - op.i.length; - if (p > max_p) { - logger.warn({max_p, p}, "truncating position to content length"); - p = max_p; - } + rewindOp(content, op) { + let p + if (op.i != null) { + // ShareJS will accept an op where p > content.length when applied, + // and it applies as though p == content.length. However, the op is + // passed to us with the original p > content.length. Detect if that + // is the case with this op, and shift p back appropriately to match + // ShareJS if so. + ;({ p } = op) + const max_p = content.length - op.i.length + if (p > max_p) { + logger.warn({ max_p, p }, 'truncating position to content length') + p = max_p + } - const textToBeRemoved = content.slice(p, p + op.i.length); - if (op.i !== textToBeRemoved) { - throw new ConsistencyError( - `Inserted content, '${op.i}', does not match text to be removed, '${textToBeRemoved}'` - ); - } + const textToBeRemoved = content.slice(p, p + op.i.length) + if (op.i !== textToBeRemoved) { + throw new ConsistencyError( + `Inserted content, '${op.i}', does not match text to be removed, '${textToBeRemoved}'` + ) + } - return content.slice(0, p) + content.slice(p + op.i.length); + return content.slice(0, p) + content.slice(p + op.i.length) + } else if (op.d != null) { + return content.slice(0, op.p) + op.d + content.slice(op.p) + } else { + return content + } + }, - } else if (op.d != null) { - return content.slice(0, op.p) + op.d + content.slice(op.p); - - } else { - return content; - } - }, + rewindUpdates(content, updates) { + for (const update of Array.from(updates.reverse())) { + try { + content = DiffGenerator.rewindUpdate(content, update) + } catch (e) { + e.attempted_update = update // keep a record of the attempted update + throw e // rethrow the exception + } + } + return content + }, - rewindUpdates(content, updates) { - for (const update of Array.from(updates.reverse())) { - try { - content = DiffGenerator.rewindUpdate(content, update); - } catch (e) { - e.attempted_update = update; // keep a record of the attempted update - throw e; // rethrow the exception - } - } - return content; - }, + buildDiff(initialContent, updates) { + let diff = [{ u: initialContent }] + for (const update of Array.from(updates)) { + diff = DiffGenerator.applyUpdateToDiff(diff, update) + } + diff = DiffGenerator.compressDiff(diff) + return diff + }, - buildDiff(initialContent, updates) { - let diff = [ {u: initialContent} ]; - for (const update of Array.from(updates)) { - diff = DiffGenerator.applyUpdateToDiff(diff, update); - } - diff = DiffGenerator.compressDiff(diff); - return diff; - }, + compressDiff(diff) { + const newDiff = [] + for (const part of Array.from(diff)) { + const lastPart = newDiff[newDiff.length - 1] + if ( + lastPart != null && + (lastPart.meta != null ? lastPart.meta.user : undefined) != null && + (part.meta != null ? part.meta.user : undefined) != null + ) { + if ( + lastPart.i != null && + part.i != null && + lastPart.meta.user.id === part.meta.user.id + ) { + lastPart.i += part.i + lastPart.meta.start_ts = Math.min( + lastPart.meta.start_ts, + part.meta.start_ts + ) + lastPart.meta.end_ts = Math.max( + lastPart.meta.end_ts, + part.meta.end_ts + ) + } else if ( + lastPart.d != null && + part.d != null && + lastPart.meta.user.id === part.meta.user.id + ) { + lastPart.d += part.d + lastPart.meta.start_ts = Math.min( + lastPart.meta.start_ts, + part.meta.start_ts + ) + lastPart.meta.end_ts = Math.max( + lastPart.meta.end_ts, + part.meta.end_ts + ) + } else { + newDiff.push(part) + } + } else { + newDiff.push(part) + } + } + return newDiff + }, - compressDiff(diff) { - const newDiff = []; - for (const part of Array.from(diff)) { - const lastPart = newDiff[newDiff.length - 1]; - if ((lastPart != null) && ((lastPart.meta != null ? lastPart.meta.user : undefined) != null) && ((part.meta != null ? part.meta.user : undefined) != null)) { - if ((lastPart.i != null) && (part.i != null) && (lastPart.meta.user.id === part.meta.user.id)) { - lastPart.i += part.i; - lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts); - lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts); - } else if ((lastPart.d != null) && (part.d != null) && (lastPart.meta.user.id === part.meta.user.id)) { - lastPart.d += part.d; - lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts); - lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts); - } else { - newDiff.push(part); - } - } else { - newDiff.push(part); - } - } - return newDiff; - }, + applyOpToDiff(diff, op, meta) { + let consumedDiff + const position = 0 - applyOpToDiff(diff, op, meta) { - let consumedDiff; - const position = 0; + let remainingDiff = diff.slice() + ;({ consumedDiff, remainingDiff } = DiffGenerator._consumeToOffset( + remainingDiff, + op.p + )) + const newDiff = consumedDiff - let remainingDiff = diff.slice(); - ({consumedDiff, remainingDiff} = DiffGenerator._consumeToOffset(remainingDiff, op.p)); - const newDiff = consumedDiff; + if (op.i != null) { + newDiff.push({ + i: op.i, + meta + }) + } else if (op.d != null) { + ;({ + consumedDiff, + remainingDiff + } = DiffGenerator._consumeDiffAffectedByDeleteOp(remainingDiff, op, meta)) + newDiff.push(...Array.from(consumedDiff || [])) + } - if (op.i != null) { - newDiff.push({ - i: op.i, - meta - }); - } else if (op.d != null) { - ({consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteOp(remainingDiff, op, meta)); - newDiff.push(...Array.from(consumedDiff || [])); - } + newDiff.push(...Array.from(remainingDiff || [])) - newDiff.push(...Array.from(remainingDiff || [])); + return newDiff + }, - return newDiff; - }, + applyUpdateToDiff(diff, update) { + for (const op of Array.from(update.op)) { + if (op.broken !== true) { + diff = DiffGenerator.applyOpToDiff(diff, op, update.meta) + } + } + return diff + }, - applyUpdateToDiff(diff, update) { - for (const op of Array.from(update.op)) { - if (op.broken !== true) { - diff = DiffGenerator.applyOpToDiff(diff, op, update.meta); - } - } - return diff; - }, + _consumeToOffset(remainingDiff, totalOffset) { + let part + const consumedDiff = [] + let position = 0 + while ((part = remainingDiff.shift())) { + const length = DiffGenerator._getLengthOfDiffPart(part) + if (part.d != null) { + consumedDiff.push(part) + } else if (position + length >= totalOffset) { + const partOffset = totalOffset - position + if (partOffset > 0) { + consumedDiff.push(DiffGenerator._slicePart(part, 0, partOffset)) + } + if (partOffset < length) { + remainingDiff.unshift(DiffGenerator._slicePart(part, partOffset)) + } + break + } else { + position += length + consumedDiff.push(part) + } + } - _consumeToOffset(remainingDiff, totalOffset) { - let part; - const consumedDiff = []; - let position = 0; - while ((part = remainingDiff.shift())) { - const length = DiffGenerator._getLengthOfDiffPart(part); - if (part.d != null) { - consumedDiff.push(part); - } else if ((position + length) >= totalOffset) { - const partOffset = totalOffset - position; - if (partOffset > 0) { - consumedDiff.push(DiffGenerator._slicePart(part, 0, partOffset)); - } - if (partOffset < length) { - remainingDiff.unshift(DiffGenerator._slicePart(part, partOffset)); - } - break; - } else { - position += length; - consumedDiff.push(part); - } - } + return { + consumedDiff, + remainingDiff + } + }, - return { - consumedDiff, - remainingDiff - }; - }, + _consumeDiffAffectedByDeleteOp(remainingDiff, deleteOp, meta) { + const consumedDiff = [] + let remainingOp = deleteOp + while (remainingOp && remainingDiff.length > 0) { + let newPart + ;({ + newPart, + remainingDiff, + remainingOp + } = DiffGenerator._consumeDeletedPart(remainingDiff, remainingOp, meta)) + if (newPart != null) { + consumedDiff.push(newPart) + } + } + return { + consumedDiff, + remainingDiff + } + }, - _consumeDiffAffectedByDeleteOp(remainingDiff, deleteOp, meta) { - const consumedDiff = []; - let remainingOp = deleteOp; - while (remainingOp && (remainingDiff.length > 0)) { - let newPart; - ({newPart, remainingDiff, remainingOp} = DiffGenerator._consumeDeletedPart(remainingDiff, remainingOp, meta)); - if (newPart != null) { consumedDiff.push(newPart); } - } - return { - consumedDiff, - remainingDiff - }; - }, + _consumeDeletedPart(remainingDiff, op, meta) { + let deletedContent, newPart, remainingOp + const part = remainingDiff.shift() + const partLength = DiffGenerator._getLengthOfDiffPart(part) - _consumeDeletedPart(remainingDiff, op, meta) { - let deletedContent, newPart, remainingOp; - const part = remainingDiff.shift(); - const partLength = DiffGenerator._getLengthOfDiffPart(part); + if (part.d != null) { + // Skip existing deletes + remainingOp = op + newPart = part + } else if (partLength > op.d.length) { + // Only the first bit of the part has been deleted + const remainingPart = DiffGenerator._slicePart(part, op.d.length) + remainingDiff.unshift(remainingPart) - if (part.d != null) { - // Skip existing deletes - remainingOp = op; - newPart = part; + deletedContent = DiffGenerator._getContentOfPart(part).slice( + 0, + op.d.length + ) + if (deletedContent !== op.d) { + throw new ConsistencyError( + `deleted content, '${deletedContent}', does not match delete op, '${op.d}'` + ) + } - } else if (partLength > op.d.length) { - // Only the first bit of the part has been deleted - const remainingPart = DiffGenerator._slicePart(part, op.d.length); - remainingDiff.unshift(remainingPart); + if (part.u != null) { + newPart = { + d: op.d, + meta + } + } else if (part.i != null) { + newPart = null + } - deletedContent = DiffGenerator._getContentOfPart(part).slice(0, op.d.length); - if (deletedContent !== op.d) { - throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${op.d}'`); - } + remainingOp = null + } else if (partLength === op.d.length) { + // The entire part has been deleted, but it is the last part - if (part.u != null) { - newPart = { - d: op.d, - meta - }; - } else if (part.i != null) { - newPart = null; - } + deletedContent = DiffGenerator._getContentOfPart(part) + if (deletedContent !== op.d) { + throw new ConsistencyError( + `deleted content, '${deletedContent}', does not match delete op, '${op.d}'` + ) + } - remainingOp = null; + if (part.u != null) { + newPart = { + d: op.d, + meta + } + } else if (part.i != null) { + newPart = null + } - } else if (partLength === op.d.length) { - // The entire part has been deleted, but it is the last part + remainingOp = null + } else if (partLength < op.d.length) { + // The entire part has been deleted and there is more - deletedContent = DiffGenerator._getContentOfPart(part); - if (deletedContent !== op.d) { - throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${op.d}'`); - } + deletedContent = DiffGenerator._getContentOfPart(part) + const opContent = op.d.slice(0, deletedContent.length) + if (deletedContent !== opContent) { + throw new ConsistencyError( + `deleted content, '${deletedContent}', does not match delete op, '${opContent}'` + ) + } - if (part.u != null) { - newPart = { - d: op.d, - meta - }; - } else if (part.i != null) { - newPart = null; - } + if (part.u) { + newPart = { + d: part.u, + meta + } + } else if (part.i != null) { + newPart = null + } - remainingOp = null; + remainingOp = { + p: op.p, + d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)) + } + } - } else if (partLength < op.d.length) { - // The entire part has been deleted and there is more + return { + newPart, + remainingDiff, + remainingOp + } + }, - deletedContent = DiffGenerator._getContentOfPart(part); - const opContent = op.d.slice(0, deletedContent.length); - if (deletedContent !== opContent) { - throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${opContent}'`); - } + _slicePart(basePart, from, to) { + let part + if (basePart.u != null) { + part = { u: basePart.u.slice(from, to) } + } else if (basePart.i != null) { + part = { i: basePart.i.slice(from, to) } + } + if (basePart.meta != null) { + part.meta = basePart.meta + } + return part + }, - if (part.u) { - newPart = { - d: part.u, - meta - }; - } else if (part.i != null) { - newPart = null; - } + _getLengthOfDiffPart(part) { + return (part.u || part.d || part.i || '').length + }, - remainingOp = - {p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part))}; - } - - return { - newPart, - remainingDiff, - remainingOp - }; - }, - - _slicePart(basePart, from, to) { - let part; - if (basePart.u != null) { - part = { u: basePart.u.slice(from, to) }; - } else if (basePart.i != null) { - part = { i: basePart.i.slice(from, to) }; - } - if (basePart.meta != null) { - part.meta = basePart.meta; - } - return part; - }, - - _getLengthOfDiffPart(part) { - return (part.u || part.d || part.i || '').length; - }, - - _getContentOfPart(part) { - return part.u || part.d || part.i || ''; - } -}); + _getContentOfPart(part) { + return part.u || part.d || part.i || '' + } +} diff --git a/services/track-changes/app/js/DiffManager.js b/services/track-changes/app/js/DiffManager.js index cc20de3651..0b8fa39de6 100644 --- a/services/track-changes/app/js/DiffManager.js +++ b/services/track-changes/app/js/DiffManager.js @@ -11,124 +11,178 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let DiffManager; -const UpdatesManager = require("./UpdatesManager"); -const DocumentUpdaterManager = require("./DocumentUpdaterManager"); -const DiffGenerator = require("./DiffGenerator"); -const logger = require("logger-sharelatex"); +let DiffManager +const UpdatesManager = require('./UpdatesManager') +const DocumentUpdaterManager = require('./DocumentUpdaterManager') +const DiffGenerator = require('./DiffGenerator') +const logger = require('logger-sharelatex') -module.exports = (DiffManager = { - getLatestDocAndUpdates(project_id, doc_id, fromVersion, callback) { - // Get updates last, since then they must be ahead and it - // might be possible to rewind to the same version as the doc. - if (callback == null) { callback = function(error, content, version, updates) {}; } - return DocumentUpdaterManager.getDocument(project_id, doc_id, function(error, content, version) { - if (error != null) { return callback(error); } - if ((fromVersion == null)) { // If we haven't been given a version, just return lastest doc and no updates - return callback(null, content, version, []); - } - return UpdatesManager.getDocUpdatesWithUserInfo(project_id, doc_id, {from: fromVersion}, function(error, updates) { - if (error != null) { return callback(error); } - return callback(null, content, version, updates); - }); - }); - }, - - getDiff(project_id, doc_id, fromVersion, toVersion, callback) { - if (callback == null) { callback = function(error, diff) {}; } - return DiffManager.getDocumentBeforeVersion(project_id, doc_id, fromVersion, function(error, startingContent, updates) { - let diff; - if (error != null) { - if (error.message === "broken-history") { - return callback(null, "history unavailable"); - } else { - return callback(error); - } - } +module.exports = DiffManager = { + getLatestDocAndUpdates(project_id, doc_id, fromVersion, callback) { + // Get updates last, since then they must be ahead and it + // might be possible to rewind to the same version as the doc. + if (callback == null) { + callback = function(error, content, version, updates) {} + } + return DocumentUpdaterManager.getDocument(project_id, doc_id, function( + error, + content, + version + ) { + if (error != null) { + return callback(error) + } + if (fromVersion == null) { + // If we haven't been given a version, just return lastest doc and no updates + return callback(null, content, version, []) + } + return UpdatesManager.getDocUpdatesWithUserInfo( + project_id, + doc_id, + { from: fromVersion }, + function(error, updates) { + if (error != null) { + return callback(error) + } + return callback(null, content, version, updates) + } + ) + }) + }, - const updatesToApply = []; - for (const update of Array.from(updates.slice().reverse())) { - if (update.v <= toVersion) { - updatesToApply.push(update); - } - } + getDiff(project_id, doc_id, fromVersion, toVersion, callback) { + if (callback == null) { + callback = function(error, diff) {} + } + return DiffManager.getDocumentBeforeVersion( + project_id, + doc_id, + fromVersion, + function(error, startingContent, updates) { + let diff + if (error != null) { + if (error.message === 'broken-history') { + return callback(null, 'history unavailable') + } else { + return callback(error) + } + } - try { - diff = DiffGenerator.buildDiff(startingContent, updatesToApply); - } catch (e) { - return callback(e); - } - - return callback(null, diff); - }); - }, + const updatesToApply = [] + for (const update of Array.from(updates.slice().reverse())) { + if (update.v <= toVersion) { + updatesToApply.push(update) + } + } - getDocumentBeforeVersion(project_id, doc_id, version, _callback) { - // Whichever order we get the latest document and the latest updates, - // there is potential for updates to be applied between them so that - // they do not return the same 'latest' versions. - // If this happens, we just retry and hopefully get them at the compatible - // versions. - let retry; - if (_callback == null) { _callback = function(error, document, rewoundUpdates) {}; } - let retries = 3; - const callback = function(error, ...args) { - if (error != null) { - if (error.retry && (retries > 0)) { - logger.warn({error, project_id, doc_id, version, retries}, "retrying getDocumentBeforeVersion"); - return retry(); - } else { - return _callback(error); - } - } else { - return _callback(null, ...Array.from(args)); - } - }; + try { + diff = DiffGenerator.buildDiff(startingContent, updatesToApply) + } catch (e) { + return callback(e) + } - return (retry = function() { - retries--; - return DiffManager._tryGetDocumentBeforeVersion(project_id, doc_id, version, callback); - })(); - }, + return callback(null, diff) + } + ) + }, - _tryGetDocumentBeforeVersion(project_id, doc_id, version, callback) { - if (callback == null) { callback = function(error, document, rewoundUpdates) {}; } - logger.log({project_id, doc_id, version}, "getting document before version"); - return DiffManager.getLatestDocAndUpdates(project_id, doc_id, version, function(error, content, version, updates) { - let startingContent; - if (error != null) { return callback(error); } + getDocumentBeforeVersion(project_id, doc_id, version, _callback) { + // Whichever order we get the latest document and the latest updates, + // there is potential for updates to be applied between them so that + // they do not return the same 'latest' versions. + // If this happens, we just retry and hopefully get them at the compatible + // versions. + let retry + if (_callback == null) { + _callback = function(error, document, rewoundUpdates) {} + } + let retries = 3 + const callback = function(error, ...args) { + if (error != null) { + if (error.retry && retries > 0) { + logger.warn( + { error, project_id, doc_id, version, retries }, + 'retrying getDocumentBeforeVersion' + ) + return retry() + } else { + return _callback(error) + } + } else { + return _callback(null, ...Array.from(args)) + } + } - // bail out if we hit a broken update - for (const u of Array.from(updates)) { - if (u.broken) { - return callback(new Error("broken-history")); - } - } + return (retry = function() { + retries-- + return DiffManager._tryGetDocumentBeforeVersion( + project_id, + doc_id, + version, + callback + ) + })() + }, - // discard any updates which are ahead of this document version - while ((updates[0] != null ? updates[0].v : undefined) >= version) { - updates.shift(); - } + _tryGetDocumentBeforeVersion(project_id, doc_id, version, callback) { + if (callback == null) { + callback = function(error, document, rewoundUpdates) {} + } + logger.log( + { project_id, doc_id, version }, + 'getting document before version' + ) + return DiffManager.getLatestDocAndUpdates( + project_id, + doc_id, + version, + function(error, content, version, updates) { + let startingContent + if (error != null) { + return callback(error) + } - const lastUpdate = updates[0]; - if ((lastUpdate != null) && (lastUpdate.v !== (version - 1))) { - error = new Error(`latest update version, ${lastUpdate.v}, does not match doc version, ${version}`); - error.retry = true; - return callback(error); - } - - logger.log({docVersion: version, lastUpdateVersion: (lastUpdate != null ? lastUpdate.v : undefined), updateCount: updates.length}, "rewinding updates"); + // bail out if we hit a broken update + for (const u of Array.from(updates)) { + if (u.broken) { + return callback(new Error('broken-history')) + } + } - const tryUpdates = updates.slice().reverse(); + // discard any updates which are ahead of this document version + while ((updates[0] != null ? updates[0].v : undefined) >= version) { + updates.shift() + } - try { - startingContent = DiffGenerator.rewindUpdates(content, tryUpdates); - // tryUpdates is reversed, and any unapplied ops are marked as broken - } catch (e) { - return callback(e); - } + const lastUpdate = updates[0] + if (lastUpdate != null && lastUpdate.v !== version - 1) { + error = new Error( + `latest update version, ${lastUpdate.v}, does not match doc version, ${version}` + ) + error.retry = true + return callback(error) + } - return callback(null, startingContent, tryUpdates); - }); - } -}); + logger.log( + { + docVersion: version, + lastUpdateVersion: lastUpdate != null ? lastUpdate.v : undefined, + updateCount: updates.length + }, + 'rewinding updates' + ) + + const tryUpdates = updates.slice().reverse() + + try { + startingContent = DiffGenerator.rewindUpdates(content, tryUpdates) + // tryUpdates is reversed, and any unapplied ops are marked as broken + } catch (e) { + return callback(e) + } + + return callback(null, startingContent, tryUpdates) + } + ) + } +} diff --git a/services/track-changes/app/js/DocumentUpdaterManager.js b/services/track-changes/app/js/DocumentUpdaterManager.js index 6ee516f333..5e83cdd9ab 100644 --- a/services/track-changes/app/js/DocumentUpdaterManager.js +++ b/services/track-changes/app/js/DocumentUpdaterManager.js @@ -11,60 +11,80 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let DocumentUpdaterManager; -const request = require("request"); -const logger = require("logger-sharelatex"); -const Settings = require("settings-sharelatex"); +let DocumentUpdaterManager +const request = require('request') +const logger = require('logger-sharelatex') +const Settings = require('settings-sharelatex') -module.exports = (DocumentUpdaterManager = { - getDocument(project_id, doc_id, callback) { - if (callback == null) { callback = function(error, content, version) {}; } - const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}`; - logger.log({project_id, doc_id}, "getting doc from document updater"); - return request.get(url, function(error, res, body){ - if (error != null) { - return callback(error); - } - if ((res.statusCode >= 200) && (res.statusCode < 300)) { - try { - body = JSON.parse(body); - } catch (error1) { - error = error1; - return callback(error); - } - logger.log({project_id, doc_id, version: body.version}, "got doc from document updater"); - return callback(null, body.lines.join("\n"), body.version); - } else { - error = new Error(`doc updater returned a non-success status code: ${res.statusCode}`); - logger.error({err: error, project_id, doc_id, url}, "error accessing doc updater"); - return callback(error); - } - }); - }, +module.exports = DocumentUpdaterManager = { + getDocument(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error, content, version) {} + } + const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}` + logger.log({ project_id, doc_id }, 'getting doc from document updater') + return request.get(url, function(error, res, body) { + if (error != null) { + return callback(error) + } + if (res.statusCode >= 200 && res.statusCode < 300) { + try { + body = JSON.parse(body) + } catch (error1) { + error = error1 + return callback(error) + } + logger.log( + { project_id, doc_id, version: body.version }, + 'got doc from document updater' + ) + return callback(null, body.lines.join('\n'), body.version) + } else { + error = new Error( + `doc updater returned a non-success status code: ${res.statusCode}` + ) + logger.error( + { err: error, project_id, doc_id, url }, + 'error accessing doc updater' + ) + return callback(error) + } + }) + }, - setDocument(project_id, doc_id, content, user_id, callback) { - if (callback == null) { callback = function(error) {}; } - const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}`; - logger.log({project_id, doc_id}, "setting doc in document updater"); - return request.post({ - url, - json: { - lines: content.split("\n"), - source: "restore", - user_id, - undoing: true - } - }, function(error, res, body){ - if (error != null) { - return callback(error); - } - if ((res.statusCode >= 200) && (res.statusCode < 300)) { - return callback(null); - } else { - error = new Error(`doc updater returned a non-success status code: ${res.statusCode}`); - logger.error({err: error, project_id, doc_id, url}, "error accessing doc updater"); - return callback(error); - } - }); - } -}); \ No newline at end of file + setDocument(project_id, doc_id, content, user_id, callback) { + if (callback == null) { + callback = function(error) {} + } + const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}` + logger.log({ project_id, doc_id }, 'setting doc in document updater') + return request.post( + { + url, + json: { + lines: content.split('\n'), + source: 'restore', + user_id, + undoing: true + } + }, + function(error, res, body) { + if (error != null) { + return callback(error) + } + if (res.statusCode >= 200 && res.statusCode < 300) { + return callback(null) + } else { + error = new Error( + `doc updater returned a non-success status code: ${res.statusCode}` + ) + logger.error( + { err: error, project_id, doc_id, url }, + 'error accessing doc updater' + ) + return callback(error) + } + } + ) + } +} diff --git a/services/track-changes/app/js/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js index 68e18b5f13..7d02157fe8 100644 --- a/services/track-changes/app/js/HealthChecker.js +++ b/services/track-changes/app/js/HealthChecker.js @@ -10,61 +10,75 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const { ObjectId } = require("mongojs"); -const request = require("request"); -const async = require("async"); -const settings = require("settings-sharelatex"); -const { port } = settings.internal.trackchanges; -const logger = require("logger-sharelatex"); -const LockManager = require("./LockManager"); +const { ObjectId } = require('mongojs') +const request = require('request') +const async = require('async') +const settings = require('settings-sharelatex') +const { port } = settings.internal.trackchanges +const logger = require('logger-sharelatex') +const LockManager = require('./LockManager') module.exports = { - check(callback){ - const project_id = ObjectId(settings.trackchanges.healthCheck.project_id); - const url = `http://localhost:${port}/project/${project_id}`; - logger.log({project_id}, "running health check"); - const jobs = [ - cb=> - request.get({url:`http://localhost:${port}/check_lock`, timeout:3000}, function(err, res, body) { - if (err != null) { - logger.err({err, project_id}, "error checking lock for health check"); - return cb(err); - } else if ((res != null ? res.statusCode : undefined) !== 200) { - return cb(`status code not 200, it's ${res.statusCode}`); - } else { - return cb(); - } - }) - , - cb=> - request.post({url:`${url}/flush`, timeout:10000}, function(err, res, body) { - if (err != null) { - logger.err({err, project_id}, "error flushing for health check"); - return cb(err); - } else if ((res != null ? res.statusCode : undefined) !== 204) { - return cb(`status code not 204, it's ${res.statusCode}`); - } else { - return cb(); - } - }) - , - cb=> - request.get({url:`${url}/updates`, timeout:10000}, function(err, res, body){ - if (err != null) { - logger.err({err, project_id}, "error getting updates for health check"); - return cb(err); - } else if ((res != null ? res.statusCode : undefined) !== 200) { - return cb(`status code not 200, it's ${res.statusCode}`); - } else { - return cb(); - } - }) - - ]; - return async.series(jobs, callback); - }, + check(callback) { + const project_id = ObjectId(settings.trackchanges.healthCheck.project_id) + const url = `http://localhost:${port}/project/${project_id}` + logger.log({ project_id }, 'running health check') + const jobs = [ + cb => + request.get( + { url: `http://localhost:${port}/check_lock`, timeout: 3000 }, + function(err, res, body) { + if (err != null) { + logger.err( + { err, project_id }, + 'error checking lock for health check' + ) + return cb(err) + } else if ((res != null ? res.statusCode : undefined) !== 200) { + return cb(`status code not 200, it's ${res.statusCode}`) + } else { + return cb() + } + } + ), + cb => + request.post({ url: `${url}/flush`, timeout: 10000 }, function( + err, + res, + body + ) { + if (err != null) { + logger.err({ err, project_id }, 'error flushing for health check') + return cb(err) + } else if ((res != null ? res.statusCode : undefined) !== 204) { + return cb(`status code not 204, it's ${res.statusCode}`) + } else { + return cb() + } + }), + cb => + request.get({ url: `${url}/updates`, timeout: 10000 }, function( + err, + res, + body + ) { + if (err != null) { + logger.err( + { err, project_id }, + 'error getting updates for health check' + ) + return cb(err) + } else if ((res != null ? res.statusCode : undefined) !== 200) { + return cb(`status code not 200, it's ${res.statusCode}`) + } else { + return cb() + } + }) + ] + return async.series(jobs, callback) + }, - checkLock(callback) { - return LockManager.healthCheck(callback); - } -}; + checkLock(callback) { + return LockManager.healthCheck(callback) + } +} diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js index 6c3178a063..87f0003606 100644 --- a/services/track-changes/app/js/HttpController.js +++ b/services/track-changes/app/js/HttpController.js @@ -12,191 +12,263 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let HttpController; -const UpdatesManager = require("./UpdatesManager"); -const DiffManager = require("./DiffManager"); -const PackManager = require("./PackManager"); -const RestoreManager = require("./RestoreManager"); -const logger = require("logger-sharelatex"); -const HealthChecker = require("./HealthChecker"); -const _ = require("underscore"); +let HttpController +const UpdatesManager = require('./UpdatesManager') +const DiffManager = require('./DiffManager') +const PackManager = require('./PackManager') +const RestoreManager = require('./RestoreManager') +const logger = require('logger-sharelatex') +const HealthChecker = require('./HealthChecker') +const _ = require('underscore') -module.exports = (HttpController = { - flushDoc(req, res, next) { - if (next == null) { next = function(error) {}; } - const { doc_id } = req.params; - const { project_id } = req.params; - logger.log({project_id, doc_id}, "compressing doc history"); - return UpdatesManager.processUncompressedUpdatesWithLock(project_id, doc_id, function(error) { - if (error != null) { return next(error); } - return res.send(204); - }); - }, +module.exports = HttpController = { + flushDoc(req, res, next) { + if (next == null) { + next = function(error) {} + } + const { doc_id } = req.params + const { project_id } = req.params + logger.log({ project_id, doc_id }, 'compressing doc history') + return UpdatesManager.processUncompressedUpdatesWithLock( + project_id, + doc_id, + function(error) { + if (error != null) { + return next(error) + } + return res.send(204) + } + ) + }, - flushProject(req, res, next) { - if (next == null) { next = function(error) {}; } - const { project_id } = req.params; - logger.log({project_id}, "compressing project history"); - return UpdatesManager.processUncompressedUpdatesForProject(project_id, function(error) { - if (error != null) { return next(error); } - return res.send(204); - }); - }, + flushProject(req, res, next) { + if (next == null) { + next = function(error) {} + } + const { project_id } = req.params + logger.log({ project_id }, 'compressing project history') + return UpdatesManager.processUncompressedUpdatesForProject( + project_id, + function(error) { + if (error != null) { + return next(error) + } + return res.send(204) + } + ) + }, - flushAll(req, res, next) { - // limit on projects to flush or -1 for all (default) - if (next == null) { next = function(error) {}; } - const limit = (req.query.limit != null) ? parseInt(req.query.limit, 10) : -1; - logger.log({limit}, "flushing all projects"); - return UpdatesManager.flushAll(limit, function(error, result) { - if (error != null) { return next(error); } - const {failed, succeeded, all} = result; - const status = `${succeeded.length} succeeded, ${failed.length} failed`; - if (limit === 0) { - return res.status(200).send(`${status}\nwould flush:\n${all.join('\n')}\n`); - } else if (failed.length > 0) { - logger.log({failed, succeeded}, "error flushing projects"); - return res.status(500).send(`${status}\nfailed to flush:\n${failed.join('\n')}\n`); - } else { - return res.status(200).send(`${status}\nflushed ${succeeded.length} projects of ${all.length}\n`); - } - }); - }, + flushAll(req, res, next) { + // limit on projects to flush or -1 for all (default) + if (next == null) { + next = function(error) {} + } + const limit = req.query.limit != null ? parseInt(req.query.limit, 10) : -1 + logger.log({ limit }, 'flushing all projects') + return UpdatesManager.flushAll(limit, function(error, result) { + if (error != null) { + return next(error) + } + const { failed, succeeded, all } = result + const status = `${succeeded.length} succeeded, ${failed.length} failed` + if (limit === 0) { + return res + .status(200) + .send(`${status}\nwould flush:\n${all.join('\n')}\n`) + } else if (failed.length > 0) { + logger.log({ failed, succeeded }, 'error flushing projects') + return res + .status(500) + .send(`${status}\nfailed to flush:\n${failed.join('\n')}\n`) + } else { + return res + .status(200) + .send( + `${status}\nflushed ${succeeded.length} projects of ${all.length}\n` + ) + } + }) + }, - checkDanglingUpdates(req, res, next) { - if (next == null) { next = function(error) {}; } - logger.log("checking dangling updates"); - return UpdatesManager.getDanglingUpdates(function(error, result) { - if (error != null) { return next(error); } - if (result.length > 0) { - logger.log({dangling: result}, "found dangling updates"); - return res.status(500).send(`dangling updates:\n${result.join('\n')}\n`); - } else { - return res.status(200).send("no dangling updates found\n"); - } - }); - }, + checkDanglingUpdates(req, res, next) { + if (next == null) { + next = function(error) {} + } + logger.log('checking dangling updates') + return UpdatesManager.getDanglingUpdates(function(error, result) { + if (error != null) { + return next(error) + } + if (result.length > 0) { + logger.log({ dangling: result }, 'found dangling updates') + return res.status(500).send(`dangling updates:\n${result.join('\n')}\n`) + } else { + return res.status(200).send('no dangling updates found\n') + } + }) + }, - checkDoc(req, res, next) { - if (next == null) { next = function(error) {}; } - const { doc_id } = req.params; - const { project_id } = req.params; - logger.log({project_id, doc_id}, "checking doc history"); - return DiffManager.getDocumentBeforeVersion(project_id, doc_id, 1, function(error, document, rewoundUpdates) { - if (error != null) { return next(error); } - const broken = []; - for (const update of Array.from(rewoundUpdates)) { - for (const op of Array.from(update.op)) { - if (op.broken === true) { - broken.push(op); - } - } - } - if (broken.length > 0) { - return res.send(broken); - } else { - return res.send(204); - } - }); - }, + checkDoc(req, res, next) { + if (next == null) { + next = function(error) {} + } + const { doc_id } = req.params + const { project_id } = req.params + logger.log({ project_id, doc_id }, 'checking doc history') + return DiffManager.getDocumentBeforeVersion(project_id, doc_id, 1, function( + error, + document, + rewoundUpdates + ) { + if (error != null) { + return next(error) + } + const broken = [] + for (const update of Array.from(rewoundUpdates)) { + for (const op of Array.from(update.op)) { + if (op.broken === true) { + broken.push(op) + } + } + } + if (broken.length > 0) { + return res.send(broken) + } else { + return res.send(204) + } + }) + }, - getDiff(req, res, next) { - let from, to; - if (next == null) { next = function(error) {}; } - const { doc_id } = req.params; - const { project_id } = req.params; + getDiff(req, res, next) { + let from, to + if (next == null) { + next = function(error) {} + } + const { doc_id } = req.params + const { project_id } = req.params - if (req.query.from != null) { - from = parseInt(req.query.from, 10); - } else { - from = null; - } - if (req.query.to != null) { - to = parseInt(req.query.to, 10); - } else { - to = null; - } + if (req.query.from != null) { + from = parseInt(req.query.from, 10) + } else { + from = null + } + if (req.query.to != null) { + to = parseInt(req.query.to, 10) + } else { + to = null + } - logger.log({project_id, doc_id, from, to}, "getting diff"); - return DiffManager.getDiff(project_id, doc_id, from, to, function(error, diff) { - if (error != null) { return next(error); } - return res.json({diff}); - }); - }, + logger.log({ project_id, doc_id, from, to }, 'getting diff') + return DiffManager.getDiff(project_id, doc_id, from, to, function( + error, + diff + ) { + if (error != null) { + return next(error) + } + return res.json({ diff }) + }) + }, - getUpdates(req, res, next) { - let before, min_count; - if (next == null) { next = function(error) {}; } - const { project_id } = req.params; + getUpdates(req, res, next) { + let before, min_count + if (next == null) { + next = function(error) {} + } + const { project_id } = req.params - if (req.query.before != null) { - before = parseInt(req.query.before, 10); - } - if (req.query.min_count != null) { - min_count = parseInt(req.query.min_count, 10); - } + if (req.query.before != null) { + before = parseInt(req.query.before, 10) + } + if (req.query.min_count != null) { + min_count = parseInt(req.query.min_count, 10) + } - return UpdatesManager.getSummarizedProjectUpdates(project_id, {before, min_count}, function(error, updates, nextBeforeTimestamp) { - if (error != null) { return next(error); } - return res.json({ - updates, - nextBeforeTimestamp - }); - }); - }, + return UpdatesManager.getSummarizedProjectUpdates( + project_id, + { before, min_count }, + function(error, updates, nextBeforeTimestamp) { + if (error != null) { + return next(error) + } + return res.json({ + updates, + nextBeforeTimestamp + }) + } + ) + }, - restore(req, res, next) { - if (next == null) { next = function(error) {}; } - let {doc_id, project_id, version} = req.params; - const user_id = req.headers["x-user-id"]; - version = parseInt(version, 10); - return RestoreManager.restoreToBeforeVersion(project_id, doc_id, version, user_id, function(error) { - if (error != null) { return next(error); } - return res.send(204); - }); - }, + restore(req, res, next) { + if (next == null) { + next = function(error) {} + } + let { doc_id, project_id, version } = req.params + const user_id = req.headers['x-user-id'] + version = parseInt(version, 10) + return RestoreManager.restoreToBeforeVersion( + project_id, + doc_id, + version, + user_id, + function(error) { + if (error != null) { + return next(error) + } + return res.send(204) + } + ) + }, - pushDocHistory(req, res, next) { - if (next == null) { next = function(error) {}; } - const { project_id } = req.params; - const { doc_id } = req.params; - logger.log({project_id, doc_id}, "pushing all finalised changes to s3"); - return PackManager.pushOldPacks(project_id, doc_id, function(error) { - if (error != null) { return next(error); } - return res.send(204); - }); - }, + pushDocHistory(req, res, next) { + if (next == null) { + next = function(error) {} + } + const { project_id } = req.params + const { doc_id } = req.params + logger.log({ project_id, doc_id }, 'pushing all finalised changes to s3') + return PackManager.pushOldPacks(project_id, doc_id, function(error) { + if (error != null) { + return next(error) + } + return res.send(204) + }) + }, - pullDocHistory(req, res, next) { - if (next == null) { next = function(error) {}; } - const { project_id } = req.params; - const { doc_id } = req.params; - logger.log({project_id, doc_id}, "pulling all packs from s3"); - return PackManager.pullOldPacks(project_id, doc_id, function(error) { - if (error != null) { return next(error); } - return res.send(204); - }); - }, + pullDocHistory(req, res, next) { + if (next == null) { + next = function(error) {} + } + const { project_id } = req.params + const { doc_id } = req.params + logger.log({ project_id, doc_id }, 'pulling all packs from s3') + return PackManager.pullOldPacks(project_id, doc_id, function(error) { + if (error != null) { + return next(error) + } + return res.send(204) + }) + }, - healthCheck(req, res){ - return HealthChecker.check(function(err){ - if (err != null) { - logger.err({err}, "error performing health check"); - return res.send(500); - } else { - return res.send(200); - } - }); - }, + healthCheck(req, res) { + return HealthChecker.check(function(err) { + if (err != null) { + logger.err({ err }, 'error performing health check') + return res.send(500) + } else { + return res.send(200) + } + }) + }, - checkLock(req, res){ - return HealthChecker.checkLock(function(err) { - if (err != null) { - logger.err({err}, "error performing lock check"); - return res.send(500); - } else { - return res.send(200); - } - }); - } -}); + checkLock(req, res) { + return HealthChecker.checkLock(function(err) { + if (err != null) { + logger.err({ err }, 'error performing lock check') + return res.send(500) + } else { + return res.send(200) + } + }) + } +} diff --git a/services/track-changes/app/js/LockManager.js b/services/track-changes/app/js/LockManager.js index a0e36d6509..729e9bab6b 100644 --- a/services/track-changes/app/js/LockManager.js +++ b/services/track-changes/app/js/LockManager.js @@ -9,116 +9,149 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let LockManager; -const Settings = require("settings-sharelatex"); -const redis = require("redis-sharelatex"); -const rclient = redis.createClient(Settings.redis.lock); -const os = require("os"); -const crypto = require("crypto"); -const logger = require("logger-sharelatex"); +let LockManager +const Settings = require('settings-sharelatex') +const redis = require('redis-sharelatex') +const rclient = redis.createClient(Settings.redis.lock) +const os = require('os') +const crypto = require('crypto') +const logger = require('logger-sharelatex') -const HOST = os.hostname(); -const PID = process.pid; -const RND = crypto.randomBytes(4).toString('hex'); -let COUNT = 0; +const HOST = os.hostname() +const PID = process.pid +const RND = crypto.randomBytes(4).toString('hex') +let COUNT = 0 -module.exports = (LockManager = { - LOCK_TEST_INTERVAL: 50, // 50ms between each test of the lock - MAX_LOCK_WAIT_TIME: 10000, // 10s maximum time to spend trying to get the lock - LOCK_TTL: 300, // seconds (allow 5 minutes for any operation to complete) +module.exports = LockManager = { + LOCK_TEST_INTERVAL: 50, // 50ms between each test of the lock + MAX_LOCK_WAIT_TIME: 10000, // 10s maximum time to spend trying to get the lock + LOCK_TTL: 300, // seconds (allow 5 minutes for any operation to complete) - // Use a signed lock value as described in - // http://redis.io/topics/distlock#correct-implementation-with-a-single-instance - // to prevent accidental unlocking by multiple processes - randomLock() { - const time = Date.now(); - return `locked:host=${HOST}:pid=${PID}:random=${RND}:time=${time}:count=${COUNT++}`; - }, + // Use a signed lock value as described in + // http://redis.io/topics/distlock#correct-implementation-with-a-single-instance + // to prevent accidental unlocking by multiple processes + randomLock() { + const time = Date.now() + return `locked:host=${HOST}:pid=${PID}:random=${RND}:time=${time}:count=${COUNT++}` + }, - unlockScript: 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end', + unlockScript: + 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end', - tryLock(key, callback) { - if (callback == null) { callback = function(err, gotLock) {}; } - const lockValue = LockManager.randomLock(); - return rclient.set(key, lockValue, "EX", this.LOCK_TTL, "NX", function(err, gotLock){ - if (err != null) { return callback(err); } - if (gotLock === "OK") { - return callback(err, true, lockValue); - } else { - return callback(err, false); - } - }); - }, + tryLock(key, callback) { + if (callback == null) { + callback = function(err, gotLock) {} + } + const lockValue = LockManager.randomLock() + return rclient.set(key, lockValue, 'EX', this.LOCK_TTL, 'NX', function( + err, + gotLock + ) { + if (err != null) { + return callback(err) + } + if (gotLock === 'OK') { + return callback(err, true, lockValue) + } else { + return callback(err, false) + } + }) + }, - getLock(key, callback) { - let attempt; - if (callback == null) { callback = function(error) {}; } - const startTime = Date.now(); - return (attempt = function() { - if ((Date.now() - startTime) > LockManager.MAX_LOCK_WAIT_TIME) { - const e = new Error("Timeout"); - e.key = key; - return callback(e); - } + getLock(key, callback) { + let attempt + if (callback == null) { + callback = function(error) {} + } + const startTime = Date.now() + return (attempt = function() { + if (Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME) { + const e = new Error('Timeout') + e.key = key + return callback(e) + } - return LockManager.tryLock(key, function(error, gotLock, lockValue) { - if (error != null) { return callback(error); } - if (gotLock) { - return callback(null, lockValue); - } else { - return setTimeout(attempt, LockManager.LOCK_TEST_INTERVAL); - } - }); - })(); - }, + return LockManager.tryLock(key, function(error, gotLock, lockValue) { + if (error != null) { + return callback(error) + } + if (gotLock) { + return callback(null, lockValue) + } else { + return setTimeout(attempt, LockManager.LOCK_TEST_INTERVAL) + } + }) + })() + }, - checkLock(key, callback) { - if (callback == null) { callback = function(err, isFree) {}; } - return rclient.exists(key, function(err, exists) { - if (err != null) { return callback(err); } - exists = parseInt(exists); - if (exists === 1) { - return callback(err, false); - } else { - return callback(err, true); - } - }); - }, + checkLock(key, callback) { + if (callback == null) { + callback = function(err, isFree) {} + } + return rclient.exists(key, function(err, exists) { + if (err != null) { + return callback(err) + } + exists = parseInt(exists) + if (exists === 1) { + return callback(err, false) + } else { + return callback(err, true) + } + }) + }, - releaseLock(key, lockValue, callback) { - return rclient.eval(LockManager.unlockScript, 1, key, lockValue, function(err, result) { - if (err != null) { - return callback(err); - } - if ((result != null) && (result !== 1)) { // successful unlock should release exactly one key - logger.error({key, lockValue, redis_err:err, redis_result:result}, "unlocking error"); - return callback(new Error("tried to release timed out lock")); - } - return callback(err,result); - }); - }, + releaseLock(key, lockValue, callback) { + return rclient.eval(LockManager.unlockScript, 1, key, lockValue, function( + err, + result + ) { + if (err != null) { + return callback(err) + } + if (result != null && result !== 1) { + // successful unlock should release exactly one key + logger.error( + { key, lockValue, redis_err: err, redis_result: result }, + 'unlocking error' + ) + return callback(new Error('tried to release timed out lock')) + } + return callback(err, result) + }) + }, - runWithLock(key, runner, callback) { - if (callback == null) { callback = function(error) {}; } - return LockManager.getLock(key, function(error, lockValue) { - if (error != null) { return callback(error); } - return runner(error1 => - LockManager.releaseLock(key, lockValue, function(error2) { - error = error1 || error2; - if (error != null) { return callback(error); } - return callback(); - }) - ); - }); - }, + runWithLock(key, runner, callback) { + if (callback == null) { + callback = function(error) {} + } + return LockManager.getLock(key, function(error, lockValue) { + if (error != null) { + return callback(error) + } + return runner(error1 => + LockManager.releaseLock(key, lockValue, function(error2) { + error = error1 || error2 + if (error != null) { + return callback(error) + } + return callback() + }) + ) + }) + }, - healthCheck(callback) { - const action = releaseLock => releaseLock(); - return LockManager.runWithLock(`HistoryLock:HealthCheck:host=${HOST}:pid=${PID}:random=${RND}`, action, callback); - }, + healthCheck(callback) { + const action = releaseLock => releaseLock() + return LockManager.runWithLock( + `HistoryLock:HealthCheck:host=${HOST}:pid=${PID}:random=${RND}`, + action, + callback + ) + }, - close(callback) { - rclient.quit(); - return rclient.once('end', callback); - } -}); + close(callback) { + rclient.quit() + return rclient.once('end', callback) + } +} diff --git a/services/track-changes/app/js/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js index 3f6326a7e4..613ed930f4 100644 --- a/services/track-changes/app/js/MongoAWS.js +++ b/services/track-changes/app/js/MongoAWS.js @@ -13,137 +13,181 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let MongoAWS; -const settings = require("settings-sharelatex"); -const logger = require("logger-sharelatex"); -const AWS = require('aws-sdk'); -const S3S = require('s3-streams'); -const {db, ObjectId} = require("./mongojs"); -const JSONStream = require("JSONStream"); -const ReadlineStream = require("byline"); -const zlib = require("zlib"); -const Metrics = require("metrics-sharelatex"); +let MongoAWS +const settings = require('settings-sharelatex') +const logger = require('logger-sharelatex') +const AWS = require('aws-sdk') +const S3S = require('s3-streams') +const { db, ObjectId } = require('./mongojs') +const JSONStream = require('JSONStream') +const ReadlineStream = require('byline') +const zlib = require('zlib') +const Metrics = require('metrics-sharelatex') -const DAYS = 24 * 3600 * 1000; // one day in milliseconds +const DAYS = 24 * 3600 * 1000 // one day in milliseconds const createStream = function(streamConstructor, project_id, doc_id, pack_id) { - const AWS_CONFIG = { - accessKeyId: settings.trackchanges.s3.key, - secretAccessKey: settings.trackchanges.s3.secret, - endpoint: settings.trackchanges.s3.endpoint, - s3ForcePathStyle: settings.trackchanges.s3.pathStyle - }; + const AWS_CONFIG = { + accessKeyId: settings.trackchanges.s3.key, + secretAccessKey: settings.trackchanges.s3.secret, + endpoint: settings.trackchanges.s3.endpoint, + s3ForcePathStyle: settings.trackchanges.s3.pathStyle + } - return streamConstructor(new AWS.S3(AWS_CONFIG), { - "Bucket": settings.trackchanges.stores.doc_history, - "Key": project_id+"/changes-"+doc_id+"/pack-"+pack_id - }); -}; + return streamConstructor(new AWS.S3(AWS_CONFIG), { + Bucket: settings.trackchanges.stores.doc_history, + Key: project_id + '/changes-' + doc_id + '/pack-' + pack_id + }) +} -module.exports = (MongoAWS = { +module.exports = MongoAWS = { + archivePack(project_id, doc_id, pack_id, _callback) { + if (_callback == null) { + _callback = function(error) {} + } + const callback = function(...args) { + _callback(...Array.from(args || [])) + return (_callback = function() {}) + } - archivePack(project_id, doc_id, pack_id, _callback) { + const query = { + _id: ObjectId(pack_id), + doc_id: ObjectId(doc_id) + } - if (_callback == null) { _callback = function(error) {}; } - const callback = function(...args) { - _callback(...Array.from(args || [])); - return _callback = function() {}; - }; + if (project_id == null) { + return callback(new Error('invalid project id')) + } + if (doc_id == null) { + return callback(new Error('invalid doc id')) + } + if (pack_id == null) { + return callback(new Error('invalid pack id')) + } - const query = { - _id: ObjectId(pack_id), - doc_id: ObjectId(doc_id) - }; + logger.log({ project_id, doc_id, pack_id }, 'uploading data to s3') - if ((project_id == null)) { return callback(new Error("invalid project id")); } - if ((doc_id == null)) { return callback(new Error("invalid doc id")); } - if ((pack_id == null)) { return callback(new Error("invalid pack id")); } + const upload = createStream(S3S.WriteStream, project_id, doc_id, pack_id) - logger.log({project_id, doc_id, pack_id}, "uploading data to s3"); + return db.docHistory.findOne(query, function(err, result) { + if (err != null) { + return callback(err) + } + if (result == null) { + return callback(new Error('cannot find pack to send to s3')) + } + if (result.expiresAt != null) { + return callback(new Error('refusing to send pack with TTL to s3')) + } + const uncompressedData = JSON.stringify(result) + if (uncompressedData.indexOf('\u0000') !== -1) { + const error = new Error('null bytes found in upload') + logger.error({ err: error, project_id, doc_id, pack_id }, error.message) + return callback(error) + } + return zlib.gzip(uncompressedData, function(err, buf) { + logger.log( + { + project_id, + doc_id, + pack_id, + origSize: uncompressedData.length, + newSize: buf.length + }, + 'compressed pack' + ) + if (err != null) { + return callback(err) + } + upload.on('error', err => callback(err)) + upload.on('finish', function() { + Metrics.inc('archive-pack') + logger.log({ project_id, doc_id, pack_id }, 'upload to s3 completed') + return callback(null) + }) + upload.write(buf) + return upload.end() + }) + }) + }, - const upload = createStream(S3S.WriteStream, project_id, doc_id, pack_id); + readArchivedPack(project_id, doc_id, pack_id, _callback) { + if (_callback == null) { + _callback = function(error, result) {} + } + const callback = function(...args) { + _callback(...Array.from(args || [])) + return (_callback = function() {}) + } - return db.docHistory.findOne(query, function(err, result) { - if (err != null) { return callback(err); } - if ((result == null)) { return callback(new Error("cannot find pack to send to s3")); } - if (result.expiresAt != null) { return callback(new Error("refusing to send pack with TTL to s3")); } - const uncompressedData = JSON.stringify(result); - if (uncompressedData.indexOf("\u0000") !== -1) { - const error = new Error("null bytes found in upload"); - logger.error({err: error, project_id, doc_id, pack_id}, error.message); - return callback(error); - } - return zlib.gzip(uncompressedData, function(err, buf) { - logger.log({project_id, doc_id, pack_id, origSize: uncompressedData.length, newSize: buf.length}, "compressed pack"); - if (err != null) { return callback(err); } - upload.on('error', err => callback(err)); - upload.on('finish', function() { - Metrics.inc("archive-pack"); - logger.log({project_id, doc_id, pack_id}, "upload to s3 completed"); - return callback(null); - }); - upload.write(buf); - return upload.end(); - }); - }); - }, + if (project_id == null) { + return callback(new Error('invalid project id')) + } + if (doc_id == null) { + return callback(new Error('invalid doc id')) + } + if (pack_id == null) { + return callback(new Error('invalid pack id')) + } - readArchivedPack(project_id, doc_id, pack_id, _callback) { - if (_callback == null) { _callback = function(error, result) {}; } - const callback = function(...args) { - _callback(...Array.from(args || [])); - return _callback = function() {}; - }; + logger.log({ project_id, doc_id, pack_id }, 'downloading data from s3') - if ((project_id == null)) { return callback(new Error("invalid project id")); } - if ((doc_id == null)) { return callback(new Error("invalid doc id")); } - if ((pack_id == null)) { return callback(new Error("invalid pack id")); } + const download = createStream(S3S.ReadStream, project_id, doc_id, pack_id) - logger.log({project_id, doc_id, pack_id}, "downloading data from s3"); + const inputStream = download + .on('open', obj => 1) + .on('error', err => callback(err)) - const download = createStream(S3S.ReadStream, project_id, doc_id, pack_id); + const gunzip = zlib.createGunzip() + gunzip.setEncoding('utf8') + gunzip.on('error', function(err) { + logger.log( + { project_id, doc_id, pack_id, err }, + 'error uncompressing gzip stream' + ) + return callback(err) + }) - const inputStream = download - .on('open', obj => 1).on('error', err => callback(err)); + const outputStream = inputStream.pipe(gunzip) + const parts = [] + outputStream.on('error', err => callback(err)) + outputStream.on('end', function() { + let object + logger.log({ project_id, doc_id, pack_id }, 'download from s3 completed') + try { + object = JSON.parse(parts.join('')) + } catch (e) { + return callback(e) + } + object._id = ObjectId(object._id) + object.doc_id = ObjectId(object.doc_id) + object.project_id = ObjectId(object.project_id) + for (const op of Array.from(object.pack)) { + if (op._id != null) { + op._id = ObjectId(op._id) + } + } + return callback(null, object) + }) + return outputStream.on('data', data => parts.push(data)) + }, - const gunzip = zlib.createGunzip(); - gunzip.setEncoding('utf8'); - gunzip.on('error', function(err) { - logger.log({project_id, doc_id, pack_id, err}, "error uncompressing gzip stream"); - return callback(err); - }); - - const outputStream = inputStream.pipe(gunzip); - const parts = []; - outputStream.on('error', err => callback(err)); - outputStream.on('end', function() { - let object; - logger.log({project_id, doc_id, pack_id}, "download from s3 completed"); - try { - object = JSON.parse(parts.join('')); - } catch (e) { - return callback(e); - } - object._id = ObjectId(object._id); - object.doc_id = ObjectId(object.doc_id); - object.project_id = ObjectId(object.project_id); - for (const op of Array.from(object.pack)) { - if (op._id != null) { op._id = ObjectId(op._id); } - } - return callback(null, object); - }); - return outputStream.on('data', data => parts.push(data)); - }, - - unArchivePack(project_id, doc_id, pack_id, callback) { - if (callback == null) { callback = function(error) {}; } - return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function(err, object) { - if (err != null) { return callback(err); } - Metrics.inc("unarchive-pack"); - // allow the object to expire, we can always retrieve it again - object.expiresAt = new Date(Date.now() + (7 * DAYS)); - logger.log({project_id, doc_id, pack_id}, "inserting object from s3"); - return db.docHistory.insert(object, callback); - }); - } -}); + unArchivePack(project_id, doc_id, pack_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function( + err, + object + ) { + if (err != null) { + return callback(err) + } + Metrics.inc('unarchive-pack') + // allow the object to expire, we can always retrieve it again + object.expiresAt = new Date(Date.now() + 7 * DAYS) + logger.log({ project_id, doc_id, pack_id }, 'inserting object from s3') + return db.docHistory.insert(object, callback) + }) + } +} diff --git a/services/track-changes/app/js/MongoManager.js b/services/track-changes/app/js/MongoManager.js index 5edbe6b6e3..947e5b1db1 100644 --- a/services/track-changes/app/js/MongoManager.js +++ b/services/track-changes/app/js/MongoManager.js @@ -11,128 +11,200 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let MongoManager; -const {db, ObjectId} = require("./mongojs"); -const PackManager = require("./PackManager"); -const async = require("async"); -const _ = require("underscore"); -const metrics = require('metrics-sharelatex'); -const logger = require('logger-sharelatex'); +let MongoManager +const { db, ObjectId } = require('./mongojs') +const PackManager = require('./PackManager') +const async = require('async') +const _ = require('underscore') +const metrics = require('metrics-sharelatex') +const logger = require('logger-sharelatex') -module.exports = (MongoManager = { - getLastCompressedUpdate(doc_id, callback) { - if (callback == null) { callback = function(error, update) {}; } - return db.docHistory - .find({doc_id: ObjectId(doc_id.toString())}, {pack: {$slice:-1}}) // only return the last entry in a pack - .sort({ v: -1 }) - .limit(1) - .toArray(function(error, compressedUpdates) { - if (error != null) { return callback(error); } - return callback(null, compressedUpdates[0] || null); - }); - }, +module.exports = MongoManager = { + getLastCompressedUpdate(doc_id, callback) { + if (callback == null) { + callback = function(error, update) {} + } + return db.docHistory + .find({ doc_id: ObjectId(doc_id.toString()) }, { pack: { $slice: -1 } }) // only return the last entry in a pack + .sort({ v: -1 }) + .limit(1) + .toArray(function(error, compressedUpdates) { + if (error != null) { + return callback(error) + } + return callback(null, compressedUpdates[0] || null) + }) + }, - peekLastCompressedUpdate(doc_id, callback) { - // under normal use we pass back the last update as - // callback(null,update,version). - // - // when we have an existing last update but want to force a new one - // to start, we pass it back as callback(null,null,version), just - // giving the version so we can check consistency. - if (callback == null) { callback = function(error, update, version) {}; } - return MongoManager.getLastCompressedUpdate(doc_id, function(error, update) { - if (error != null) { return callback(error); } - if (update != null) { - if (update.broken) { // marked as broken so we will force a new op - return callback(null, null); - } else if (update.pack != null) { - if (update.finalised) { // no more ops can be appended - return callback(null, null, update.pack[0] != null ? update.pack[0].v : undefined); - } else { - return callback(null, update, update.pack[0] != null ? update.pack[0].v : undefined); - } - } else { - return callback(null, update, update.v); - } - } else { - return PackManager.getLastPackFromIndex(doc_id, function(error, pack) { - if (error != null) { return callback(error); } - if (((pack != null ? pack.inS3 : undefined) != null) && ((pack != null ? pack.v_end : undefined) != null)) { return callback(null, null, pack.v_end); } - return callback(null, null); - }); - } - }); - }, + peekLastCompressedUpdate(doc_id, callback) { + // under normal use we pass back the last update as + // callback(null,update,version). + // + // when we have an existing last update but want to force a new one + // to start, we pass it back as callback(null,null,version), just + // giving the version so we can check consistency. + if (callback == null) { + callback = function(error, update, version) {} + } + return MongoManager.getLastCompressedUpdate(doc_id, function( + error, + update + ) { + if (error != null) { + return callback(error) + } + if (update != null) { + if (update.broken) { + // marked as broken so we will force a new op + return callback(null, null) + } else if (update.pack != null) { + if (update.finalised) { + // no more ops can be appended + return callback( + null, + null, + update.pack[0] != null ? update.pack[0].v : undefined + ) + } else { + return callback( + null, + update, + update.pack[0] != null ? update.pack[0].v : undefined + ) + } + } else { + return callback(null, update, update.v) + } + } else { + return PackManager.getLastPackFromIndex(doc_id, function(error, pack) { + if (error != null) { + return callback(error) + } + if ( + (pack != null ? pack.inS3 : undefined) != null && + (pack != null ? pack.v_end : undefined) != null + ) { + return callback(null, null, pack.v_end) + } + return callback(null, null) + }) + } + }) + }, - backportProjectId(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return db.docHistory.update({ - doc_id: ObjectId(doc_id.toString()), - project_id: { $exists: false } - }, { - $set: { project_id: ObjectId(project_id.toString()) } - }, { - multi: true - }, callback); - }, + backportProjectId(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return db.docHistory.update( + { + doc_id: ObjectId(doc_id.toString()), + project_id: { $exists: false } + }, + { + $set: { project_id: ObjectId(project_id.toString()) } + }, + { + multi: true + }, + callback + ) + }, - getProjectMetaData(project_id, callback) { - if (callback == null) { callback = function(error, metadata) {}; } - return db.projectHistoryMetaData.find({ - project_id: ObjectId(project_id.toString()) - }, function(error, results) { - if (error != null) { return callback(error); } - return callback(null, results[0]); - }); - }, + getProjectMetaData(project_id, callback) { + if (callback == null) { + callback = function(error, metadata) {} + } + return db.projectHistoryMetaData.find( + { + project_id: ObjectId(project_id.toString()) + }, + function(error, results) { + if (error != null) { + return callback(error) + } + return callback(null, results[0]) + } + ) + }, - setProjectMetaData(project_id, metadata, callback) { - if (callback == null) { callback = function(error) {}; } - return db.projectHistoryMetaData.update({ - project_id: ObjectId(project_id) - }, { - $set: metadata - }, { - upsert: true - }, callback); - }, + setProjectMetaData(project_id, metadata, callback) { + if (callback == null) { + callback = function(error) {} + } + return db.projectHistoryMetaData.update( + { + project_id: ObjectId(project_id) + }, + { + $set: metadata + }, + { + upsert: true + }, + callback + ) + }, - upgradeHistory(project_id, callback) { - // preserve the project's existing history - if (callback == null) { callback = function(error) {}; } - return db.docHistory.update({ - project_id: ObjectId(project_id), - temporary: true, - expiresAt: {$exists: true} - }, { - $set: {temporary: false}, - $unset: {expiresAt: ""} - }, { - multi: true - }, callback); - }, + upgradeHistory(project_id, callback) { + // preserve the project's existing history + if (callback == null) { + callback = function(error) {} + } + return db.docHistory.update( + { + project_id: ObjectId(project_id), + temporary: true, + expiresAt: { $exists: true } + }, + { + $set: { temporary: false }, + $unset: { expiresAt: '' } + }, + { + multi: true + }, + callback + ) + }, - ensureIndices() { - // For finding all updates that go into a diff for a doc - db.docHistory.ensureIndex({ doc_id: 1, v: 1 }, { background: true }); - // For finding all updates that affect a project - db.docHistory.ensureIndex({ project_id: 1, "meta.end_ts": 1 }, { background: true }); - // For finding updates that don't yet have a project_id and need it inserting - db.docHistory.ensureIndex({ doc_id: 1, project_id: 1 }, { background: true }); - // For finding project meta-data - db.projectHistoryMetaData.ensureIndex({ project_id: 1 }, { background: true }); - // TTL index for auto deleting week old temporary ops - db.docHistory.ensureIndex({ expiresAt: 1 }, { expireAfterSeconds: 0, background: true }); - // For finding packs to be checked for archiving - db.docHistory.ensureIndex({ last_checked: 1 }, { background: true }); - // For finding archived packs - return db.docHistoryIndex.ensureIndex({ project_id: 1 }, { background: true }); - } -}); + ensureIndices() { + // For finding all updates that go into a diff for a doc + db.docHistory.ensureIndex({ doc_id: 1, v: 1 }, { background: true }) + // For finding all updates that affect a project + db.docHistory.ensureIndex( + { project_id: 1, 'meta.end_ts': 1 }, + { background: true } + ) + // For finding updates that don't yet have a project_id and need it inserting + db.docHistory.ensureIndex( + { doc_id: 1, project_id: 1 }, + { background: true } + ) + // For finding project meta-data + db.projectHistoryMetaData.ensureIndex( + { project_id: 1 }, + { background: true } + ) + // TTL index for auto deleting week old temporary ops + db.docHistory.ensureIndex( + { expiresAt: 1 }, + { expireAfterSeconds: 0, background: true } + ) + // For finding packs to be checked for archiving + db.docHistory.ensureIndex({ last_checked: 1 }, { background: true }) + // For finding archived packs + return db.docHistoryIndex.ensureIndex( + { project_id: 1 }, + { background: true } + ) + } +} - -[ - 'getLastCompressedUpdate', - 'getProjectMetaData', - 'setProjectMetaData' -].map(method => metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger)); +;[ + 'getLastCompressedUpdate', + 'getProjectMetaData', + 'setProjectMetaData' +].map(method => + metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger) +) diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 1446c3421d..5f5c0444e1 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -13,17 +13,17 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let PackManager; -const async = require("async"); -const _ = require("underscore"); -const {db, ObjectId, BSON} = require("./mongojs"); -const logger = require("logger-sharelatex"); -const LockManager = require("./LockManager"); -const MongoAWS = require("./MongoAWS"); -const Metrics = require("metrics-sharelatex"); -const ProjectIterator = require("./ProjectIterator"); -const Settings = require("settings-sharelatex"); -const keys = Settings.redis.lock.key_schema; +let PackManager +const async = require('async') +const _ = require('underscore') +const { db, ObjectId, BSON } = require('./mongojs') +const logger = require('logger-sharelatex') +const LockManager = require('./LockManager') +const MongoAWS = require('./MongoAWS') +const Metrics = require('metrics-sharelatex') +const ProjectIterator = require('./ProjectIterator') +const Settings = require('settings-sharelatex') +const keys = Settings.redis.lock.key_schema // Sharejs operations are stored in a 'pack' object // @@ -59,653 +59,1104 @@ const keys = Settings.redis.lock.key_schema; // v_end field from UN. The meta.end_ts field of the pack itself is // from the last entry UN, the meta.start_ts field from U1. -const DAYS = 24 * 3600 * 1000; // one day in milliseconds +const DAYS = 24 * 3600 * 1000 // one day in milliseconds -module.exports = (PackManager = { +module.exports = PackManager = { + MAX_SIZE: 1024 * 1024, // make these configurable parameters + MAX_COUNT: 1024, - MAX_SIZE: 1024*1024, // make these configurable parameters - MAX_COUNT: 1024, + insertCompressedUpdates( + project_id, + doc_id, + lastUpdate, + newUpdates, + temporary, + callback + ) { + if (callback == null) { + callback = function(error) {} + } + if (newUpdates.length === 0) { + return callback() + } - insertCompressedUpdates(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) { - if (callback == null) { callback = function(error) {}; } - if (newUpdates.length === 0) { return callback(); } + // never append permanent ops to a pack that will expire + if ( + (lastUpdate != null ? lastUpdate.expiresAt : undefined) != null && + !temporary + ) { + lastUpdate = null + } - // never append permanent ops to a pack that will expire - if (((lastUpdate != null ? lastUpdate.expiresAt : undefined) != null) && !temporary) { lastUpdate = null; } + const updatesToFlush = [] + const updatesRemaining = newUpdates.slice() - const updatesToFlush = []; - const updatesRemaining = newUpdates.slice(); + let n = (lastUpdate != null ? lastUpdate.n : undefined) || 0 + let sz = (lastUpdate != null ? lastUpdate.sz : undefined) || 0 - let n = (lastUpdate != null ? lastUpdate.n : undefined) || 0; - let sz = (lastUpdate != null ? lastUpdate.sz : undefined) || 0; + while ( + updatesRemaining.length && + n < PackManager.MAX_COUNT && + sz < PackManager.MAX_SIZE + ) { + const nextUpdate = updatesRemaining[0] + const nextUpdateSize = BSON.calculateObjectSize(nextUpdate) + if (nextUpdateSize + sz > PackManager.MAX_SIZE && n > 0) { + break + } + n++ + sz += nextUpdateSize + updatesToFlush.push(updatesRemaining.shift()) + } - while (updatesRemaining.length && (n < PackManager.MAX_COUNT) && (sz < PackManager.MAX_SIZE)) { - const nextUpdate = updatesRemaining[0]; - const nextUpdateSize = BSON.calculateObjectSize(nextUpdate); - if (((nextUpdateSize + sz) > PackManager.MAX_SIZE) && (n > 0)) { - break; - } - n++; - sz += nextUpdateSize; - updatesToFlush.push(updatesRemaining.shift()); - } + return PackManager.flushCompressedUpdates( + project_id, + doc_id, + lastUpdate, + updatesToFlush, + temporary, + function(error) { + if (error != null) { + return callback(error) + } + return PackManager.insertCompressedUpdates( + project_id, + doc_id, + null, + updatesRemaining, + temporary, + callback + ) + } + ) + }, - return PackManager.flushCompressedUpdates(project_id, doc_id, lastUpdate, updatesToFlush, temporary, function(error) { - if (error != null) { return callback(error); } - return PackManager.insertCompressedUpdates(project_id, doc_id, null, updatesRemaining, temporary, callback); - }); - }, + flushCompressedUpdates( + project_id, + doc_id, + lastUpdate, + newUpdates, + temporary, + callback + ) { + if (callback == null) { + callback = function(error) {} + } + if (newUpdates.length === 0) { + return callback() + } - flushCompressedUpdates(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) { - if (callback == null) { callback = function(error) {}; } - if (newUpdates.length === 0) { return callback(); } + let canAppend = false + // check if it is safe to append to an existing pack + if (lastUpdate != null) { + if (!temporary && lastUpdate.expiresAt == null) { + // permanent pack appends to permanent pack + canAppend = true + } + const age = + Date.now() - + (lastUpdate.meta != null ? lastUpdate.meta.start_ts : undefined) + if (temporary && lastUpdate.expiresAt != null && age < 1 * DAYS) { + // temporary pack appends to temporary pack if same day + canAppend = true + } + } - let canAppend = false; - // check if it is safe to append to an existing pack - if (lastUpdate != null) { - if (!temporary && (lastUpdate.expiresAt == null)) { - // permanent pack appends to permanent pack - canAppend = true; - } - const age = Date.now() - (lastUpdate.meta != null ? lastUpdate.meta.start_ts : undefined); - if (temporary && (lastUpdate.expiresAt != null) && (age < (1 * DAYS))) { - // temporary pack appends to temporary pack if same day - canAppend = true; - } - } + if (canAppend) { + return PackManager.appendUpdatesToExistingPack( + project_id, + doc_id, + lastUpdate, + newUpdates, + temporary, + callback + ) + } else { + return PackManager.insertUpdatesIntoNewPack( + project_id, + doc_id, + newUpdates, + temporary, + callback + ) + } + }, - if (canAppend) { - return PackManager.appendUpdatesToExistingPack(project_id, doc_id, lastUpdate, newUpdates, temporary, callback); - } else { - return PackManager.insertUpdatesIntoNewPack(project_id, doc_id, newUpdates, temporary, callback); - } - }, + insertUpdatesIntoNewPack( + project_id, + doc_id, + newUpdates, + temporary, + callback + ) { + if (callback == null) { + callback = function(error) {} + } + const first = newUpdates[0] + const last = newUpdates[newUpdates.length - 1] + const n = newUpdates.length + const sz = BSON.calculateObjectSize(newUpdates) + const newPack = { + project_id: ObjectId(project_id.toString()), + doc_id: ObjectId(doc_id.toString()), + pack: newUpdates, + n, + sz, + meta: { + start_ts: first.meta.start_ts, + end_ts: last.meta.end_ts + }, + v: first.v, + v_end: last.v, + temporary + } + if (temporary) { + newPack.expiresAt = new Date(Date.now() + 7 * DAYS) + newPack.last_checked = new Date(Date.now() + 30 * DAYS) // never check temporary packs + } + logger.log( + { project_id, doc_id, newUpdates }, + 'inserting updates into new pack' + ) + return db.docHistory.save(newPack, function(err, result) { + if (err != null) { + return callback(err) + } + Metrics.inc(`insert-pack-${temporary ? 'temporary' : 'permanent'}`) + if (temporary) { + return callback() + } else { + return PackManager.updateIndex(project_id, doc_id, callback) + } + }) + }, - insertUpdatesIntoNewPack(project_id, doc_id, newUpdates, temporary, callback) { - if (callback == null) { callback = function(error) {}; } - const first = newUpdates[0]; - const last = newUpdates[newUpdates.length - 1]; - const n = newUpdates.length; - const sz = BSON.calculateObjectSize(newUpdates); - const newPack = { - project_id: ObjectId(project_id.toString()), - doc_id: ObjectId(doc_id.toString()), - pack: newUpdates, - n, - sz, - meta: { - start_ts: first.meta.start_ts, - end_ts: last.meta.end_ts - }, - v: first.v, - v_end: last.v, - temporary - }; - if (temporary) { - newPack.expiresAt = new Date(Date.now() + (7 * DAYS)); - newPack.last_checked = new Date(Date.now() + (30 * DAYS)); // never check temporary packs - } - logger.log({project_id, doc_id, newUpdates}, "inserting updates into new pack"); - return db.docHistory.save(newPack, function(err, result) { - if (err != null) { return callback(err); } - Metrics.inc(`insert-pack-${temporary ? "temporary" : "permanent"}`); - if (temporary) { - return callback(); - } else { - return PackManager.updateIndex(project_id, doc_id, callback); - } - }); - }, + appendUpdatesToExistingPack( + project_id, + doc_id, + lastUpdate, + newUpdates, + temporary, + callback + ) { + if (callback == null) { + callback = function(error) {} + } + const first = newUpdates[0] + const last = newUpdates[newUpdates.length - 1] + const n = newUpdates.length + const sz = BSON.calculateObjectSize(newUpdates) + const query = { + _id: lastUpdate._id, + project_id: ObjectId(project_id.toString()), + doc_id: ObjectId(doc_id.toString()), + pack: { $exists: true } + } + const update = { + $push: { + pack: { $each: newUpdates } + }, + $inc: { + n: n, + sz: sz + }, + $set: { + 'meta.end_ts': last.meta.end_ts, + v_end: last.v + } + } + if (lastUpdate.expiresAt && temporary) { + update.$set.expiresAt = new Date(Date.now() + 7 * DAYS) + } + logger.log( + { project_id, doc_id, lastUpdate, newUpdates }, + 'appending updates to existing pack' + ) + Metrics.inc(`append-pack-${temporary ? 'temporary' : 'permanent'}`) + return db.docHistory.findAndModify( + { query, update, new: true, fields: { meta: 1, v_end: 1 } }, + callback + ) + }, - appendUpdatesToExistingPack(project_id, doc_id, lastUpdate, newUpdates, temporary, callback) { - if (callback == null) { callback = function(error) {}; } - const first = newUpdates[0]; - const last = newUpdates[newUpdates.length - 1]; - const n = newUpdates.length; - const sz = BSON.calculateObjectSize(newUpdates); - const query = { - _id: lastUpdate._id, - project_id: ObjectId(project_id.toString()), - doc_id: ObjectId(doc_id.toString()), - pack: {$exists: true} - }; - const update = { - $push: { - "pack": {$each: newUpdates} - }, - $inc: { - "n": n, - "sz": sz - }, - $set: { - "meta.end_ts": last.meta.end_ts, - "v_end": last.v - } - }; - if (lastUpdate.expiresAt && temporary) { - update.$set.expiresAt = new Date(Date.now() + (7 * DAYS)); - } - logger.log({project_id, doc_id, lastUpdate, newUpdates}, "appending updates to existing pack"); - Metrics.inc(`append-pack-${temporary ? "temporary" : "permanent"}`); - return db.docHistory.findAndModify({query, update, new:true, fields:{meta:1,v_end:1}}, callback); - }, + // Retrieve all changes for a document - // Retrieve all changes for a document + getOpsByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) { + if (callback == null) { + callback = function(error, updates) {} + } + return PackManager.loadPacksByVersionRange( + project_id, + doc_id, + fromVersion, + toVersion, + function(error) { + const query = { doc_id: ObjectId(doc_id.toString()) } + if (toVersion != null) { + query.v = { $lte: toVersion } + } + if (fromVersion != null) { + query.v_end = { $gte: fromVersion } + } + // console.log "query:", query + return db.docHistory.find(query).sort({ v: -1 }, function(err, result) { + if (err != null) { + return callback(err) + } + // console.log "getOpsByVersionRange:", err, result + const updates = [] + const opInRange = function(op, from, to) { + if (fromVersion != null && op.v < fromVersion) { + return false + } + if (toVersion != null && op.v > toVersion) { + return false + } + return true + } + for (const docHistory of Array.from(result)) { + // console.log 'adding', docHistory.pack + for (const op of Array.from(docHistory.pack.reverse())) { + if (opInRange(op, fromVersion, toVersion)) { + op.project_id = docHistory.project_id + op.doc_id = docHistory.doc_id + // console.log "added op", op.v, fromVersion, toVersion + updates.push(op) + } + } + } + return callback(null, updates) + }) + } + ) + }, - getOpsByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) { - if (callback == null) { callback = function(error, updates) {}; } - return PackManager.loadPacksByVersionRange(project_id, doc_id, fromVersion, toVersion, function(error) { - const query = {doc_id:ObjectId(doc_id.toString())}; - if (toVersion != null) { query.v = {$lte:toVersion}; } - if (fromVersion != null) { query.v_end = {$gte:fromVersion}; } - // console.log "query:", query - return db.docHistory.find(query).sort({v:-1}, function(err, result) { - if (err != null) { return callback(err); } - // console.log "getOpsByVersionRange:", err, result - const updates = []; - const opInRange = function(op, from, to) { - if ((fromVersion != null) && (op.v < fromVersion)) { return false; } - if ((toVersion != null) && (op.v > toVersion)) { return false; } - return true; - }; - for (const docHistory of Array.from(result)) { - // console.log 'adding', docHistory.pack - for (const op of Array.from(docHistory.pack.reverse())) { - if (opInRange(op, fromVersion, toVersion)) { - op.project_id = docHistory.project_id; - op.doc_id = docHistory.doc_id; - // console.log "added op", op.v, fromVersion, toVersion - updates.push(op); - } - } - } - return callback(null, updates); - }); - }); - }, + loadPacksByVersionRange( + project_id, + doc_id, + fromVersion, + toVersion, + callback + ) { + return PackManager.getIndex(doc_id, function(err, indexResult) { + let pack + if (err != null) { + return callback(err) + } + const indexPacks = + (indexResult != null ? indexResult.packs : undefined) || [] + const packInRange = function(pack, from, to) { + if (fromVersion != null && pack.v_end < fromVersion) { + return false + } + if (toVersion != null && pack.v > toVersion) { + return false + } + return true + } + const neededIds = (() => { + const result = [] + for (pack of Array.from(indexPacks)) { + if (packInRange(pack, fromVersion, toVersion)) { + result.push(pack._id) + } + } + return result + })() + if (neededIds.length) { + return PackManager.fetchPacksIfNeeded( + project_id, + doc_id, + neededIds, + callback + ) + } else { + return callback() + } + }) + }, - loadPacksByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) { - return PackManager.getIndex(doc_id, function(err, indexResult) { - let pack; - if (err != null) { return callback(err); } - const indexPacks = (indexResult != null ? indexResult.packs : undefined) || []; - const packInRange = function(pack, from, to) { - if ((fromVersion != null) && (pack.v_end < fromVersion)) { return false; } - if ((toVersion != null) && (pack.v > toVersion)) { return false; } - return true; - }; - const neededIds = ((() => { - const result = []; - for (pack of Array.from(indexPacks)) { if (packInRange(pack, fromVersion, toVersion)) { - result.push(pack._id); - } - } - return result; - })()); - if (neededIds.length) { - return PackManager.fetchPacksIfNeeded(project_id, doc_id, neededIds, callback); - } else { - return callback(); - } - }); - }, + fetchPacksIfNeeded(project_id, doc_id, pack_ids, callback) { + let id + return db.docHistory.find( + { + _id: { + $in: (() => { + const result = [] + for (id of Array.from(pack_ids)) { + result.push(ObjectId(id)) + } + return result + })() + } + }, + { _id: 1 }, + function(err, loadedPacks) { + if (err != null) { + return callback(err) + } + const allPackIds = (() => { + const result1 = [] + for (id of Array.from(pack_ids)) { + result1.push(id.toString()) + } + return result1 + })() + const loadedPackIds = Array.from(loadedPacks).map(pack => + pack._id.toString() + ) + const packIdsToFetch = _.difference(allPackIds, loadedPackIds) + logger.log( + { project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch }, + 'analysed packs' + ) + if (packIdsToFetch.length === 0) { + return callback() + } + return async.eachLimit( + packIdsToFetch, + 4, + (pack_id, cb) => + MongoAWS.unArchivePack(project_id, doc_id, pack_id, cb), + function(err) { + if (err != null) { + return callback(err) + } + logger.log({ project_id, doc_id }, 'done unarchiving') + return callback() + } + ) + } + ) + }, - fetchPacksIfNeeded(project_id, doc_id, pack_ids, callback) { - let id; - return db.docHistory.find({_id: {$in: ((() => { - const result = []; - for (id of Array.from(pack_ids)) { result.push(ObjectId(id)); - } - return result; - })())}}, {_id:1}, function(err, loadedPacks) { - if (err != null) { return callback(err); } - const allPackIds = ((() => { - const result1 = []; - for (id of Array.from(pack_ids)) { result1.push(id.toString()); - } - return result1; - })()); - const loadedPackIds = (Array.from(loadedPacks).map((pack) => pack._id.toString())); - const packIdsToFetch = _.difference(allPackIds, loadedPackIds); - logger.log({project_id, doc_id, loadedPackIds, allPackIds, packIdsToFetch}, "analysed packs"); - if (packIdsToFetch.length === 0) { return callback(); } - return async.eachLimit(packIdsToFetch, 4, (pack_id, cb) => MongoAWS.unArchivePack(project_id, doc_id, pack_id, cb) - , function(err) { - if (err != null) { return callback(err); } - logger.log({project_id, doc_id}, "done unarchiving"); - return callback(); - }); - }); - }, + // Retrieve all changes across a project - // Retrieve all changes across a project + makeProjectIterator(project_id, before, callback) { + // get all the docHistory Entries + return db.docHistory + .find({ project_id: ObjectId(project_id) }, { pack: false }) + .sort({ 'meta.end_ts': -1 }, function(err, packs) { + let pack + if (err != null) { + return callback(err) + } + const allPacks = [] + const seenIds = {} + for (pack of Array.from(packs)) { + allPacks.push(pack) + seenIds[pack._id] = true + } + return db.docHistoryIndex.find( + { project_id: ObjectId(project_id) }, + function(err, indexes) { + if (err != null) { + return callback(err) + } + for (const index of Array.from(indexes)) { + for (pack of Array.from(index.packs)) { + if (!seenIds[pack._id]) { + pack.project_id = index.project_id + pack.doc_id = index._id + pack.fromIndex = true + allPacks.push(pack) + seenIds[pack._id] = true + } + } + } + return callback( + null, + new ProjectIterator(allPacks, before, PackManager.getPackById) + ) + } + ) + }) + }, - makeProjectIterator(project_id, before, callback) { - // get all the docHistory Entries - return db.docHistory.find({project_id: ObjectId(project_id)},{pack:false}).sort({"meta.end_ts":-1}, function(err, packs) { - let pack; - if (err != null) { return callback(err); } - const allPacks = []; - const seenIds = {}; - for (pack of Array.from(packs)) { - allPacks.push(pack); - seenIds[pack._id] = true; - } - return db.docHistoryIndex.find({project_id: ObjectId(project_id)}, function(err, indexes) { - if (err != null) { return callback(err); } - for (const index of Array.from(indexes)) { - for (pack of Array.from(index.packs)) { - if (!seenIds[pack._id]) { - pack.project_id = index.project_id; - pack.doc_id = index._id; - pack.fromIndex = true; - allPacks.push(pack); - seenIds[pack._id] = true; - } - } - } - return callback(null, new ProjectIterator(allPacks, before, PackManager.getPackById)); - }); - }); - }, + getPackById(project_id, doc_id, pack_id, callback) { + return db.docHistory.findOne({ _id: pack_id }, function(err, pack) { + if (err != null) { + return callback(err) + } + if (pack == null) { + return MongoAWS.unArchivePack(project_id, doc_id, pack_id, callback) + } else if (pack.expiresAt != null && pack.temporary === false) { + // we only need to touch the TTL when listing the changes in the project + // because diffs on individual documents are always done after that + return PackManager.increaseTTL(pack, callback) + // only do this for cached packs, not temporary ones to avoid older packs + // being kept longer than newer ones (which messes up the last update version) + } else { + return callback(null, pack) + } + }) + }, - getPackById(project_id, doc_id, pack_id, callback) { - return db.docHistory.findOne({_id: pack_id}, function(err, pack) { - if (err != null) { return callback(err); } - if ((pack == null)) { - return MongoAWS.unArchivePack(project_id, doc_id, pack_id, callback); - } else if ((pack.expiresAt != null) && (pack.temporary === false)) { - // we only need to touch the TTL when listing the changes in the project - // because diffs on individual documents are always done after that - return PackManager.increaseTTL(pack, callback); - // only do this for cached packs, not temporary ones to avoid older packs - // being kept longer than newer ones (which messes up the last update version) - } else { - return callback(null, pack); - } - }); - }, + increaseTTL(pack, callback) { + if (pack.expiresAt < new Date(Date.now() + 6 * DAYS)) { + // update cache expiry since we are using this pack + return db.docHistory.findAndModify( + { + query: { _id: pack._id }, + update: { $set: { expiresAt: new Date(Date.now() + 7 * DAYS) } } + }, + err => callback(err, pack) + ) + } else { + return callback(null, pack) + } + }, - increaseTTL(pack, callback) { - if (pack.expiresAt < new Date(Date.now() + (6 * DAYS))) { - // update cache expiry since we are using this pack - return db.docHistory.findAndModify({ - query: {_id: pack._id}, - update: {$set: {expiresAt: new Date(Date.now() + (7 * DAYS))}} - }, err => callback(err, pack)); - } else { - return callback(null, pack); - } - }, + // Manage docHistoryIndex collection - // Manage docHistoryIndex collection + getIndex(doc_id, callback) { + return db.docHistoryIndex.findOne( + { _id: ObjectId(doc_id.toString()) }, + callback + ) + }, - getIndex(doc_id, callback) { - return db.docHistoryIndex.findOne({_id:ObjectId(doc_id.toString())}, callback); - }, + getPackFromIndex(doc_id, pack_id, callback) { + return db.docHistoryIndex.findOne( + { _id: ObjectId(doc_id.toString()), 'packs._id': pack_id }, + { 'packs.$': 1 }, + callback + ) + }, - getPackFromIndex(doc_id, pack_id, callback) { - return db.docHistoryIndex.findOne({_id:ObjectId(doc_id.toString()), "packs._id": pack_id}, {"packs.$":1}, callback); - }, + getLastPackFromIndex(doc_id, callback) { + return db.docHistoryIndex.findOne( + { _id: ObjectId(doc_id.toString()) }, + { packs: { $slice: -1 } }, + function(err, indexPack) { + if (err != null) { + return callback(err) + } + if (indexPack == null) { + return callback() + } + return callback(null, indexPack[0]) + } + ) + }, - getLastPackFromIndex(doc_id, callback) { - return db.docHistoryIndex.findOne({_id: ObjectId(doc_id.toString())}, {packs:{$slice:-1}}, function(err, indexPack) { - if (err != null) { return callback(err); } - if ((indexPack == null)) { return callback(); } - return callback(null,indexPack[0]); - }); - }, + getIndexWithKeys(doc_id, callback) { + return PackManager.getIndex(doc_id, function(err, index) { + if (err != null) { + return callback(err) + } + if (index == null) { + return callback() + } + for (const pack of Array.from( + (index != null ? index.packs : undefined) || [] + )) { + index[pack._id] = pack + } + return callback(null, index) + }) + }, - getIndexWithKeys(doc_id, callback) { - return PackManager.getIndex(doc_id, function(err, index) { - if (err != null) { return callback(err); } - if ((index == null)) { return callback(); } - for (const pack of Array.from((index != null ? index.packs : undefined) || [])) { - index[pack._id] = pack; - } - return callback(null, index); - }); - }, + initialiseIndex(project_id, doc_id, callback) { + return PackManager.findCompletedPacks(project_id, doc_id, function( + err, + packs + ) { + // console.log 'err', err, 'packs', packs, packs?.length + if (err != null) { + return callback(err) + } + if (packs == null) { + return callback() + } + return PackManager.insertPacksIntoIndexWithLock( + project_id, + doc_id, + packs, + callback + ) + }) + }, - initialiseIndex(project_id, doc_id, callback) { - return PackManager.findCompletedPacks(project_id, doc_id, function(err, packs) { - // console.log 'err', err, 'packs', packs, packs?.length - if (err != null) { return callback(err); } - if ((packs == null)) { return callback(); } - return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, packs, callback); - }); - }, + updateIndex(project_id, doc_id, callback) { + // find all packs prior to current pack + return PackManager.findUnindexedPacks(project_id, doc_id, function( + err, + newPacks + ) { + if (err != null) { + return callback(err) + } + if (newPacks == null || newPacks.length === 0) { + return callback() + } + return PackManager.insertPacksIntoIndexWithLock( + project_id, + doc_id, + newPacks, + function(err) { + if (err != null) { + return callback(err) + } + logger.log( + { project_id, doc_id, newPacks }, + 'added new packs to index' + ) + return callback() + } + ) + }) + }, - updateIndex(project_id, doc_id, callback) { - // find all packs prior to current pack - return PackManager.findUnindexedPacks(project_id, doc_id, function(err, newPacks) { - if (err != null) { return callback(err); } - if ((newPacks == null) || (newPacks.length === 0)) { return callback(); } - return PackManager.insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, function(err) { - if (err != null) { return callback(err); } - logger.log({project_id, doc_id, newPacks}, "added new packs to index"); - return callback(); - }); - }); - }, + findCompletedPacks(project_id, doc_id, callback) { + const query = { + doc_id: ObjectId(doc_id.toString()), + expiresAt: { $exists: false } + } + return db.docHistory + .find(query, { pack: false }) + .sort({ v: 1 }, function(err, packs) { + if (err != null) { + return callback(err) + } + if (packs == null) { + return callback() + } + if (!(packs != null ? packs.length : undefined)) { + return callback() + } + const last = packs.pop() // discard the last pack, if it's still in progress + if (last.finalised) { + packs.push(last) + } // it's finalised so we push it back to archive it + return callback(null, packs) + }) + }, - findCompletedPacks(project_id, doc_id, callback) { - const query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} }; - return db.docHistory.find(query, {pack:false}).sort({v:1}, function(err, packs) { - if (err != null) { return callback(err); } - if ((packs == null)) { return callback(); } - if (!(packs != null ? packs.length : undefined)) { return callback(); } - const last = packs.pop(); // discard the last pack, if it's still in progress - if (last.finalised) { packs.push(last); } // it's finalised so we push it back to archive it - return callback(null, packs); - }); - }, + findPacks(project_id, doc_id, callback) { + const query = { + doc_id: ObjectId(doc_id.toString()), + expiresAt: { $exists: false } + } + return db.docHistory + .find(query, { pack: false }) + .sort({ v: 1 }, function(err, packs) { + if (err != null) { + return callback(err) + } + if (packs == null) { + return callback() + } + if (!(packs != null ? packs.length : undefined)) { + return callback() + } + return callback(null, packs) + }) + }, - findPacks(project_id, doc_id, callback) { - const query = { doc_id: ObjectId(doc_id.toString()), expiresAt: {$exists:false} }; - return db.docHistory.find(query, {pack:false}).sort({v:1}, function(err, packs) { - if (err != null) { return callback(err); } - if ((packs == null)) { return callback(); } - if (!(packs != null ? packs.length : undefined)) { return callback(); } - return callback(null, packs); - }); - }, + findUnindexedPacks(project_id, doc_id, callback) { + return PackManager.getIndexWithKeys(doc_id, function(err, indexResult) { + if (err != null) { + return callback(err) + } + return PackManager.findCompletedPacks(project_id, doc_id, function( + err, + historyPacks + ) { + let pack + if (err != null) { + return callback(err) + } + if (historyPacks == null) { + return callback() + } + // select only the new packs not already in the index + let newPacks = (() => { + const result = [] + for (pack of Array.from(historyPacks)) { + if ( + (indexResult != null ? indexResult[pack._id] : undefined) == null + ) { + result.push(pack) + } + } + return result + })() + newPacks = (() => { + const result1 = [] + for (pack of Array.from(newPacks)) { + result1.push( + _.omit( + pack, + 'doc_id', + 'project_id', + 'n', + 'sz', + 'last_checked', + 'finalised' + ) + ) + } + return result1 + })() + if (newPacks.length) { + logger.log( + { project_id, doc_id, n: newPacks.length }, + 'found new packs' + ) + } + return callback(null, newPacks) + }) + }) + }, - findUnindexedPacks(project_id, doc_id, callback) { - return PackManager.getIndexWithKeys(doc_id, function(err, indexResult) { - if (err != null) { return callback(err); } - return PackManager.findCompletedPacks(project_id, doc_id, function(err, historyPacks) { - let pack; - if (err != null) { return callback(err); } - if ((historyPacks == null)) { return callback(); } - // select only the new packs not already in the index - let newPacks = ((() => { - const result = []; - for (pack of Array.from(historyPacks)) { if (((indexResult != null ? indexResult[pack._id] : undefined) == null)) { - result.push(pack); - } - } - return result; - })()); - newPacks = ((() => { - const result1 = []; - for (pack of Array.from(newPacks)) { result1.push(_.omit(pack, 'doc_id', 'project_id', 'n', 'sz', 'last_checked', 'finalised')); - } - return result1; - })()); - if (newPacks.length) { - logger.log({project_id, doc_id, n: newPacks.length}, "found new packs"); - } - return callback(null, newPacks); - }); - }); - }, + insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, callback) { + return LockManager.runWithLock( + keys.historyIndexLock({ doc_id }), + releaseLock => + PackManager._insertPacksIntoIndex( + project_id, + doc_id, + newPacks, + releaseLock + ), + callback + ) + }, - insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, callback) { - return LockManager.runWithLock( - keys.historyIndexLock({doc_id}), - releaseLock => PackManager._insertPacksIntoIndex(project_id, doc_id, newPacks, releaseLock), - callback - ); - }, + _insertPacksIntoIndex(project_id, doc_id, newPacks, callback) { + return db.docHistoryIndex.findAndModify( + { + query: { _id: ObjectId(doc_id.toString()) }, + update: { + $setOnInsert: { project_id: ObjectId(project_id.toString()) }, + $push: { + packs: { $each: newPacks, $sort: { v: 1 } } + } + }, + upsert: true + }, + callback + ) + }, - _insertPacksIntoIndex(project_id, doc_id, newPacks, callback) { - return db.docHistoryIndex.findAndModify({ - query: {_id:ObjectId(doc_id.toString())}, - update: { - $setOnInsert: { project_id: ObjectId(project_id.toString()) - }, - $push: { - packs: {$each: newPacks, $sort: {v: 1}} - } - }, - upsert: true - }, callback); - }, + // Archiving packs to S3 - // Archiving packs to S3 + archivePack(project_id, doc_id, pack_id, callback) { + const clearFlagOnError = function(err, cb) { + if (err != null) { + // clear the inS3 flag on error + return PackManager.clearPackAsArchiveInProgress( + project_id, + doc_id, + pack_id, + function(err2) { + if (err2 != null) { + return cb(err2) + } + return cb(err) + } + ) + } else { + return cb() + } + } + return async.series( + [ + cb => + PackManager.checkArchiveNotInProgress( + project_id, + doc_id, + pack_id, + cb + ), + cb => + PackManager.markPackAsArchiveInProgress( + project_id, + doc_id, + pack_id, + cb + ), + cb => + MongoAWS.archivePack(project_id, doc_id, pack_id, err => + clearFlagOnError(err, cb) + ), + cb => + PackManager.checkArchivedPack(project_id, doc_id, pack_id, err => + clearFlagOnError(err, cb) + ), + cb => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), + cb => + PackManager.setTTLOnArchivedPack( + project_id, + doc_id, + pack_id, + callback + ) + ], + callback + ) + }, - archivePack(project_id, doc_id, pack_id, callback) { - const clearFlagOnError = function(err, cb) { - if (err != null) { // clear the inS3 flag on error - return PackManager.clearPackAsArchiveInProgress(project_id, doc_id, pack_id, function(err2) { - if (err2 != null) { return cb(err2); } - return cb(err); - }); - } else { - return cb(); - } - }; - return async.series([ - cb => PackManager.checkArchiveNotInProgress(project_id, doc_id, pack_id, cb), - cb => PackManager.markPackAsArchiveInProgress(project_id, doc_id, pack_id, cb), - cb => - MongoAWS.archivePack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb)) - , - cb => - PackManager.checkArchivedPack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb)) - , - cb => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), - cb => PackManager.setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) - ], callback); - }, + checkArchivedPack(project_id, doc_id, pack_id, callback) { + return db.docHistory.findOne({ _id: pack_id }, function(err, pack) { + if (err != null) { + return callback(err) + } + if (pack == null) { + return callback(new Error('pack not found')) + } + return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function( + err, + result + ) { + delete result.last_checked + delete pack.last_checked + // need to compare ids as ObjectIds with .equals() + for (const key of ['_id', 'project_id', 'doc_id']) { + if (result[key].equals(pack[key])) { + result[key] = pack[key] + } + } + for (let i = 0; i < result.pack.length; i++) { + const op = result.pack[i] + if (op._id != null && op._id.equals(pack.pack[i]._id)) { + op._id = pack.pack[i]._id + } + } + if (_.isEqual(pack, result)) { + return callback() + } else { + logger.err( + { + pack, + result, + jsondiff: JSON.stringify(pack) === JSON.stringify(result) + }, + 'difference when comparing packs' + ) + return callback( + new Error('pack retrieved from s3 does not match pack in mongo') + ) + } + }) + }) + }, + // Extra methods to test archive/unarchive for a doc_id + pushOldPacks(project_id, doc_id, callback) { + return PackManager.findPacks(project_id, doc_id, function(err, packs) { + if (err != null) { + return callback(err) + } + if (!(packs != null ? packs.length : undefined)) { + return callback() + } + return PackManager.processOldPack( + project_id, + doc_id, + packs[0]._id, + callback + ) + }) + }, - checkArchivedPack(project_id, doc_id, pack_id, callback) { - return db.docHistory.findOne({_id: pack_id}, function(err, pack) { - if (err != null) { return callback(err); } - if ((pack == null)) { return callback(new Error("pack not found")); } - return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function(err, result) { - delete result.last_checked; - delete pack.last_checked; - // need to compare ids as ObjectIds with .equals() - for (const key of ['_id', 'project_id', 'doc_id']) { - if (result[key].equals(pack[key])) { result[key] = pack[key]; } - } - for (let i = 0; i < result.pack.length; i++) { - const op = result.pack[i]; - if ((op._id != null) && op._id.equals(pack.pack[i]._id)) { op._id = pack.pack[i]._id; } - } - if (_.isEqual(pack, result)) { - return callback(); - } else { - logger.err({pack, result, jsondiff: JSON.stringify(pack) === JSON.stringify(result)}, "difference when comparing packs"); - return callback(new Error("pack retrieved from s3 does not match pack in mongo")); - } - }); - }); - }, - // Extra methods to test archive/unarchive for a doc_id + pullOldPacks(project_id, doc_id, callback) { + return PackManager.loadPacksByVersionRange( + project_id, + doc_id, + null, + null, + callback + ) + }, - pushOldPacks(project_id, doc_id, callback) { - return PackManager.findPacks(project_id, doc_id, function(err, packs) { - if (err != null) { return callback(err); } - if (!(packs != null ? packs.length : undefined)) { return callback(); } - return PackManager.processOldPack(project_id, doc_id, packs[0]._id, callback); - }); - }, + // Processing old packs via worker - pullOldPacks(project_id, doc_id, callback) { - return PackManager.loadPacksByVersionRange(project_id, doc_id, null, null, callback); - }, + processOldPack(project_id, doc_id, pack_id, callback) { + const markAsChecked = err => + PackManager.markPackAsChecked(project_id, doc_id, pack_id, function( + err2 + ) { + if (err2 != null) { + return callback(err2) + } + return callback(err) + }) + logger.log({ project_id, doc_id }, 'processing old packs') + return db.docHistory.findOne({ _id: pack_id }, function(err, pack) { + if (err != null) { + return markAsChecked(err) + } + if (pack == null) { + return markAsChecked() + } + if (pack.expiresAt != null) { + return callback() + } // return directly + return PackManager.finaliseIfNeeded( + project_id, + doc_id, + pack._id, + pack, + function(err) { + if (err != null) { + return markAsChecked(err) + } + return PackManager.updateIndexIfNeeded(project_id, doc_id, function( + err + ) { + if (err != null) { + return markAsChecked(err) + } + return PackManager.findUnarchivedPacks(project_id, doc_id, function( + err, + unarchivedPacks + ) { + if (err != null) { + return markAsChecked(err) + } + if ( + !(unarchivedPacks != null ? unarchivedPacks.length : undefined) + ) { + logger.log({ project_id, doc_id }, 'no packs need archiving') + return markAsChecked() + } + return async.eachSeries( + unarchivedPacks, + (pack, cb) => + PackManager.archivePack(project_id, doc_id, pack._id, cb), + function(err) { + if (err != null) { + return markAsChecked(err) + } + logger.log({ project_id, doc_id }, 'done processing') + return markAsChecked() + } + ) + }) + }) + } + ) + }) + }, + finaliseIfNeeded(project_id, doc_id, pack_id, pack, callback) { + const sz = pack.sz / (1024 * 1024) // in fractions of a megabyte + const n = pack.n / 1024 // in fraction of 1024 ops + const age = (Date.now() - pack.meta.end_ts) / DAYS + if (age < 30) { + // always keep if less than 1 month old + logger.log({ project_id, doc_id, pack_id, age }, 'less than 30 days old') + return callback() + } + // compute an archiving threshold which decreases for each month of age + const archive_threshold = 30 / age + if (sz > archive_threshold || n > archive_threshold || age > 90) { + logger.log( + { project_id, doc_id, pack_id, age, archive_threshold, sz, n }, + 'meets archive threshold' + ) + return PackManager.markPackAsFinalisedWithLock( + project_id, + doc_id, + pack_id, + callback + ) + } else { + logger.log( + { project_id, doc_id, pack_id, age, archive_threshold, sz, n }, + 'does not meet archive threshold' + ) + return callback() + } + }, - // Processing old packs via worker + markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback) { + return LockManager.runWithLock( + keys.historyLock({ doc_id }), + releaseLock => + PackManager._markPackAsFinalised( + project_id, + doc_id, + pack_id, + releaseLock + ), + callback + ) + }, - processOldPack(project_id, doc_id, pack_id, callback) { - const markAsChecked = err => - PackManager.markPackAsChecked(project_id, doc_id, pack_id, function(err2) { - if (err2 != null) { return callback(err2); } - return callback(err); - }) - ; - logger.log({project_id, doc_id}, "processing old packs"); - return db.docHistory.findOne({_id:pack_id}, function(err, pack) { - if (err != null) { return markAsChecked(err); } - if ((pack == null)) { return markAsChecked(); } - if (pack.expiresAt != null) { return callback(); } // return directly - return PackManager.finaliseIfNeeded(project_id, doc_id, pack._id, pack, function(err) { - if (err != null) { return markAsChecked(err); } - return PackManager.updateIndexIfNeeded(project_id, doc_id, function(err) { - if (err != null) { return markAsChecked(err); } - return PackManager.findUnarchivedPacks(project_id, doc_id, function(err, unarchivedPacks) { - if (err != null) { return markAsChecked(err); } - if (!(unarchivedPacks != null ? unarchivedPacks.length : undefined)) { - logger.log({project_id, doc_id}, "no packs need archiving"); - return markAsChecked(); - } - return async.eachSeries(unarchivedPacks, (pack, cb) => PackManager.archivePack(project_id, doc_id, pack._id, cb) - , function(err) { - if (err != null) { return markAsChecked(err); } - logger.log({project_id, doc_id}, "done processing"); - return markAsChecked(); - }); - }); - }); - }); - }); - }, + _markPackAsFinalised(project_id, doc_id, pack_id, callback) { + logger.log({ project_id, doc_id, pack_id }, 'marking pack as finalised') + return db.docHistory.findAndModify( + { + query: { _id: pack_id }, + update: { $set: { finalised: true } } + }, + callback + ) + }, - finaliseIfNeeded(project_id, doc_id, pack_id, pack, callback) { - const sz = pack.sz / (1024 * 1024); // in fractions of a megabyte - const n = pack.n / 1024; // in fraction of 1024 ops - const age = (Date.now() - pack.meta.end_ts) / DAYS; - if (age < 30) { // always keep if less than 1 month old - logger.log({project_id, doc_id, pack_id, age}, "less than 30 days old"); - return callback(); - } - // compute an archiving threshold which decreases for each month of age - const archive_threshold = 30 / age; - if ((sz > archive_threshold) || (n > archive_threshold) || (age > 90)) { - logger.log({project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "meets archive threshold"); - return PackManager.markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback); - } else { - logger.log({project_id, doc_id, pack_id, age, archive_threshold, sz, n}, "does not meet archive threshold"); - return callback(); - } - }, + updateIndexIfNeeded(project_id, doc_id, callback) { + logger.log({ project_id, doc_id }, 'archiving old packs') + return PackManager.getIndexWithKeys(doc_id, function(err, index) { + if (err != null) { + return callback(err) + } + if (index == null) { + return PackManager.initialiseIndex(project_id, doc_id, callback) + } else { + return PackManager.updateIndex(project_id, doc_id, callback) + } + }) + }, - markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback) { - return LockManager.runWithLock( - keys.historyLock({doc_id}), - releaseLock => PackManager._markPackAsFinalised(project_id, doc_id, pack_id, releaseLock), - callback - ); - }, + markPackAsChecked(project_id, doc_id, pack_id, callback) { + logger.log({ project_id, doc_id, pack_id }, 'marking pack as checked') + return db.docHistory.findAndModify( + { + query: { _id: pack_id }, + update: { $currentDate: { last_checked: true } } + }, + callback + ) + }, - _markPackAsFinalised(project_id, doc_id, pack_id, callback) { - logger.log({project_id, doc_id, pack_id}, "marking pack as finalised"); - return db.docHistory.findAndModify({ - query: {_id: pack_id}, - update: {$set: {finalised: true}} - }, callback); - }, + findUnarchivedPacks(project_id, doc_id, callback) { + return PackManager.getIndex(doc_id, function(err, indexResult) { + if (err != null) { + return callback(err) + } + const indexPacks = + (indexResult != null ? indexResult.packs : undefined) || [] + const unArchivedPacks = (() => { + const result = [] + for (const pack of Array.from(indexPacks)) { + if (pack.inS3 == null) { + result.push(pack) + } + } + return result + })() + if (unArchivedPacks.length) { + logger.log( + { project_id, doc_id, n: unArchivedPacks.length }, + 'find unarchived packs' + ) + } + return callback(null, unArchivedPacks) + }) + }, + // Archive locking flags - updateIndexIfNeeded(project_id, doc_id, callback) { - logger.log({project_id, doc_id}, "archiving old packs"); - return PackManager.getIndexWithKeys(doc_id, function(err, index) { - if (err != null) { return callback(err); } - if ((index == null)) { - return PackManager.initialiseIndex(project_id, doc_id, callback); - } else { - return PackManager.updateIndex(project_id, doc_id, callback); - } - }); - }, + checkArchiveNotInProgress(project_id, doc_id, pack_id, callback) { + logger.log( + { project_id, doc_id, pack_id }, + 'checking if archive in progress' + ) + return PackManager.getPackFromIndex(doc_id, pack_id, function(err, result) { + if (err != null) { + return callback(err) + } + if (result == null) { + return callback(new Error('pack not found in index')) + } + if (result.inS3) { + return callback(new Error('pack archiving already done')) + } else if (result.inS3 != null) { + return callback(new Error('pack archiving already in progress')) + } else { + return callback() + } + }) + }, - markPackAsChecked(project_id, doc_id, pack_id, callback) { - logger.log({project_id, doc_id, pack_id}, "marking pack as checked"); - return db.docHistory.findAndModify({ - query: {_id: pack_id}, - update: {$currentDate: {"last_checked":true}} - }, callback); - }, + markPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { + logger.log( + { project_id, doc_id }, + 'marking pack as archive in progress status' + ) + return db.docHistoryIndex.findAndModify( + { + query: { + _id: ObjectId(doc_id.toString()), + packs: { $elemMatch: { _id: pack_id, inS3: { $exists: false } } } + }, + fields: { 'packs.$': 1 }, + update: { $set: { 'packs.$.inS3': false } } + }, + function(err, result) { + if (err != null) { + return callback(err) + } + if (result == null) { + return callback(new Error('archive is already in progress')) + } + logger.log( + { project_id, doc_id, pack_id }, + 'marked as archive in progress' + ) + return callback() + } + ) + }, - findUnarchivedPacks(project_id, doc_id, callback) { - return PackManager.getIndex(doc_id, function(err, indexResult) { - if (err != null) { return callback(err); } - const indexPacks = (indexResult != null ? indexResult.packs : undefined) || []; - const unArchivedPacks = ((() => { - const result = []; - for (const pack of Array.from(indexPacks)) { if ((pack.inS3 == null)) { - result.push(pack); - } - } - return result; - })()); - if (unArchivedPacks.length) { - logger.log({project_id, doc_id, n: unArchivedPacks.length}, "find unarchived packs"); - } - return callback(null, unArchivedPacks); - }); - }, + clearPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { + logger.log( + { project_id, doc_id, pack_id }, + 'clearing as archive in progress' + ) + return db.docHistoryIndex.findAndModify( + { + query: { + _id: ObjectId(doc_id.toString()), + packs: { $elemMatch: { _id: pack_id, inS3: false } } + }, + fields: { 'packs.$': 1 }, + update: { $unset: { 'packs.$.inS3': true } } + }, + callback + ) + }, - // Archive locking flags - - checkArchiveNotInProgress(project_id, doc_id, pack_id, callback) { - logger.log({project_id, doc_id, pack_id}, "checking if archive in progress"); - return PackManager.getPackFromIndex(doc_id, pack_id, function(err, result) { - if (err != null) { return callback(err); } - if ((result == null)) { return callback(new Error("pack not found in index")); } - if (result.inS3) { - return callback(new Error("pack archiving already done")); - } else if (result.inS3 != null) { - return callback(new Error("pack archiving already in progress")); - } else { - return callback(); - } - }); - }, - - markPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { - logger.log({project_id, doc_id}, "marking pack as archive in progress status"); - return db.docHistoryIndex.findAndModify({ - query: {_id:ObjectId(doc_id.toString()), packs: {$elemMatch: {"_id": pack_id, inS3: {$exists:false}}}}, - fields: { "packs.$": 1 }, - update: {$set: {"packs.$.inS3":false}} - }, function(err, result) { - if (err != null) { return callback(err); } - if ((result == null)) { return callback(new Error("archive is already in progress")); } - logger.log({project_id, doc_id, pack_id}, "marked as archive in progress"); - return callback(); - }); - }, - - clearPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { - logger.log({project_id, doc_id, pack_id}, "clearing as archive in progress"); - return db.docHistoryIndex.findAndModify({ - query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}}, - fields: { "packs.$": 1 }, - update: {$unset: {"packs.$.inS3":true}} - }, callback); - }, - - markPackAsArchived(project_id, doc_id, pack_id, callback) { - logger.log({project_id, doc_id, pack_id}, "marking pack as archived"); - return db.docHistoryIndex.findAndModify({ - query: {_id:ObjectId(doc_id.toString()), "packs" : {$elemMatch: {"_id": pack_id, inS3: false}}}, - fields: { "packs.$": 1 }, - update: {$set: {"packs.$.inS3":true}} - }, function(err, result) { - if (err != null) { return callback(err); } - if ((result == null)) { return callback(new Error("archive is not marked as progress")); } - logger.log({project_id, doc_id, pack_id}, "marked as archived"); - return callback(); - }); - }, - - setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) { - return db.docHistory.findAndModify({ - query: {_id: pack_id}, - update: {$set: {expiresAt: new Date(Date.now() + (1*DAYS))}} - }, function(err) { - logger.log({project_id, doc_id, pack_id}, "set expiry on pack"); - return callback(); - }); - } -}); + markPackAsArchived(project_id, doc_id, pack_id, callback) { + logger.log({ project_id, doc_id, pack_id }, 'marking pack as archived') + return db.docHistoryIndex.findAndModify( + { + query: { + _id: ObjectId(doc_id.toString()), + packs: { $elemMatch: { _id: pack_id, inS3: false } } + }, + fields: { 'packs.$': 1 }, + update: { $set: { 'packs.$.inS3': true } } + }, + function(err, result) { + if (err != null) { + return callback(err) + } + if (result == null) { + return callback(new Error('archive is not marked as progress')) + } + logger.log({ project_id, doc_id, pack_id }, 'marked as archived') + return callback() + } + ) + }, + setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) { + return db.docHistory.findAndModify( + { + query: { _id: pack_id }, + update: { $set: { expiresAt: new Date(Date.now() + 1 * DAYS) } } + }, + function(err) { + logger.log({ project_id, doc_id, pack_id }, 'set expiry on pack') + return callback() + } + ) + } +} // _getOneDayInFutureWithRandomDelay: -> // thirtyMins = 1000 * 60 * 30 diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index ecd60a7ccd..f24ddf5fce 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -13,177 +13,199 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let LIMIT, pending; -let project_id, doc_id; -const Settings = require("settings-sharelatex"); -const async = require("async"); -const _ = require("underscore"); -const {db, ObjectId, BSON} = require("./mongojs"); -const fs = require("fs"); -const Metrics = require("metrics-sharelatex"); -Metrics.initialize("track-changes"); -const logger = require("logger-sharelatex"); -logger.initialize("track-changes-packworker"); +let LIMIT, pending +let project_id, doc_id +const Settings = require('settings-sharelatex') +const async = require('async') +const _ = require('underscore') +const { db, ObjectId, BSON } = require('./mongojs') +const fs = require('fs') +const Metrics = require('metrics-sharelatex') +Metrics.initialize('track-changes') +const logger = require('logger-sharelatex') +logger.initialize('track-changes-packworker') if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) { - logger.initializeErrorReporting(Settings.sentry.dsn); + logger.initializeErrorReporting(Settings.sentry.dsn) } -const DAYS = 24 * 3600 * 1000; +const DAYS = 24 * 3600 * 1000 -const LockManager = require("./LockManager"); -const PackManager = require("./PackManager"); +const LockManager = require('./LockManager') +const PackManager = require('./PackManager') // this worker script is forked by the main process to look for // document histories which can be archived -const source = process.argv[2]; -const DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000; -const TIMEOUT = Number(process.argv[4]) || (30*60*1000); -let COUNT = 0; // number processed -let TOTAL = 0; // total number to process +const source = process.argv[2] +const DOCUMENT_PACK_DELAY = Number(process.argv[3]) || 1000 +const TIMEOUT = Number(process.argv[4]) || 30 * 60 * 1000 +let COUNT = 0 // number processed +let TOTAL = 0 // total number to process if (!source.match(/^[0-9]+$/)) { - const file = fs.readFileSync(source); - const result = (() => { - const result1 = []; - for (const line of Array.from(file.toString().split('\n'))) { - [project_id, doc_id] = Array.from(line.split(' ')); - result1.push({doc_id, project_id}); - } - return result1; - })(); - pending = _.filter(result, row => __guard__(row != null ? row.doc_id : undefined, x => x.match(/^[a-f0-9]{24}$/))); + const file = fs.readFileSync(source) + const result = (() => { + const result1 = [] + for (const line of Array.from(file.toString().split('\n'))) { + ;[project_id, doc_id] = Array.from(line.split(' ')) + result1.push({ doc_id, project_id }) + } + return result1 + })() + pending = _.filter(result, row => + __guard__(row != null ? row.doc_id : undefined, x => + x.match(/^[a-f0-9]{24}$/) + ) + ) } else { - LIMIT = Number(process.argv[2]) || 1000; + LIMIT = Number(process.argv[2]) || 1000 } -let shutDownRequested = false; +let shutDownRequested = false const shutDownTimer = setTimeout(function() { - logger.log("pack timed out, requesting shutdown"); - // start the shutdown on the next pack - shutDownRequested = true; - // do a hard shutdown after a further 5 minutes - const hardTimeout = setTimeout(function() { - logger.error("HARD TIMEOUT in pack archive worker"); - return process.exit(); - } - , 5*60*1000); - return hardTimeout.unref(); -} -, TIMEOUT); + logger.log('pack timed out, requesting shutdown') + // start the shutdown on the next pack + shutDownRequested = true + // do a hard shutdown after a further 5 minutes + const hardTimeout = setTimeout(function() { + logger.error('HARD TIMEOUT in pack archive worker') + return process.exit() + }, 5 * 60 * 1000) + return hardTimeout.unref() +}, TIMEOUT) -logger.log(`checking for updates, limit=${LIMIT}, delay=${DOCUMENT_PACK_DELAY}, timeout=${TIMEOUT}`); +logger.log( + `checking for updates, limit=${LIMIT}, delay=${DOCUMENT_PACK_DELAY}, timeout=${TIMEOUT}` +) // work around for https://github.com/mafintosh/mongojs/issues/224 -db.close = function(callback) { - return this._getServer(function(err, server) { - if (err != null) { return callback(err); } - server = (server.destroy != null) ? server : server.topology; - server.destroy(true, true); - return callback(); - }); -}; +db.close = function(callback) { + return this._getServer(function(err, server) { + if (err != null) { + return callback(err) + } + server = server.destroy != null ? server : server.topology + server.destroy(true, true) + return callback() + }) +} const finish = function() { - if (shutDownTimer != null) { - logger.log('cancelling timeout'); - clearTimeout(shutDownTimer); - } - logger.log('closing db'); - return db.close(function() { - logger.log('closing LockManager Redis Connection'); - return LockManager.close(function() { - logger.log({processedCount: COUNT, allCount: TOTAL}, 'ready to exit from pack archive worker'); - const hardTimeout = setTimeout(function() { - logger.error('hard exit from pack archive worker'); - return process.exit(1); - } - , 5*1000); - return hardTimeout.unref(); - }); - }); -}; + if (shutDownTimer != null) { + logger.log('cancelling timeout') + clearTimeout(shutDownTimer) + } + logger.log('closing db') + return db.close(function() { + logger.log('closing LockManager Redis Connection') + return LockManager.close(function() { + logger.log( + { processedCount: COUNT, allCount: TOTAL }, + 'ready to exit from pack archive worker' + ) + const hardTimeout = setTimeout(function() { + logger.error('hard exit from pack archive worker') + return process.exit(1) + }, 5 * 1000) + return hardTimeout.unref() + }) + }) +} -process.on('exit', code => logger.log({code}, 'pack archive worker exited')); +process.on('exit', code => logger.log({ code }, 'pack archive worker exited')) const processUpdates = pending => - async.eachSeries(pending, function(result, callback) { - let _id; - ({_id, project_id, doc_id} = result); - COUNT++; - logger.log({project_id, doc_id}, `processing ${COUNT}/${TOTAL}`); - if ((project_id == null) || (doc_id == null)) { - logger.log({project_id, doc_id}, "skipping pack, missing project/doc id"); - return callback(); - } - const handler = function(err, result) { - if ((err != null) && (err.code === "InternalError") && err.retryable) { - logger.warn({err, result}, "ignoring S3 error in pack archive worker"); - // Ignore any s3 errors due to random problems - err = null; - } - if (err != null) { - logger.error({err, result}, "error in pack archive worker"); - return callback(err); - } - if (shutDownRequested) { - logger.warn("shutting down pack archive worker"); - return callback(new Error("shutdown")); - } - return setTimeout(() => callback(err, result) - , DOCUMENT_PACK_DELAY); - }; - if ((_id == null)) { - return PackManager.pushOldPacks(project_id, doc_id, handler); - } else { - return PackManager.processOldPack(project_id, doc_id, _id, handler); - } - } - , function(err, results) { - if ((err != null) && (err.message !== "shutdown")) { - logger.error({err}, 'error in pack archive worker processUpdates'); - } - return finish(); - }) -; - + async.eachSeries( + pending, + function(result, callback) { + let _id + ;({ _id, project_id, doc_id } = result) + COUNT++ + logger.log({ project_id, doc_id }, `processing ${COUNT}/${TOTAL}`) + if (project_id == null || doc_id == null) { + logger.log( + { project_id, doc_id }, + 'skipping pack, missing project/doc id' + ) + return callback() + } + const handler = function(err, result) { + if (err != null && err.code === 'InternalError' && err.retryable) { + logger.warn( + { err, result }, + 'ignoring S3 error in pack archive worker' + ) + // Ignore any s3 errors due to random problems + err = null + } + if (err != null) { + logger.error({ err, result }, 'error in pack archive worker') + return callback(err) + } + if (shutDownRequested) { + logger.warn('shutting down pack archive worker') + return callback(new Error('shutdown')) + } + return setTimeout(() => callback(err, result), DOCUMENT_PACK_DELAY) + } + if (_id == null) { + return PackManager.pushOldPacks(project_id, doc_id, handler) + } else { + return PackManager.processOldPack(project_id, doc_id, _id, handler) + } + }, + function(err, results) { + if (err != null && err.message !== 'shutdown') { + logger.error({ err }, 'error in pack archive worker processUpdates') + } + return finish() + } + ) // find the packs which can be archived -const ObjectIdFromDate = function(date) { - const id = Math.floor(date.getTime() / 1000).toString(16) + "0000000000000000"; - return ObjectId(id); -}; +const ObjectIdFromDate = function(date) { + const id = Math.floor(date.getTime() / 1000).toString(16) + '0000000000000000' + return ObjectId(id) +} // new approach, two passes // find packs to be marked as finalised:true, those which have a newer pack present // then only consider finalised:true packs for archiving if (pending != null) { - logger.log(`got ${pending.length} entries from ${source}`); - processUpdates(pending); + logger.log(`got ${pending.length} entries from ${source}`) + processUpdates(pending) } else { - const oneWeekAgo = new Date(Date.now() - (7 * DAYS)); - db.docHistory.find({ - expiresAt: {$exists: false}, - project_id: {$exists: true}, - v_end: {$exists: true}, - _id: {$lt: ObjectIdFromDate(oneWeekAgo)}, - last_checked: {$lt: oneWeekAgo} - }, {_id:1, doc_id:1, project_id:1}).sort({ - last_checked:1 - }).limit(LIMIT, function(err, results) { - if (err != null) { - logger.log({err}, 'error checking for updates'); - finish(); - return; - } - pending = _.uniq(results, false, result => result.doc_id.toString()); - TOTAL = pending.length; - logger.log(`found ${TOTAL} documents to archive`); - return processUpdates(pending); - }); + const oneWeekAgo = new Date(Date.now() - 7 * DAYS) + db.docHistory + .find( + { + expiresAt: { $exists: false }, + project_id: { $exists: true }, + v_end: { $exists: true }, + _id: { $lt: ObjectIdFromDate(oneWeekAgo) }, + last_checked: { $lt: oneWeekAgo } + }, + { _id: 1, doc_id: 1, project_id: 1 } + ) + .sort({ + last_checked: 1 + }) + .limit(LIMIT, function(err, results) { + if (err != null) { + logger.log({ err }, 'error checking for updates') + finish() + return + } + pending = _.uniq(results, false, result => result.doc_id.toString()) + TOTAL = pending.length + logger.log(`found ${TOTAL} documents to archive`) + return processUpdates(pending) + }) } function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/app/js/ProjectIterator.js b/services/track-changes/app/js/ProjectIterator.js index 25afa95737..589c68c241 100644 --- a/services/track-changes/app/js/ProjectIterator.js +++ b/services/track-changes/app/js/ProjectIterator.js @@ -11,80 +11,99 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let ProjectIterator; -const Heap = require("heap"); +let ProjectIterator +const Heap = require('heap') -module.exports = (ProjectIterator = +module.exports = ProjectIterator = ProjectIterator = class ProjectIterator { + constructor(packs, before, getPackByIdFn) { + this.before = before + this.getPackByIdFn = getPackByIdFn + const byEndTs = (a, b) => + b.meta.end_ts - a.meta.end_ts || a.fromIndex - b.fromIndex + this.packs = packs.slice().sort(byEndTs) + this.queue = new Heap(byEndTs) + } - (ProjectIterator = class ProjectIterator { - constructor(packs, before, getPackByIdFn) { - this.before = before; - this.getPackByIdFn = getPackByIdFn; - const byEndTs = (a,b) => (b.meta.end_ts - a.meta.end_ts) || (a.fromIndex - b.fromIndex); - this.packs = packs.slice().sort(byEndTs); - this.queue = new Heap(byEndTs); - } + next(callback) { + // what's up next + // console.log ">>> top item", iterator.packs[0] + const iterator = this + const { before } = this + const { queue } = iterator + const opsToReturn = [] + let nextPack = iterator.packs[0] + let lowWaterMark = + (nextPack != null ? nextPack.meta.end_ts : undefined) || 0 + let nextItem = queue.peek() - next(callback) { - // what's up next - // console.log ">>> top item", iterator.packs[0] - const iterator = this; - const { before } = this; - const { queue } = iterator; - const opsToReturn = []; - let nextPack = iterator.packs[0]; - let lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0; - let nextItem = queue.peek(); + // console.log "queue empty?", queue.empty() + // console.log "nextItem", nextItem + // console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts + // console.log "lowWaterMark", lowWaterMark - // console.log "queue empty?", queue.empty() - // console.log "nextItem", nextItem - // console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts - // console.log "lowWaterMark", lowWaterMark + while ( + before != null && + (nextPack != null ? nextPack.meta.start_ts : undefined) > before + ) { + // discard pack that is outside range + iterator.packs.shift() + nextPack = iterator.packs[0] + lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0 + } - while ((before != null) && ((nextPack != null ? nextPack.meta.start_ts : undefined) > before)) { - // discard pack that is outside range - iterator.packs.shift(); - nextPack = iterator.packs[0]; - lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0; - } + if ( + (queue.empty() || + (nextItem != null ? nextItem.meta.end_ts : undefined) <= + lowWaterMark) && + nextPack != null + ) { + // retrieve the next pack and populate the queue + return this.getPackByIdFn( + nextPack.project_id, + nextPack.doc_id, + nextPack._id, + function(err, pack) { + if (err != null) { + return callback(err) + } + iterator.packs.shift() // have now retrieved this pack, remove it + // console.log "got pack", pack + for (const op of Array.from(pack.pack)) { + // console.log "adding op", op + if (before == null || op.meta.end_ts < before) { + op.doc_id = nextPack.doc_id + op.project_id = nextPack.project_id + queue.push(op) + } + } + // now try again + return iterator.next(callback) + } + ) + } - if ((queue.empty() || ((nextItem != null ? nextItem.meta.end_ts : undefined) <= lowWaterMark)) && (nextPack != null)) { - // retrieve the next pack and populate the queue - return this.getPackByIdFn(nextPack.project_id, nextPack.doc_id, nextPack._id, function(err, pack) { - if (err != null) { return callback(err); } - iterator.packs.shift(); // have now retrieved this pack, remove it - // console.log "got pack", pack - for (const op of Array.from(pack.pack)) { - // console.log "adding op", op - if ((before == null) || (op.meta.end_ts < before)) { - op.doc_id = nextPack.doc_id; - op.project_id = nextPack.project_id; - queue.push(op); - } - } - // now try again - return iterator.next(callback); - }); - } + // console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark + while ( + nextItem != null && + (nextItem != null ? nextItem.meta.end_ts : undefined) > lowWaterMark + ) { + opsToReturn.push(nextItem) + queue.pop() + nextItem = queue.peek() + } - // console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark - while ((nextItem != null) && ((nextItem != null ? nextItem.meta.end_ts : undefined) > lowWaterMark)) { - opsToReturn.push(nextItem); - queue.pop(); - nextItem = queue.peek(); - } + // console.log "queue empty?", queue.empty() + // console.log "nextPack", nextPack? - // console.log "queue empty?", queue.empty() - // console.log "nextPack", nextPack? + if (queue.empty() && nextPack == null) { + // got everything + iterator._done = true + } - if (queue.empty() && (nextPack == null)) { // got everything - iterator._done = true; - } + return callback(null, opsToReturn) + } - return callback(null, opsToReturn); - } - - done() { - return this._done; - } - })); + done() { + return this._done + } +} diff --git a/services/track-changes/app/js/RedisManager.js b/services/track-changes/app/js/RedisManager.js index 8130d16710..a4d8cb1167 100644 --- a/services/track-changes/app/js/RedisManager.js +++ b/services/track-changes/app/js/RedisManager.js @@ -12,116 +12,155 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let RedisManager; -const Settings = require("settings-sharelatex"); -const redis = require("redis-sharelatex"); -const rclient = redis.createClient(Settings.redis.history); -const Keys = Settings.redis.history.key_schema; -const async = require("async"); +let RedisManager +const Settings = require('settings-sharelatex') +const redis = require('redis-sharelatex') +const rclient = redis.createClient(Settings.redis.history) +const Keys = Settings.redis.history.key_schema +const async = require('async') -module.exports = (RedisManager = { +module.exports = RedisManager = { + getOldestDocUpdates(doc_id, batchSize, callback) { + if (callback == null) { + callback = function(error, jsonUpdates) {} + } + const key = Keys.uncompressedHistoryOps({ doc_id }) + return rclient.lrange(key, 0, batchSize - 1, callback) + }, - getOldestDocUpdates(doc_id, batchSize, callback) { - if (callback == null) { callback = function(error, jsonUpdates) {}; } - const key = Keys.uncompressedHistoryOps({doc_id}); - return rclient.lrange(key, 0, batchSize - 1, callback); - }, + expandDocUpdates(jsonUpdates, callback) { + let rawUpdates + if (callback == null) { + callback = function(error, rawUpdates) {} + } + try { + rawUpdates = Array.from(jsonUpdates || []).map(update => + JSON.parse(update) + ) + } catch (e) { + return callback(e) + } + return callback(null, rawUpdates) + }, - expandDocUpdates(jsonUpdates, callback) { - let rawUpdates; - if (callback == null) { callback = function(error, rawUpdates) {}; } - try { - rawUpdates = ( Array.from(jsonUpdates || []).map((update) => JSON.parse(update)) ); - } catch (e) { - return callback(e); - } - return callback(null, rawUpdates); - }, + deleteAppliedDocUpdates(project_id, doc_id, docUpdates, callback) { + if (callback == null) { + callback = function(error) {} + } + const multi = rclient.multi() + // Delete all the updates which have been applied (exact match) + for (const update of Array.from(docUpdates || [])) { + multi.lrem(Keys.uncompressedHistoryOps({ doc_id }), 1, update) + } + return multi.exec(function(error, results) { + if (error != null) { + return callback(error) + } + // It's ok to delete the doc_id from the set here. Even though the list + // of updates may not be empty, we will continue to process it until it is. + return rclient.srem( + Keys.docsWithHistoryOps({ project_id }), + doc_id, + function(error) { + if (error != null) { + return callback(error) + } + return callback(null) + } + ) + }) + }, - deleteAppliedDocUpdates(project_id, doc_id, docUpdates, callback) { - if (callback == null) { callback = function(error) {}; } - const multi = rclient.multi(); - // Delete all the updates which have been applied (exact match) - for (const update of Array.from(docUpdates || [])) { - multi.lrem(Keys.uncompressedHistoryOps({doc_id}), 1, update); - } - return multi.exec(function(error, results) { - if (error != null) { return callback(error); } - // It's ok to delete the doc_id from the set here. Even though the list - // of updates may not be empty, we will continue to process it until it is. - return rclient.srem(Keys.docsWithHistoryOps({project_id}), doc_id, function(error) { - if (error != null) { return callback(error); } - return callback(null); - }); - }); - }, + getDocIdsWithHistoryOps(project_id, callback) { + if (callback == null) { + callback = function(error, doc_ids) {} + } + return rclient.smembers(Keys.docsWithHistoryOps({ project_id }), callback) + }, - getDocIdsWithHistoryOps(project_id, callback) { - if (callback == null) { callback = function(error, doc_ids) {}; } - return rclient.smembers(Keys.docsWithHistoryOps({project_id}), callback); - }, + // iterate over keys asynchronously using redis scan (non-blocking) + // handle all the cluster nodes or single redis server + _getKeys(pattern, callback) { + const nodes = (typeof rclient.nodes === 'function' + ? rclient.nodes('master') + : undefined) || [rclient] + const doKeyLookupForNode = (node, cb) => + RedisManager._getKeysFromNode(node, pattern, cb) + return async.concatSeries(nodes, doKeyLookupForNode, callback) + }, - // iterate over keys asynchronously using redis scan (non-blocking) - // handle all the cluster nodes or single redis server - _getKeys(pattern, callback) { - const nodes = (typeof rclient.nodes === 'function' ? rclient.nodes('master') : undefined) || [ rclient ]; - const doKeyLookupForNode = (node, cb) => RedisManager._getKeysFromNode(node, pattern, cb); - return async.concatSeries(nodes, doKeyLookupForNode, callback); - }, + _getKeysFromNode(node, pattern, callback) { + let cursor = 0 // redis iterator + const keySet = {} // use hash to avoid duplicate results + // scan over all keys looking for pattern + var doIteration = cb => + node.scan(cursor, 'MATCH', pattern, 'COUNT', 1000, function( + error, + reply + ) { + let keys + if (error != null) { + return callback(error) + } + ;[cursor, keys] = Array.from(reply) + for (const key of Array.from(keys)) { + keySet[key] = true + } + if (cursor === '0') { + // note redis returns string result not numeric + return callback(null, Object.keys(keySet)) + } else { + return doIteration() + } + }) + return doIteration() + }, - _getKeysFromNode(node, pattern, callback) { - let cursor = 0; // redis iterator - const keySet = {}; // use hash to avoid duplicate results - // scan over all keys looking for pattern - var doIteration = cb => - node.scan(cursor, "MATCH", pattern, "COUNT", 1000, function(error, reply) { - let keys; - if (error != null) { return callback(error); } - [cursor, keys] = Array.from(reply); - for (const key of Array.from(keys)) { - keySet[key] = true; - } - if (cursor === '0') { // note redis returns string result not numeric - return callback(null, Object.keys(keySet)); - } else { - return doIteration(); - } - }) - ; - return doIteration(); - }, + // extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b + // or DocsWithHistoryOps:{57fd0b1f53a8396d22b2c24b} (for redis cluster) + _extractIds(keyList) { + const ids = (() => { + const result = [] + for (const key of Array.from(keyList)) { + const m = key.match(/:\{?([0-9a-f]{24})\}?/) // extract object id + result.push(m[1]) + } + return result + })() + return ids + }, - // extract ids from keys like DocsWithHistoryOps:57fd0b1f53a8396d22b2c24b - // or DocsWithHistoryOps:{57fd0b1f53a8396d22b2c24b} (for redis cluster) - _extractIds(keyList) { - const ids = (() => { - const result = []; - for (const key of Array.from(keyList)) { - const m = key.match(/:\{?([0-9a-f]{24})\}?/); // extract object id - result.push(m[1]); - } - return result; - })(); - return ids; - }, + getProjectIdsWithHistoryOps(callback) { + if (callback == null) { + callback = function(error, project_ids) {} + } + return RedisManager._getKeys( + Keys.docsWithHistoryOps({ project_id: '*' }), + function(error, project_keys) { + if (error != null) { + return callback(error) + } + const project_ids = RedisManager._extractIds(project_keys) + return callback(error, project_ids) + } + ) + }, - getProjectIdsWithHistoryOps(callback) { - if (callback == null) { callback = function(error, project_ids) {}; } - return RedisManager._getKeys(Keys.docsWithHistoryOps({project_id:"*"}), function(error, project_keys) { - if (error != null) { return callback(error); } - const project_ids = RedisManager._extractIds(project_keys); - return callback(error, project_ids); - }); - }, - - getAllDocIdsWithHistoryOps(callback) { - // return all the docids, to find dangling history entries after - // everything is flushed. - if (callback == null) { callback = function(error, doc_ids) {}; } - return RedisManager._getKeys(Keys.uncompressedHistoryOps({doc_id:"*"}), function(error, doc_keys) { - if (error != null) { return callback(error); } - const doc_ids = RedisManager._extractIds(doc_keys); - return callback(error, doc_ids); - }); - } -}); + getAllDocIdsWithHistoryOps(callback) { + // return all the docids, to find dangling history entries after + // everything is flushed. + if (callback == null) { + callback = function(error, doc_ids) {} + } + return RedisManager._getKeys( + Keys.uncompressedHistoryOps({ doc_id: '*' }), + function(error, doc_keys) { + if (error != null) { + return callback(error) + } + const doc_ids = RedisManager._extractIds(doc_keys) + return callback(error, doc_ids) + } + ) + } +} diff --git a/services/track-changes/app/js/RestoreManager.js b/services/track-changes/app/js/RestoreManager.js index cc4c471309..cc365f6709 100644 --- a/services/track-changes/app/js/RestoreManager.js +++ b/services/track-changes/app/js/RestoreManager.js @@ -11,21 +11,38 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let RestoreManager; -const DocumentUpdaterManager = require("./DocumentUpdaterManager"); -const DiffManager = require("./DiffManager"); -const logger = require("logger-sharelatex"); +let RestoreManager +const DocumentUpdaterManager = require('./DocumentUpdaterManager') +const DiffManager = require('./DiffManager') +const logger = require('logger-sharelatex') -module.exports = (RestoreManager = { - restoreToBeforeVersion(project_id, doc_id, version, user_id, callback) { - if (callback == null) { callback = function(error) {}; } - logger.log({project_id, doc_id, version, user_id}, "restoring document"); - return DiffManager.getDocumentBeforeVersion(project_id, doc_id, version, function(error, content) { - if (error != null) { return callback(error); } - return DocumentUpdaterManager.setDocument(project_id, doc_id, content, user_id, function(error) { - if (error != null) { return callback(error); } - return callback(); - }); - }); - } -}); +module.exports = RestoreManager = { + restoreToBeforeVersion(project_id, doc_id, version, user_id, callback) { + if (callback == null) { + callback = function(error) {} + } + logger.log({ project_id, doc_id, version, user_id }, 'restoring document') + return DiffManager.getDocumentBeforeVersion( + project_id, + doc_id, + version, + function(error, content) { + if (error != null) { + return callback(error) + } + return DocumentUpdaterManager.setDocument( + project_id, + doc_id, + content, + user_id, + function(error) { + if (error != null) { + return callback(error) + } + return callback() + } + ) + } + ) + } +} diff --git a/services/track-changes/app/js/UpdateCompressor.js b/services/track-changes/app/js/UpdateCompressor.js index 5496889536..570bc97a61 100644 --- a/services/track-changes/app/js/UpdateCompressor.js +++ b/services/track-changes/app/js/UpdateCompressor.js @@ -14,274 +14,324 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let oneMinute, twoMegabytes, UpdateCompressor; -const strInject = (s1, pos, s2) => s1.slice(0, pos) + s2 + s1.slice(pos); -const strRemove = (s1, pos, length) => s1.slice(0, pos) + s1.slice((pos + length)); +let oneMinute, twoMegabytes, UpdateCompressor +const strInject = (s1, pos, s2) => s1.slice(0, pos) + s2 + s1.slice(pos) +const strRemove = (s1, pos, length) => s1.slice(0, pos) + s1.slice(pos + length) -const { diff_match_patch } = require("../lib/diff_match_patch"); -const dmp = new diff_match_patch(); +const { diff_match_patch } = require('../lib/diff_match_patch') +const dmp = new diff_match_patch() -module.exports = (UpdateCompressor = { - NOOP: "noop", +module.exports = UpdateCompressor = { + NOOP: 'noop', - // Updates come from the doc updater in format - // { - // op: [ { ... op1 ... }, { ... op2 ... } ] - // meta: { ts: ..., user_id: ... } - // } - // but it's easier to work with on op per update, so convert these updates to - // our compressed format - // [{ - // op: op1 - // meta: { start_ts: ... , end_ts: ..., user_id: ... } - // }, { - // op: op2 - // meta: { start_ts: ... , end_ts: ..., user_id: ... } - // }] - convertToSingleOpUpdates(updates) { - const splitUpdates = []; - for (const update of Array.from(updates)) { - // Reject any non-insert or delete ops, i.e. comments - const ops = update.op.filter(o => (o.i != null) || (o.d != null)); - if (ops.length === 0) { - splitUpdates.push({ - op: UpdateCompressor.NOOP, - meta: { - start_ts: update.meta.start_ts || update.meta.ts, - end_ts: update.meta.end_ts || update.meta.ts, - user_id: update.meta.user_id - }, - v: update.v - }); - } else { - for (const op of Array.from(ops)) { - splitUpdates.push({ - op, - meta: { - start_ts: update.meta.start_ts || update.meta.ts, - end_ts: update.meta.end_ts || update.meta.ts, - user_id: update.meta.user_id - }, - v: update.v - }); - } - } - } - return splitUpdates; - }, + // Updates come from the doc updater in format + // { + // op: [ { ... op1 ... }, { ... op2 ... } ] + // meta: { ts: ..., user_id: ... } + // } + // but it's easier to work with on op per update, so convert these updates to + // our compressed format + // [{ + // op: op1 + // meta: { start_ts: ... , end_ts: ..., user_id: ... } + // }, { + // op: op2 + // meta: { start_ts: ... , end_ts: ..., user_id: ... } + // }] + convertToSingleOpUpdates(updates) { + const splitUpdates = [] + for (const update of Array.from(updates)) { + // Reject any non-insert or delete ops, i.e. comments + const ops = update.op.filter(o => o.i != null || o.d != null) + if (ops.length === 0) { + splitUpdates.push({ + op: UpdateCompressor.NOOP, + meta: { + start_ts: update.meta.start_ts || update.meta.ts, + end_ts: update.meta.end_ts || update.meta.ts, + user_id: update.meta.user_id + }, + v: update.v + }) + } else { + for (const op of Array.from(ops)) { + splitUpdates.push({ + op, + meta: { + start_ts: update.meta.start_ts || update.meta.ts, + end_ts: update.meta.end_ts || update.meta.ts, + user_id: update.meta.user_id + }, + v: update.v + }) + } + } + } + return splitUpdates + }, - concatUpdatesWithSameVersion(updates) { - const concattedUpdates = []; - for (const update of Array.from(updates)) { - const lastUpdate = concattedUpdates[concattedUpdates.length - 1]; - if ((lastUpdate != null) && (lastUpdate.v === update.v)) { - if (update.op !== UpdateCompressor.NOOP) { lastUpdate.op.push(update.op); } - } else { - const nextUpdate = { - op: [], - meta: update.meta, - v: update.v - }; - if (update.op !== UpdateCompressor.NOOP) { nextUpdate.op.push(update.op); } - concattedUpdates.push(nextUpdate); - } - } - return concattedUpdates; - }, + concatUpdatesWithSameVersion(updates) { + const concattedUpdates = [] + for (const update of Array.from(updates)) { + const lastUpdate = concattedUpdates[concattedUpdates.length - 1] + if (lastUpdate != null && lastUpdate.v === update.v) { + if (update.op !== UpdateCompressor.NOOP) { + lastUpdate.op.push(update.op) + } + } else { + const nextUpdate = { + op: [], + meta: update.meta, + v: update.v + } + if (update.op !== UpdateCompressor.NOOP) { + nextUpdate.op.push(update.op) + } + concattedUpdates.push(nextUpdate) + } + } + return concattedUpdates + }, - compressRawUpdates(lastPreviousUpdate, rawUpdates) { - if (__guard__(lastPreviousUpdate != null ? lastPreviousUpdate.op : undefined, x => x.length) > 1) { - // if the last previous update was an array op, don't compress onto it. - // The avoids cases where array length changes but version number doesn't - return [lastPreviousUpdate].concat(UpdateCompressor.compressRawUpdates(null,rawUpdates)); - } - if (lastPreviousUpdate != null) { - rawUpdates = [lastPreviousUpdate].concat(rawUpdates); - } - let updates = UpdateCompressor.convertToSingleOpUpdates(rawUpdates); - updates = UpdateCompressor.compressUpdates(updates); - return UpdateCompressor.concatUpdatesWithSameVersion(updates); - }, + compressRawUpdates(lastPreviousUpdate, rawUpdates) { + if ( + __guard__( + lastPreviousUpdate != null ? lastPreviousUpdate.op : undefined, + x => x.length + ) > 1 + ) { + // if the last previous update was an array op, don't compress onto it. + // The avoids cases where array length changes but version number doesn't + return [lastPreviousUpdate].concat( + UpdateCompressor.compressRawUpdates(null, rawUpdates) + ) + } + if (lastPreviousUpdate != null) { + rawUpdates = [lastPreviousUpdate].concat(rawUpdates) + } + let updates = UpdateCompressor.convertToSingleOpUpdates(rawUpdates) + updates = UpdateCompressor.compressUpdates(updates) + return UpdateCompressor.concatUpdatesWithSameVersion(updates) + }, - compressUpdates(updates) { - if (updates.length === 0) { return []; } + compressUpdates(updates) { + if (updates.length === 0) { + return [] + } - let compressedUpdates = [updates.shift()]; - for (const update of Array.from(updates)) { - const lastCompressedUpdate = compressedUpdates.pop(); - if (lastCompressedUpdate != null) { - compressedUpdates = compressedUpdates.concat(UpdateCompressor._concatTwoUpdates(lastCompressedUpdate, update)); - } else { - compressedUpdates.push(update); - } - } + let compressedUpdates = [updates.shift()] + for (const update of Array.from(updates)) { + const lastCompressedUpdate = compressedUpdates.pop() + if (lastCompressedUpdate != null) { + compressedUpdates = compressedUpdates.concat( + UpdateCompressor._concatTwoUpdates(lastCompressedUpdate, update) + ) + } else { + compressedUpdates.push(update) + } + } - return compressedUpdates; - }, + return compressedUpdates + }, - MAX_TIME_BETWEEN_UPDATES: (oneMinute = 60 * 1000), - MAX_UPDATE_SIZE: (twoMegabytes = 2* 1024 * 1024), + MAX_TIME_BETWEEN_UPDATES: (oneMinute = 60 * 1000), + MAX_UPDATE_SIZE: (twoMegabytes = 2 * 1024 * 1024), - _concatTwoUpdates(firstUpdate, secondUpdate) { - let offset; - firstUpdate = { - op: firstUpdate.op, - meta: { - user_id: firstUpdate.meta.user_id || null, - start_ts: firstUpdate.meta.start_ts || firstUpdate.meta.ts, - end_ts: firstUpdate.meta.end_ts || firstUpdate.meta.ts - }, - v: firstUpdate.v - }; - secondUpdate = { - op: secondUpdate.op, - meta: { - user_id: secondUpdate.meta.user_id || null, - start_ts: secondUpdate.meta.start_ts || secondUpdate.meta.ts, - end_ts: secondUpdate.meta.end_ts || secondUpdate.meta.ts - }, - v: secondUpdate.v - }; + _concatTwoUpdates(firstUpdate, secondUpdate) { + let offset + firstUpdate = { + op: firstUpdate.op, + meta: { + user_id: firstUpdate.meta.user_id || null, + start_ts: firstUpdate.meta.start_ts || firstUpdate.meta.ts, + end_ts: firstUpdate.meta.end_ts || firstUpdate.meta.ts + }, + v: firstUpdate.v + } + secondUpdate = { + op: secondUpdate.op, + meta: { + user_id: secondUpdate.meta.user_id || null, + start_ts: secondUpdate.meta.start_ts || secondUpdate.meta.ts, + end_ts: secondUpdate.meta.end_ts || secondUpdate.meta.ts + }, + v: secondUpdate.v + } - if (firstUpdate.meta.user_id !== secondUpdate.meta.user_id) { - return [firstUpdate, secondUpdate]; - } + if (firstUpdate.meta.user_id !== secondUpdate.meta.user_id) { + return [firstUpdate, secondUpdate] + } - if ((secondUpdate.meta.start_ts - firstUpdate.meta.end_ts) > UpdateCompressor.MAX_TIME_BETWEEN_UPDATES) { - return [firstUpdate, secondUpdate]; - } + if ( + secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > + UpdateCompressor.MAX_TIME_BETWEEN_UPDATES + ) { + return [firstUpdate, secondUpdate] + } - const firstOp = firstUpdate.op; - const secondOp = secondUpdate.op; + const firstOp = firstUpdate.op + const secondOp = secondUpdate.op - const firstSize = (firstOp.i != null ? firstOp.i.length : undefined) || (firstOp.d != null ? firstOp.d.length : undefined); - const secondSize = (secondOp.i != null ? secondOp.i.length : undefined) || (secondOp.d != null ? secondOp.d.length : undefined); + const firstSize = + (firstOp.i != null ? firstOp.i.length : undefined) || + (firstOp.d != null ? firstOp.d.length : undefined) + const secondSize = + (secondOp.i != null ? secondOp.i.length : undefined) || + (secondOp.d != null ? secondOp.d.length : undefined) - // Two inserts - if ((firstOp.i != null) && (secondOp.i != null) && (firstOp.p <= secondOp.p && secondOp.p <= (firstOp.p + firstOp.i.length)) && ((firstSize + secondSize) < UpdateCompressor.MAX_UPDATE_SIZE)) { - return [{ - meta: { - start_ts: firstUpdate.meta.start_ts, - end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id - }, - op: { - p: firstOp.p, - i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) - }, - v: secondUpdate.v - } - ]; - // Two deletes - } else if ((firstOp.d != null) && (secondOp.d != null) && (secondOp.p <= firstOp.p && firstOp.p <= (secondOp.p + secondOp.d.length)) && ((firstSize + secondSize) < UpdateCompressor.MAX_UPDATE_SIZE)) { - return [{ - meta: { - start_ts: firstUpdate.meta.start_ts, - end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id - }, - op: { - p: secondOp.p, - d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) - }, - v: secondUpdate.v - } - ]; - // An insert and then a delete - } else if ((firstOp.i != null) && (secondOp.d != null) && (firstOp.p <= secondOp.p && secondOp.p <= (firstOp.p + firstOp.i.length))) { - offset = secondOp.p - firstOp.p; - const insertedText = firstOp.i.slice(offset, offset + secondOp.d.length); - // Only trim the insert when the delete is fully contained within in it - if (insertedText === secondOp.d) { - const insert = strRemove(firstOp.i, offset, secondOp.d.length); - return [{ - meta: { - start_ts: firstUpdate.meta.start_ts, - end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id - }, - op: { - p: firstOp.p, - i: insert - }, - v: secondUpdate.v - } - ]; - } else { - // This will only happen if the delete extends outside the insert - return [firstUpdate, secondUpdate]; - } + // Two inserts + if ( + firstOp.i != null && + secondOp.i != null && + firstOp.p <= secondOp.p && secondOp.p <= firstOp.p + firstOp.i.length && + firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE + ) { + return [ + { + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, + user_id: firstUpdate.meta.user_id + }, + op: { + p: firstOp.p, + i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) + }, + v: secondUpdate.v + } + ] + // Two deletes + } else if ( + firstOp.d != null && + secondOp.d != null && + secondOp.p <= firstOp.p && firstOp.p <= secondOp.p + secondOp.d.length && + firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE + ) { + return [ + { + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, + user_id: firstUpdate.meta.user_id + }, + op: { + p: secondOp.p, + d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) + }, + v: secondUpdate.v + } + ] + // An insert and then a delete + } else if ( + firstOp.i != null && + secondOp.d != null && + firstOp.p <= secondOp.p && secondOp.p <= firstOp.p + firstOp.i.length + ) { + offset = secondOp.p - firstOp.p + const insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) + // Only trim the insert when the delete is fully contained within in it + if (insertedText === secondOp.d) { + const insert = strRemove(firstOp.i, offset, secondOp.d.length) + return [ + { + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, + user_id: firstUpdate.meta.user_id + }, + op: { + p: firstOp.p, + i: insert + }, + v: secondUpdate.v + } + ] + } else { + // This will only happen if the delete extends outside the insert + return [firstUpdate, secondUpdate] + } - // A delete then an insert at the same place, likely a copy-paste of a chunk of content - } else if ((firstOp.d != null) && (secondOp.i != null) && (firstOp.p === secondOp.p)) { - offset = firstOp.p; - const diff_ops = this.diffAsShareJsOps(firstOp.d, secondOp.i); - if (diff_ops.length === 0) { - return [{ // Noop - meta: { - start_ts: firstUpdate.meta.start_ts, - end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id - }, - op: { - p: firstOp.p, - i: "" - }, - v: secondUpdate.v - }]; - } else { - return diff_ops.map(function(op) { - op.p += offset; - return { - meta: { - start_ts: firstUpdate.meta.start_ts, - end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id - }, - op, - v: secondUpdate.v - };}); - } + // A delete then an insert at the same place, likely a copy-paste of a chunk of content + } else if ( + firstOp.d != null && + secondOp.i != null && + firstOp.p === secondOp.p + ) { + offset = firstOp.p + const diff_ops = this.diffAsShareJsOps(firstOp.d, secondOp.i) + if (diff_ops.length === 0) { + return [ + { + // Noop + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, + user_id: firstUpdate.meta.user_id + }, + op: { + p: firstOp.p, + i: '' + }, + v: secondUpdate.v + } + ] + } else { + return diff_ops.map(function(op) { + op.p += offset + return { + meta: { + start_ts: firstUpdate.meta.start_ts, + end_ts: secondUpdate.meta.end_ts, + user_id: firstUpdate.meta.user_id + }, + op, + v: secondUpdate.v + } + }) + } + } else { + return [firstUpdate, secondUpdate] + } + }, - } else { - return [firstUpdate, secondUpdate]; - } - }, + ADDED: 1, + REMOVED: -1, + UNCHANGED: 0, + diffAsShareJsOps(before, after, callback) { + if (callback == null) { + callback = function(error, ops) {} + } + const diffs = dmp.diff_main(before, after) + dmp.diff_cleanupSemantic(diffs) - ADDED: 1, - REMOVED: -1, - UNCHANGED: 0, - diffAsShareJsOps(before, after, callback) { - if (callback == null) { callback = function(error, ops) {}; } - const diffs = dmp.diff_main(before, after); - dmp.diff_cleanupSemantic(diffs); - - const ops = []; - let position = 0; - for (const diff of Array.from(diffs)) { - const type = diff[0]; - const content = diff[1]; - if (type === this.ADDED) { - ops.push({ - i: content, - p: position - }); - position += content.length; - } else if (type === this.REMOVED) { - ops.push({ - d: content, - p: position - }); - } else if (type === this.UNCHANGED) { - position += content.length; - } else { - throw "Unknown type"; - } - } - return ops; - } -}); + const ops = [] + let position = 0 + for (const diff of Array.from(diffs)) { + const type = diff[0] + const content = diff[1] + if (type === this.ADDED) { + ops.push({ + i: content, + p: position + }) + position += content.length + } else if (type === this.REMOVED) { + ops.push({ + d: content, + p: position + }) + } else if (type === this.UNCHANGED) { + position += content.length + } else { + throw 'Unknown type' + } + } + return ops + } +} function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/app/js/UpdateTrimmer.js b/services/track-changes/app/js/UpdateTrimmer.js index 393ff9ce99..8a37e5484f 100644 --- a/services/track-changes/app/js/UpdateTrimmer.js +++ b/services/track-changes/app/js/UpdateTrimmer.js @@ -12,40 +12,66 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let UpdateTrimmer; -const MongoManager = require("./MongoManager"); -const WebApiManager = require("./WebApiManager"); -const logger = require("logger-sharelatex"); - -module.exports = (UpdateTrimmer = { - shouldTrimUpdates(project_id, callback) { - if (callback == null) { callback = function(error, shouldTrim) {}; } - return MongoManager.getProjectMetaData(project_id, function(error, metadata) { - if (error != null) { return callback(error); } - if (metadata != null ? metadata.preserveHistory : undefined) { - return callback(null, false); - } else { - return WebApiManager.getProjectDetails(project_id, function(error, details) { - if (error != null) { return callback(error); } - logger.log({project_id, details}, "got details"); - if (__guard__(details != null ? details.features : undefined, x => x.versioning)) { - return MongoManager.setProjectMetaData(project_id, {preserveHistory: true}, function(error) { - if (error != null) { return callback(error); } - return MongoManager.upgradeHistory(project_id, function(error) { - if (error != null) { return callback(error); } - return callback(null, false); - }); - }); - } else { - return callback(null, true); - } - }); - } - }); - } -}); +let UpdateTrimmer +const MongoManager = require('./MongoManager') +const WebApiManager = require('./WebApiManager') +const logger = require('logger-sharelatex') +module.exports = UpdateTrimmer = { + shouldTrimUpdates(project_id, callback) { + if (callback == null) { + callback = function(error, shouldTrim) {} + } + return MongoManager.getProjectMetaData(project_id, function( + error, + metadata + ) { + if (error != null) { + return callback(error) + } + if (metadata != null ? metadata.preserveHistory : undefined) { + return callback(null, false) + } else { + return WebApiManager.getProjectDetails(project_id, function( + error, + details + ) { + if (error != null) { + return callback(error) + } + logger.log({ project_id, details }, 'got details') + if ( + __guard__( + details != null ? details.features : undefined, + x => x.versioning + ) + ) { + return MongoManager.setProjectMetaData( + project_id, + { preserveHistory: true }, + function(error) { + if (error != null) { + return callback(error) + } + return MongoManager.upgradeHistory(project_id, function(error) { + if (error != null) { + return callback(error) + } + return callback(null, false) + }) + } + ) + } else { + return callback(null, true) + } + }) + } + }) + } +} function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/app/js/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js index 0f3ae8568c..89da68a631 100644 --- a/services/track-changes/app/js/UpdatesManager.js +++ b/services/track-changes/app/js/UpdatesManager.js @@ -14,488 +14,819 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let fiveMinutes, UpdatesManager; -const MongoManager = require("./MongoManager"); -const PackManager = require("./PackManager"); -const RedisManager = require("./RedisManager"); -const UpdateCompressor = require("./UpdateCompressor"); -const LockManager = require("./LockManager"); -const WebApiManager = require("./WebApiManager"); -const UpdateTrimmer = require("./UpdateTrimmer"); -const logger = require("logger-sharelatex"); -const async = require("async"); -const _ = require("underscore"); -const Settings = require("settings-sharelatex"); -const keys = Settings.redis.lock.key_schema; +let fiveMinutes, UpdatesManager +const MongoManager = require('./MongoManager') +const PackManager = require('./PackManager') +const RedisManager = require('./RedisManager') +const UpdateCompressor = require('./UpdateCompressor') +const LockManager = require('./LockManager') +const WebApiManager = require('./WebApiManager') +const UpdateTrimmer = require('./UpdateTrimmer') +const logger = require('logger-sharelatex') +const async = require('async') +const _ = require('underscore') +const Settings = require('settings-sharelatex') +const keys = Settings.redis.lock.key_schema -module.exports = (UpdatesManager = { - compressAndSaveRawUpdates(project_id, doc_id, rawUpdates, temporary, callback) { - let i; - if (callback == null) { callback = function(error) {}; } - const { length } = rawUpdates; - if (length === 0) { - return callback(); - } +module.exports = UpdatesManager = { + compressAndSaveRawUpdates( + project_id, + doc_id, + rawUpdates, + temporary, + callback + ) { + let i + if (callback == null) { + callback = function(error) {} + } + const { length } = rawUpdates + if (length === 0) { + return callback() + } - // check that ops are in the correct order - for (i = 0; i < rawUpdates.length; i++) { - const op = rawUpdates[i]; - if (i > 0) { - const thisVersion = op != null ? op.v : undefined; - const prevVersion = __guard__(rawUpdates[i-1], x => x.v); - if (!(prevVersion < thisVersion)) { - logger.error({project_id, doc_id, rawUpdates, temporary, thisVersion, prevVersion}, "op versions out of order"); - } - } - } + // check that ops are in the correct order + for (i = 0; i < rawUpdates.length; i++) { + const op = rawUpdates[i] + if (i > 0) { + const thisVersion = op != null ? op.v : undefined + const prevVersion = __guard__(rawUpdates[i - 1], x => x.v) + if (!(prevVersion < thisVersion)) { + logger.error( + { + project_id, + doc_id, + rawUpdates, + temporary, + thisVersion, + prevVersion + }, + 'op versions out of order' + ) + } + } + } - // FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it - // CORRECTION: we do use it to log the time in case of error - return MongoManager.peekLastCompressedUpdate(doc_id, function(error, lastCompressedUpdate, lastVersion) { - // lastCompressedUpdate is the most recent update in Mongo, and - // lastVersion is its sharejs version number. - // - // The peekLastCompressedUpdate method may pass the update back - // as 'null' (for example if the previous compressed update has - // been archived). In this case it can still pass back the - // lastVersion from the update to allow us to check consistency. - let op; - if (error != null) { return callback(error); } + // FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it + // CORRECTION: we do use it to log the time in case of error + return MongoManager.peekLastCompressedUpdate(doc_id, function( + error, + lastCompressedUpdate, + lastVersion + ) { + // lastCompressedUpdate is the most recent update in Mongo, and + // lastVersion is its sharejs version number. + // + // The peekLastCompressedUpdate method may pass the update back + // as 'null' (for example if the previous compressed update has + // been archived). In this case it can still pass back the + // lastVersion from the update to allow us to check consistency. + let op + if (error != null) { + return callback(error) + } - // Ensure that raw updates start where lastVersion left off - if (lastVersion != null) { - const discardedUpdates = []; - rawUpdates = rawUpdates.slice(0); - while ((rawUpdates[0] != null) && (rawUpdates[0].v <= lastVersion)) { - discardedUpdates.push(rawUpdates.shift()); - } - if (discardedUpdates.length) { - logger.error({project_id, doc_id, discardedUpdates, temporary, lastVersion}, "discarded updates already present"); - } + // Ensure that raw updates start where lastVersion left off + if (lastVersion != null) { + const discardedUpdates = [] + rawUpdates = rawUpdates.slice(0) + while (rawUpdates[0] != null && rawUpdates[0].v <= lastVersion) { + discardedUpdates.push(rawUpdates.shift()) + } + if (discardedUpdates.length) { + logger.error( + { project_id, doc_id, discardedUpdates, temporary, lastVersion }, + 'discarded updates already present' + ) + } - if ((rawUpdates[0] != null) && (rawUpdates[0].v !== (lastVersion + 1))) { - const ts = __guard__(lastCompressedUpdate != null ? lastCompressedUpdate.meta : undefined, x1 => x1.end_ts); - const last_timestamp = (ts != null) ? new Date(ts) : 'unknown time'; - error = new Error(`Tried to apply raw op at version ${rawUpdates[0].v} to last compressed update with version ${lastVersion} from ${last_timestamp}`); - logger.error({err: error, doc_id, project_id, prev_end_ts: ts, temporary, lastCompressedUpdate}, "inconsistent doc versions"); - if ((Settings.trackchanges != null ? Settings.trackchanges.continueOnError : undefined) && (rawUpdates[0].v > (lastVersion + 1))) { - // we have lost some ops - continue to write into the database, we can't recover at this point - lastCompressedUpdate = null; - } else { - return callback(error); - } - } - } + if (rawUpdates[0] != null && rawUpdates[0].v !== lastVersion + 1) { + const ts = __guard__( + lastCompressedUpdate != null + ? lastCompressedUpdate.meta + : undefined, + x1 => x1.end_ts + ) + const last_timestamp = ts != null ? new Date(ts) : 'unknown time' + error = new Error( + `Tried to apply raw op at version ${rawUpdates[0].v} to last compressed update with version ${lastVersion} from ${last_timestamp}` + ) + logger.error( + { + err: error, + doc_id, + project_id, + prev_end_ts: ts, + temporary, + lastCompressedUpdate + }, + 'inconsistent doc versions' + ) + if ( + (Settings.trackchanges != null + ? Settings.trackchanges.continueOnError + : undefined) && + rawUpdates[0].v > lastVersion + 1 + ) { + // we have lost some ops - continue to write into the database, we can't recover at this point + lastCompressedUpdate = null + } else { + return callback(error) + } + } + } - if (rawUpdates.length === 0) { - return callback(); - } + if (rawUpdates.length === 0) { + return callback() + } - // some old large ops in redis need to be rejected, they predate - // the size limit that now prevents them going through the system - const REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024; - for (var rawUpdate of Array.from(rawUpdates)) { - const opSizes = ((() => { - const result = []; - for (op of Array.from((rawUpdate != null ? rawUpdate.op : undefined) || [])) { result.push(((op.i != null ? op.i.length : undefined) || (op.d != null ? op.d.length : undefined))); - } - return result; - })()); - const size = _.max(opSizes); - if (size > REJECT_LARGE_OP_SIZE) { - error = new Error(`dropped op exceeding maximum allowed size of ${REJECT_LARGE_OP_SIZE}`); - logger.error({err: error, doc_id, project_id, size, rawUpdate}, "dropped op - too big"); - rawUpdate.op = []; - } - } + // some old large ops in redis need to be rejected, they predate + // the size limit that now prevents them going through the system + const REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024 + for (var rawUpdate of Array.from(rawUpdates)) { + const opSizes = (() => { + const result = [] + for (op of Array.from( + (rawUpdate != null ? rawUpdate.op : undefined) || [] + )) { + result.push( + (op.i != null ? op.i.length : undefined) || + (op.d != null ? op.d.length : undefined) + ) + } + return result + })() + const size = _.max(opSizes) + if (size > REJECT_LARGE_OP_SIZE) { + error = new Error( + `dropped op exceeding maximum allowed size of ${REJECT_LARGE_OP_SIZE}` + ) + logger.error( + { err: error, doc_id, project_id, size, rawUpdate }, + 'dropped op - too big' + ) + rawUpdate.op = [] + } + } - const compressedUpdates = UpdateCompressor.compressRawUpdates(null, rawUpdates); - return PackManager.insertCompressedUpdates(project_id, doc_id, lastCompressedUpdate, compressedUpdates, temporary, function(error, result) { - if (error != null) { return callback(error); } - if (result != null) { logger.log({project_id, doc_id, orig_v: (lastCompressedUpdate != null ? lastCompressedUpdate.v : undefined), new_v: result.v}, "inserted updates into pack"); } - return callback(); - }); - }); - }, + const compressedUpdates = UpdateCompressor.compressRawUpdates( + null, + rawUpdates + ) + return PackManager.insertCompressedUpdates( + project_id, + doc_id, + lastCompressedUpdate, + compressedUpdates, + temporary, + function(error, result) { + if (error != null) { + return callback(error) + } + if (result != null) { + logger.log( + { + project_id, + doc_id, + orig_v: + lastCompressedUpdate != null + ? lastCompressedUpdate.v + : undefined, + new_v: result.v + }, + 'inserted updates into pack' + ) + } + return callback() + } + ) + }) + }, - // Check whether the updates are temporary (per-project property) - _prepareProjectForUpdates(project_id, callback) { - if (callback == null) { callback = function(error, temporary) {}; } - return UpdateTrimmer.shouldTrimUpdates(project_id, function(error, temporary) { - if (error != null) { return callback(error); } - return callback(null, temporary); - }); - }, + // Check whether the updates are temporary (per-project property) + _prepareProjectForUpdates(project_id, callback) { + if (callback == null) { + callback = function(error, temporary) {} + } + return UpdateTrimmer.shouldTrimUpdates(project_id, function( + error, + temporary + ) { + if (error != null) { + return callback(error) + } + return callback(null, temporary) + }) + }, - // Check for project id on document history (per-document property) - _prepareDocForUpdates(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return MongoManager.backportProjectId(project_id, doc_id, function(error) { - if (error != null) { return callback(error); } - return callback(null); - }); - }, + // Check for project id on document history (per-document property) + _prepareDocForUpdates(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return MongoManager.backportProjectId(project_id, doc_id, function(error) { + if (error != null) { + return callback(error) + } + return callback(null) + }) + }, - // Apply updates for specific project/doc after preparing at project and doc level - REDIS_READ_BATCH_SIZE: 100, - processUncompressedUpdates(project_id, doc_id, temporary, callback) { - // get the updates as strings from redis (so we can delete them after they are applied) - if (callback == null) { callback = function(error) {}; } - return RedisManager.getOldestDocUpdates(doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, function(error, docUpdates) { - if (error != null) { return callback(error); } - const { length } = docUpdates; - // parse the redis strings into ShareJs updates - return RedisManager.expandDocUpdates(docUpdates, function(error, rawUpdates) { - if (error != null) { - logger.err({project_id, doc_id, docUpdates}, "failed to parse docUpdates"); - return callback(error); - } - logger.log({project_id, doc_id, rawUpdates}, "retrieved raw updates from redis"); - return UpdatesManager.compressAndSaveRawUpdates(project_id, doc_id, rawUpdates, temporary, function(error) { - if (error != null) { return callback(error); } - logger.log({project_id, doc_id}, "compressed and saved doc updates"); - // delete the applied updates from redis - return RedisManager.deleteAppliedDocUpdates(project_id, doc_id, docUpdates, function(error) { - if (error != null) { return callback(error); } - if (length === UpdatesManager.REDIS_READ_BATCH_SIZE) { - // There might be more updates - logger.log({project_id, doc_id}, "continuing processing updates"); - return setTimeout(() => UpdatesManager.processUncompressedUpdates(project_id, doc_id, temporary, callback) - , 0); - } else { - logger.log({project_id, doc_id}, "all raw updates processed"); - return callback(); - } - }); - }); - }); - }); - }, + // Apply updates for specific project/doc after preparing at project and doc level + REDIS_READ_BATCH_SIZE: 100, + processUncompressedUpdates(project_id, doc_id, temporary, callback) { + // get the updates as strings from redis (so we can delete them after they are applied) + if (callback == null) { + callback = function(error) {} + } + return RedisManager.getOldestDocUpdates( + doc_id, + UpdatesManager.REDIS_READ_BATCH_SIZE, + function(error, docUpdates) { + if (error != null) { + return callback(error) + } + const { length } = docUpdates + // parse the redis strings into ShareJs updates + return RedisManager.expandDocUpdates(docUpdates, function( + error, + rawUpdates + ) { + if (error != null) { + logger.err( + { project_id, doc_id, docUpdates }, + 'failed to parse docUpdates' + ) + return callback(error) + } + logger.log( + { project_id, doc_id, rawUpdates }, + 'retrieved raw updates from redis' + ) + return UpdatesManager.compressAndSaveRawUpdates( + project_id, + doc_id, + rawUpdates, + temporary, + function(error) { + if (error != null) { + return callback(error) + } + logger.log( + { project_id, doc_id }, + 'compressed and saved doc updates' + ) + // delete the applied updates from redis + return RedisManager.deleteAppliedDocUpdates( + project_id, + doc_id, + docUpdates, + function(error) { + if (error != null) { + return callback(error) + } + if (length === UpdatesManager.REDIS_READ_BATCH_SIZE) { + // There might be more updates + logger.log( + { project_id, doc_id }, + 'continuing processing updates' + ) + return setTimeout( + () => + UpdatesManager.processUncompressedUpdates( + project_id, + doc_id, + temporary, + callback + ), + 0 + ) + } else { + logger.log( + { project_id, doc_id }, + 'all raw updates processed' + ) + return callback() + } + } + ) + } + ) + }) + } + ) + }, - // Process updates for a doc when we flush it individually - processUncompressedUpdatesWithLock(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return UpdatesManager._prepareProjectForUpdates(project_id, function(error, temporary) { - if (error != null) { return callback(error); } - return UpdatesManager._processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, callback); - }); - }, + // Process updates for a doc when we flush it individually + processUncompressedUpdatesWithLock(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return UpdatesManager._prepareProjectForUpdates(project_id, function( + error, + temporary + ) { + if (error != null) { + return callback(error) + } + return UpdatesManager._processUncompressedUpdatesForDocWithLock( + project_id, + doc_id, + temporary, + callback + ) + }) + }, + // Process updates for a doc when the whole project is flushed (internal method) + _processUncompressedUpdatesForDocWithLock( + project_id, + doc_id, + temporary, + callback + ) { + if (callback == null) { + callback = function(error) {} + } + return UpdatesManager._prepareDocForUpdates(project_id, doc_id, function( + error + ) { + if (error != null) { + return callback(error) + } + return LockManager.runWithLock( + keys.historyLock({ doc_id }), + releaseLock => + UpdatesManager.processUncompressedUpdates( + project_id, + doc_id, + temporary, + releaseLock + ), + callback + ) + }) + }, - // Process updates for a doc when the whole project is flushed (internal method) - _processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, callback) { - if (callback == null) { callback = function(error) {}; } - return UpdatesManager._prepareDocForUpdates(project_id, doc_id, function(error) { - if (error != null) { return callback(error); } - return LockManager.runWithLock( - keys.historyLock({doc_id}), - releaseLock => UpdatesManager.processUncompressedUpdates(project_id, doc_id, temporary, releaseLock), - callback - ); - }); - }, + // Process all updates for a project, only check project-level information once + processUncompressedUpdatesForProject(project_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return RedisManager.getDocIdsWithHistoryOps(project_id, function( + error, + doc_ids + ) { + if (error != null) { + return callback(error) + } + return UpdatesManager._prepareProjectForUpdates(project_id, function( + error, + temporary + ) { + const jobs = [] + for (const doc_id of Array.from(doc_ids)) { + ;(doc_id => + jobs.push(cb => + UpdatesManager._processUncompressedUpdatesForDocWithLock( + project_id, + doc_id, + temporary, + cb + ) + ))(doc_id) + } + return async.parallelLimit(jobs, 5, callback) + }) + }) + }, - // Process all updates for a project, only check project-level information once - processUncompressedUpdatesForProject(project_id, callback) { - if (callback == null) { callback = function(error) {}; } - return RedisManager.getDocIdsWithHistoryOps(project_id, function(error, doc_ids) { - if (error != null) { return callback(error); } - return UpdatesManager._prepareProjectForUpdates(project_id, function(error, temporary) { - const jobs = []; - for (const doc_id of Array.from(doc_ids)) { - (doc_id => - jobs.push(cb => UpdatesManager._processUncompressedUpdatesForDocWithLock(project_id, doc_id, temporary, cb)) - )(doc_id); - } - return async.parallelLimit(jobs, 5, callback); - }); - }); - }, + // flush all outstanding changes + flushAll(limit, callback) { + if (callback == null) { + callback = function(error, result) {} + } + return RedisManager.getProjectIdsWithHistoryOps(function( + error, + project_ids + ) { + let project_id + if (error != null) { + return callback(error) + } + logger.log( + { + count: project_ids != null ? project_ids.length : undefined, + project_ids + }, + 'found projects' + ) + const jobs = [] + project_ids = _.shuffle(project_ids) // randomise to avoid hitting same projects each time + const selectedProjects = + limit < 0 ? project_ids : project_ids.slice(0, limit) + for (project_id of Array.from(selectedProjects)) { + ;(project_id => + jobs.push(cb => + UpdatesManager.processUncompressedUpdatesForProject( + project_id, + err => cb(null, { failed: err != null, project_id }) + ) + ))(project_id) + } + return async.series(jobs, function(error, result) { + let x + if (error != null) { + return callback(error) + } + const failedProjects = (() => { + const result1 = [] + for (x of Array.from(result)) { + if (x.failed) { + result1.push(x.project_id) + } + } + return result1 + })() + const succeededProjects = (() => { + const result2 = [] + for (x of Array.from(result)) { + if (!x.failed) { + result2.push(x.project_id) + } + } + return result2 + })() + return callback(null, { + failed: failedProjects, + succeeded: succeededProjects, + all: project_ids + }) + }) + }) + }, - // flush all outstanding changes - flushAll(limit, callback) { - if (callback == null) { callback = function(error, result) {}; } - return RedisManager.getProjectIdsWithHistoryOps(function(error, project_ids) { - let project_id; - if (error != null) { return callback(error); } - logger.log({count: (project_ids != null ? project_ids.length : undefined), project_ids}, "found projects"); - const jobs = []; - project_ids = _.shuffle(project_ids); // randomise to avoid hitting same projects each time - const selectedProjects = limit < 0 ? project_ids : project_ids.slice(0, limit); - for (project_id of Array.from(selectedProjects)) { - (project_id => - jobs.push(cb => - UpdatesManager.processUncompressedUpdatesForProject(project_id, err => cb(null, {failed: (err != null), project_id})) - ) - )(project_id); - } - return async.series(jobs, function(error, result) { - let x; - if (error != null) { return callback(error); } - const failedProjects = ((() => { - const result1 = []; - for (x of Array.from(result)) { if (x.failed) { - result1.push(x.project_id); - } - } - return result1; - })()); - const succeededProjects = ((() => { - const result2 = []; - for (x of Array.from(result)) { if (!x.failed) { - result2.push(x.project_id); - } - } - return result2; - })()); - return callback(null, {failed: failedProjects, succeeded: succeededProjects, all: project_ids}); - }); - }); - }, + getDanglingUpdates(callback) { + if (callback == null) { + callback = function(error, doc_ids) {} + } + return RedisManager.getAllDocIdsWithHistoryOps(function( + error, + all_doc_ids + ) { + if (error != null) { + return callback(error) + } + return RedisManager.getProjectIdsWithHistoryOps(function( + error, + all_project_ids + ) { + if (error != null) { + return callback(error) + } + // function to get doc_ids for each project + const task = cb => + async.concatSeries( + all_project_ids, + RedisManager.getDocIdsWithHistoryOps, + cb + ) + // find the dangling doc ids + return task(function(error, project_doc_ids) { + const dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids) + logger.log( + { all_doc_ids, all_project_ids, project_doc_ids, dangling_doc_ids }, + 'checking for dangling doc ids' + ) + return callback(null, dangling_doc_ids) + }) + }) + }) + }, - getDanglingUpdates(callback) { - if (callback == null) { callback = function(error, doc_ids) {}; } - return RedisManager.getAllDocIdsWithHistoryOps(function(error, all_doc_ids) { - if (error != null) { return callback(error); } - return RedisManager.getProjectIdsWithHistoryOps(function(error, all_project_ids) { - if (error != null) { return callback(error); } - // function to get doc_ids for each project - const task = cb => async.concatSeries(all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb); - // find the dangling doc ids - return task(function(error, project_doc_ids) { - const dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids); - logger.log({all_doc_ids, all_project_ids, project_doc_ids, dangling_doc_ids}, "checking for dangling doc ids"); - return callback(null, dangling_doc_ids); - }); - }); - }); - }, + getDocUpdates(project_id, doc_id, options, callback) { + if (options == null) { + options = {} + } + if (callback == null) { + callback = function(error, updates) {} + } + return UpdatesManager.processUncompressedUpdatesWithLock( + project_id, + doc_id, + function(error) { + if (error != null) { + return callback(error) + } + // console.log "options", options + return PackManager.getOpsByVersionRange( + project_id, + doc_id, + options.from, + options.to, + function(error, updates) { + if (error != null) { + return callback(error) + } + return callback(null, updates) + } + ) + } + ) + }, - getDocUpdates(project_id, doc_id, options, callback) { - if (options == null) { options = {}; } - if (callback == null) { callback = function(error, updates) {}; } - return UpdatesManager.processUncompressedUpdatesWithLock(project_id, doc_id, function(error) { - if (error != null) { return callback(error); } - // console.log "options", options - return PackManager.getOpsByVersionRange(project_id, doc_id, options.from, options.to, function(error, updates) { - if (error != null) { return callback(error); } - return callback(null, updates); - }); - }); - }, + getDocUpdatesWithUserInfo(project_id, doc_id, options, callback) { + if (options == null) { + options = {} + } + if (callback == null) { + callback = function(error, updates) {} + } + return UpdatesManager.getDocUpdates(project_id, doc_id, options, function( + error, + updates + ) { + if (error != null) { + return callback(error) + } + return UpdatesManager.fillUserInfo(updates, function(error, updates) { + if (error != null) { + return callback(error) + } + return callback(null, updates) + }) + }) + }, - getDocUpdatesWithUserInfo(project_id, doc_id, options, callback) { - if (options == null) { options = {}; } - if (callback == null) { callback = function(error, updates) {}; } - return UpdatesManager.getDocUpdates(project_id, doc_id, options, function(error, updates) { - if (error != null) { return callback(error); } - return UpdatesManager.fillUserInfo(updates, function(error, updates) { - if (error != null) { return callback(error); } - return callback(null, updates); - }); - }); - }, + getSummarizedProjectUpdates(project_id, options, callback) { + if (options == null) { + options = {} + } + if (callback == null) { + callback = function(error, updates) {} + } + if (!options.min_count) { + options.min_count = 25 + } + let summarizedUpdates = [] + const { before } = options + let nextBeforeTimestamp = null + return UpdatesManager.processUncompressedUpdatesForProject( + project_id, + function(error) { + if (error != null) { + return callback(error) + } + return PackManager.makeProjectIterator(project_id, before, function( + err, + iterator + ) { + if (err != null) { + return callback(err) + } + // repeatedly get updates and pass them through the summariser to get an final output with user info + return async.whilst( + () => + // console.log "checking iterator.done", iterator.done() + summarizedUpdates.length < options.min_count && !iterator.done(), - getSummarizedProjectUpdates(project_id, options, callback) { - if (options == null) { options = {}; } - if (callback == null) { callback = function(error, updates) {}; } - if (!options.min_count) { options.min_count = 25; } - let summarizedUpdates = []; - const { before } = options; - let nextBeforeTimestamp = null; - return UpdatesManager.processUncompressedUpdatesForProject(project_id, function(error) { - if (error != null) { return callback(error); } - return PackManager.makeProjectIterator(project_id, before, function(err, iterator) { - if (err != null) { return callback(err); } - // repeatedly get updates and pass them through the summariser to get an final output with user info - return async.whilst(() => - // console.log "checking iterator.done", iterator.done() - (summarizedUpdates.length < options.min_count) && !iterator.done() - - , cb => - iterator.next(function(err, partialUpdates) { - if (err != null) { return callback(err); } - // logger.log {partialUpdates}, 'got partialUpdates' - if (partialUpdates.length === 0) { return cb(); } // # FIXME should try to avoid this happening - nextBeforeTimestamp = partialUpdates[partialUpdates.length - 1].meta.end_ts; - // add the updates to the summary list - summarizedUpdates = UpdatesManager._summarizeUpdates(partialUpdates, summarizedUpdates); - return cb(); - }) - - , () => - // finally done all updates - // console.log 'summarized Updates', summarizedUpdates - UpdatesManager.fillSummarizedUserInfo(summarizedUpdates, function(err, results) { - if (err != null) { return callback(err); } - return callback(null, results, !iterator.done() ? nextBeforeTimestamp : undefined); - }) - ); - }); - }); - }, + cb => + iterator.next(function(err, partialUpdates) { + if (err != null) { + return callback(err) + } + // logger.log {partialUpdates}, 'got partialUpdates' + if (partialUpdates.length === 0) { + return cb() + } // # FIXME should try to avoid this happening + nextBeforeTimestamp = + partialUpdates[partialUpdates.length - 1].meta.end_ts + // add the updates to the summary list + summarizedUpdates = UpdatesManager._summarizeUpdates( + partialUpdates, + summarizedUpdates + ) + return cb() + }), - fetchUserInfo(users, callback) { - if (callback == null) { callback = function(error, fetchedUserInfo) {}; } - const jobs = []; - const fetchedUserInfo = {}; - for (const user_id in users) { - (user_id => - jobs.push(callback => - WebApiManager.getUserInfo(user_id, function(error, userInfo) { - if (error != null) { return callback(error); } - fetchedUserInfo[user_id] = userInfo; - return callback(); - }) - ) - )(user_id); - } + () => + // finally done all updates + // console.log 'summarized Updates', summarizedUpdates + UpdatesManager.fillSummarizedUserInfo(summarizedUpdates, function( + err, + results + ) { + if (err != null) { + return callback(err) + } + return callback( + null, + results, + !iterator.done() ? nextBeforeTimestamp : undefined + ) + }) + ) + }) + } + ) + }, - return async.series(jobs, function(err) { - if (err != null) { return callback(err); } - return callback(null, fetchedUserInfo); - }); - }, + fetchUserInfo(users, callback) { + if (callback == null) { + callback = function(error, fetchedUserInfo) {} + } + const jobs = [] + const fetchedUserInfo = {} + for (const user_id in users) { + ;(user_id => + jobs.push(callback => + WebApiManager.getUserInfo(user_id, function(error, userInfo) { + if (error != null) { + return callback(error) + } + fetchedUserInfo[user_id] = userInfo + return callback() + }) + ))(user_id) + } - fillUserInfo(updates, callback) { - let update, user_id; - if (callback == null) { callback = function(error, updates) {}; } - const users = {}; - for (update of Array.from(updates)) { - ({ user_id } = update.meta); - if (UpdatesManager._validUserId(user_id)) { - users[user_id] = true; - } - } + return async.series(jobs, function(err) { + if (err != null) { + return callback(err) + } + return callback(null, fetchedUserInfo) + }) + }, - return UpdatesManager.fetchUserInfo(users, function(error, fetchedUserInfo) { - if (error != null) { return callback(error); } - for (update of Array.from(updates)) { - ({ user_id } = update.meta); - delete update.meta.user_id; - if (UpdatesManager._validUserId(user_id)) { - update.meta.user = fetchedUserInfo[user_id]; - } - } - return callback(null, updates); - }); - }, + fillUserInfo(updates, callback) { + let update, user_id + if (callback == null) { + callback = function(error, updates) {} + } + const users = {} + for (update of Array.from(updates)) { + ;({ user_id } = update.meta) + if (UpdatesManager._validUserId(user_id)) { + users[user_id] = true + } + } - fillSummarizedUserInfo(updates, callback) { - let update, user_id, user_ids; - if (callback == null) { callback = function(error, updates) {}; } - const users = {}; - for (update of Array.from(updates)) { - user_ids = update.meta.user_ids || []; - for (user_id of Array.from(user_ids)) { - if (UpdatesManager._validUserId(user_id)) { - users[user_id] = true; - } - } - } + return UpdatesManager.fetchUserInfo(users, function( + error, + fetchedUserInfo + ) { + if (error != null) { + return callback(error) + } + for (update of Array.from(updates)) { + ;({ user_id } = update.meta) + delete update.meta.user_id + if (UpdatesManager._validUserId(user_id)) { + update.meta.user = fetchedUserInfo[user_id] + } + } + return callback(null, updates) + }) + }, - return UpdatesManager.fetchUserInfo(users, function(error, fetchedUserInfo) { - if (error != null) { return callback(error); } - for (update of Array.from(updates)) { - user_ids = update.meta.user_ids || []; - update.meta.users = []; - delete update.meta.user_ids; - for (user_id of Array.from(user_ids)) { - if (UpdatesManager._validUserId(user_id)) { - update.meta.users.push(fetchedUserInfo[user_id]); - } else { - update.meta.users.push(null); - } - } - } - return callback(null, updates); - }); - }, + fillSummarizedUserInfo(updates, callback) { + let update, user_id, user_ids + if (callback == null) { + callback = function(error, updates) {} + } + const users = {} + for (update of Array.from(updates)) { + user_ids = update.meta.user_ids || [] + for (user_id of Array.from(user_ids)) { + if (UpdatesManager._validUserId(user_id)) { + users[user_id] = true + } + } + } - _validUserId(user_id) { - if ((user_id == null)) { - return false; - } else { - return !!user_id.match(/^[a-f0-9]{24}$/); - } - }, + return UpdatesManager.fetchUserInfo(users, function( + error, + fetchedUserInfo + ) { + if (error != null) { + return callback(error) + } + for (update of Array.from(updates)) { + user_ids = update.meta.user_ids || [] + update.meta.users = [] + delete update.meta.user_ids + for (user_id of Array.from(user_ids)) { + if (UpdatesManager._validUserId(user_id)) { + update.meta.users.push(fetchedUserInfo[user_id]) + } else { + update.meta.users.push(null) + } + } + } + return callback(null, updates) + }) + }, - TIME_BETWEEN_DISTINCT_UPDATES: (fiveMinutes = 5 * 60 * 1000), - SPLIT_ON_DELETE_SIZE: 16, // characters - _summarizeUpdates(updates, existingSummarizedUpdates) { - if (existingSummarizedUpdates == null) { existingSummarizedUpdates = []; } - const summarizedUpdates = existingSummarizedUpdates.slice(); - let previousUpdateWasBigDelete = false; - for (const update of Array.from(updates)) { - var doc_id; - const earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1]; - let shouldConcat = false; + _validUserId(user_id) { + if (user_id == null) { + return false + } else { + return !!user_id.match(/^[a-f0-9]{24}$/) + } + }, - // If a user inserts some text, then deletes a big chunk including that text, - // the update we show might concat the insert and delete, and there will be no sign - // of that insert having happened, or be able to restore to it (restoring after a big delete is common). - // So, we split the summary on 'big' deletes. However, we've stepping backwards in time with - // most recent changes considered first, so if this update is a big delete, we want to start - // a new summarized update next timge, hence we monitor the previous update. - if (previousUpdateWasBigDelete) { - shouldConcat = false; - } else if (earliestUpdate && ((earliestUpdate.meta.end_ts - update.meta.start_ts) < this.TIME_BETWEEN_DISTINCT_UPDATES)) { - // We're going backwards in time through the updates, so only combine if this update starts less than 5 minutes before - // the end of current summarized block, so no block spans more than 5 minutes. - shouldConcat = true; - } + TIME_BETWEEN_DISTINCT_UPDATES: (fiveMinutes = 5 * 60 * 1000), + SPLIT_ON_DELETE_SIZE: 16, // characters + _summarizeUpdates(updates, existingSummarizedUpdates) { + if (existingSummarizedUpdates == null) { + existingSummarizedUpdates = [] + } + const summarizedUpdates = existingSummarizedUpdates.slice() + let previousUpdateWasBigDelete = false + for (const update of Array.from(updates)) { + var doc_id + const earliestUpdate = summarizedUpdates[summarizedUpdates.length - 1] + let shouldConcat = false - let isBigDelete = false; - for (const op of Array.from(update.op || [])) { - if ((op.d != null) && (op.d.length > this.SPLIT_ON_DELETE_SIZE)) { - isBigDelete = true; - } - } + // If a user inserts some text, then deletes a big chunk including that text, + // the update we show might concat the insert and delete, and there will be no sign + // of that insert having happened, or be able to restore to it (restoring after a big delete is common). + // So, we split the summary on 'big' deletes. However, we've stepping backwards in time with + // most recent changes considered first, so if this update is a big delete, we want to start + // a new summarized update next timge, hence we monitor the previous update. + if (previousUpdateWasBigDelete) { + shouldConcat = false + } else if ( + earliestUpdate && + earliestUpdate.meta.end_ts - update.meta.start_ts < + this.TIME_BETWEEN_DISTINCT_UPDATES + ) { + // We're going backwards in time through the updates, so only combine if this update starts less than 5 minutes before + // the end of current summarized block, so no block spans more than 5 minutes. + shouldConcat = true + } - previousUpdateWasBigDelete = isBigDelete; + let isBigDelete = false + for (const op of Array.from(update.op || [])) { + if (op.d != null && op.d.length > this.SPLIT_ON_DELETE_SIZE) { + isBigDelete = true + } + } - if (shouldConcat) { - // check if the user in this update is already present in the earliest update, - // if not, add them to the users list of the earliest update - earliestUpdate.meta.user_ids = _.union(earliestUpdate.meta.user_ids, [update.meta.user_id]); + previousUpdateWasBigDelete = isBigDelete - doc_id = update.doc_id.toString(); - const doc = earliestUpdate.docs[doc_id]; - if (doc != null) { - doc.fromV = Math.min(doc.fromV, update.v); - doc.toV = Math.max(doc.toV, update.v); - } else { - earliestUpdate.docs[doc_id] = { - fromV: update.v, - toV: update.v - }; - } + if (shouldConcat) { + // check if the user in this update is already present in the earliest update, + // if not, add them to the users list of the earliest update + earliestUpdate.meta.user_ids = _.union(earliestUpdate.meta.user_ids, [ + update.meta.user_id + ]) - earliestUpdate.meta.start_ts = Math.min(earliestUpdate.meta.start_ts, update.meta.start_ts); - earliestUpdate.meta.end_ts = Math.max(earliestUpdate.meta.end_ts, update.meta.end_ts); - } else { - const newUpdate = { - meta: { - user_ids: [], - start_ts: update.meta.start_ts, - end_ts: update.meta.end_ts - }, - docs: {} - }; + doc_id = update.doc_id.toString() + const doc = earliestUpdate.docs[doc_id] + if (doc != null) { + doc.fromV = Math.min(doc.fromV, update.v) + doc.toV = Math.max(doc.toV, update.v) + } else { + earliestUpdate.docs[doc_id] = { + fromV: update.v, + toV: update.v + } + } - newUpdate.docs[update.doc_id.toString()] = { - fromV: update.v, - toV: update.v - }; - newUpdate.meta.user_ids.push(update.meta.user_id); - summarizedUpdates.push(newUpdate); - } - } + earliestUpdate.meta.start_ts = Math.min( + earliestUpdate.meta.start_ts, + update.meta.start_ts + ) + earliestUpdate.meta.end_ts = Math.max( + earliestUpdate.meta.end_ts, + update.meta.end_ts + ) + } else { + const newUpdate = { + meta: { + user_ids: [], + start_ts: update.meta.start_ts, + end_ts: update.meta.end_ts + }, + docs: {} + } - return summarizedUpdates; - } -}); + newUpdate.docs[update.doc_id.toString()] = { + fromV: update.v, + toV: update.v + } + newUpdate.meta.user_ids.push(update.meta.user_id) + summarizedUpdates.push(newUpdate) + } + } + + return summarizedUpdates + } +} function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/app/js/WebApiManager.js b/services/track-changes/app/js/WebApiManager.js index f31c4a649e..937b767ab2 100644 --- a/services/track-changes/app/js/WebApiManager.js +++ b/services/track-changes/app/js/WebApiManager.js @@ -10,96 +10,107 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let WebApiManager; -const request = require("requestretry"); // allow retry on error https://github.com/FGRibreau/node-request-retry -const logger = require("logger-sharelatex"); -const Settings = require("settings-sharelatex"); +let WebApiManager +const request = require('requestretry') // allow retry on error https://github.com/FGRibreau/node-request-retry +const logger = require('logger-sharelatex') +const Settings = require('settings-sharelatex') // Don't let HTTP calls hang for a long time -const MAX_HTTP_REQUEST_LENGTH = 15000; // 15 seconds +const MAX_HTTP_REQUEST_LENGTH = 15000 // 15 seconds // DEPRECATED! This method of getting user details via track-changes is deprecated // in the way we lay out our services. // Instead, web should be responsible for collecting the raw data (user_ids) and // filling it out with calls to other services. All API calls should create a // tree-like structure as much as possible, with web as the root. -module.exports = (WebApiManager = { - sendRequest(url, callback) { - if (callback == null) { callback = function(error, body) {}; } - return request.get({ - url: `${Settings.apis.web.url}${url}`, - timeout: MAX_HTTP_REQUEST_LENGTH, - maxAttempts: 2, // for node-request-retry - auth: { - user: Settings.apis.web.user, - pass: Settings.apis.web.pass, - sendImmediately: true - } - }, function(error, res, body){ - if (error != null) { - return callback(error); - } - if (res.statusCode === 404) { - logger.log({url}, "got 404 from web api"); - return callback(null, null); - } - if ((res.statusCode >= 200) && (res.statusCode < 300)) { - return callback(null, body); - } else { - error = new Error(`web returned a non-success status code: ${res.statusCode} (attempts: ${res.attempts})`); - return callback(error); - } - }); - }, +module.exports = WebApiManager = { + sendRequest(url, callback) { + if (callback == null) { + callback = function(error, body) {} + } + return request.get( + { + url: `${Settings.apis.web.url}${url}`, + timeout: MAX_HTTP_REQUEST_LENGTH, + maxAttempts: 2, // for node-request-retry + auth: { + user: Settings.apis.web.user, + pass: Settings.apis.web.pass, + sendImmediately: true + } + }, + function(error, res, body) { + if (error != null) { + return callback(error) + } + if (res.statusCode === 404) { + logger.log({ url }, 'got 404 from web api') + return callback(null, null) + } + if (res.statusCode >= 200 && res.statusCode < 300) { + return callback(null, body) + } else { + error = new Error( + `web returned a non-success status code: ${res.statusCode} (attempts: ${res.attempts})` + ) + return callback(error) + } + } + ) + }, - getUserInfo(user_id, callback) { - if (callback == null) { callback = function(error, userInfo) {}; } - const url = `/user/${user_id}/personal_info`; - logger.log({user_id}, "getting user info from web"); - return WebApiManager.sendRequest(url, function(error, body) { - let user; - if (error != null) { - logger.error({err: error, user_id, url}, "error accessing web"); - return callback(error); - } + getUserInfo(user_id, callback) { + if (callback == null) { + callback = function(error, userInfo) {} + } + const url = `/user/${user_id}/personal_info` + logger.log({ user_id }, 'getting user info from web') + return WebApiManager.sendRequest(url, function(error, body) { + let user + if (error != null) { + logger.error({ err: error, user_id, url }, 'error accessing web') + return callback(error) + } - if (body === null) { - logger.error({user_id, url}, "no user found"); - return callback(null, null); - } - try { - user = JSON.parse(body); - } catch (error1) { - error = error1; - return callback(error); - } - return callback(null, { - id: user.id, - email: user.email, - first_name: user.first_name, - last_name: user.last_name - }); - }); - }, + if (body === null) { + logger.error({ user_id, url }, 'no user found') + return callback(null, null) + } + try { + user = JSON.parse(body) + } catch (error1) { + error = error1 + return callback(error) + } + return callback(null, { + id: user.id, + email: user.email, + first_name: user.first_name, + last_name: user.last_name + }) + }) + }, - getProjectDetails(project_id, callback) { - if (callback == null) { callback = function(error, details) {}; } - const url = `/project/${project_id}/details`; - logger.log({project_id}, "getting project details from web"); - return WebApiManager.sendRequest(url, function(error, body) { - let project; - if (error != null) { - logger.error({err: error, project_id, url}, "error accessing web"); - return callback(error); - } + getProjectDetails(project_id, callback) { + if (callback == null) { + callback = function(error, details) {} + } + const url = `/project/${project_id}/details` + logger.log({ project_id }, 'getting project details from web') + return WebApiManager.sendRequest(url, function(error, body) { + let project + if (error != null) { + logger.error({ err: error, project_id, url }, 'error accessing web') + return callback(error) + } - try { - project = JSON.parse(body); - } catch (error1) { - error = error1; - return callback(error); - } - return callback(null, project); - }); - } -}); + try { + project = JSON.parse(body) + } catch (error1) { + error = error1 + return callback(error) + } + return callback(null, project) + }) + } +} diff --git a/services/track-changes/app/js/mongojs.js b/services/track-changes/app/js/mongojs.js index 9dc03952e5..6b99f87a4c 100644 --- a/services/track-changes/app/js/mongojs.js +++ b/services/track-changes/app/js/mongojs.js @@ -1,12 +1,15 @@ // TODO: This file was created by bulk-decaffeinate. // Sanity-check the conversion and remove this comment. -const Settings = require("settings-sharelatex"); -const mongojs = require("mongojs"); -const bson = require("bson"); -const db = mongojs(Settings.mongo.url, ["docHistory", "projectHistoryMetaData", "docHistoryIndex"]); +const Settings = require('settings-sharelatex') +const mongojs = require('mongojs') +const bson = require('bson') +const db = mongojs(Settings.mongo.url, [ + 'docHistory', + 'projectHistoryMetaData', + 'docHistoryIndex' +]) module.exports = { - db, - ObjectId: mongojs.ObjectId, - BSON: new bson.BSONPure() -}; - + db, + ObjectId: mongojs.ObjectId, + BSON: new bson.BSONPure() +} From fa5256d3c2254786504b796cdbd3a7cfde355adf Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:34:39 +0100 Subject: [PATCH 470/549] decaffeinate: Rename DiffGeneratorTests.coffee and 13 other files from .coffee to .js --- .../{DiffGeneratorTests.coffee => DiffGeneratorTests.js} | 0 .../DiffManager/{DiffManagerTests.coffee => DiffManagerTests.js} | 0 .../test/unit/coffee/DocArchive/{MongoAWS.coffee => MongoAWS.js} | 0 ...tUpdaterManagerTests.coffee => DocumentUpdaterManagerTests.js} | 0 .../{HttpControllerTests.coffee => HttpControllerTests.js} | 0 .../LockManager/{LockManagerTests.coffee => LockManagerTests.js} | 0 .../{MongoManagerTests.coffee => MongoManagerTests.js} | 0 .../PackManager/{PackManagerTests.coffee => PackManagerTests.js} | 0 .../{RedisManagerTests.coffee => RedisManagerTests.js} | 0 .../{RestoreManagerTests.coffee => RestoreManagerTests.js} | 0 .../{UpdateCompressorTests.coffee => UpdateCompressorTests.js} | 0 .../{UpdateTrimmerTests.coffee => UpdateTrimmerTests.js} | 0 .../{UpdatesManagerTests.coffee => UpdatesManagerTests.js} | 0 .../{WebApiManagerTests.coffee => WebApiManagerTests.js} | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/test/unit/coffee/DiffGenerator/{DiffGeneratorTests.coffee => DiffGeneratorTests.js} (100%) rename services/track-changes/test/unit/coffee/DiffManager/{DiffManagerTests.coffee => DiffManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/DocArchive/{MongoAWS.coffee => MongoAWS.js} (100%) rename services/track-changes/test/unit/coffee/DocumentUpdaterManager/{DocumentUpdaterManagerTests.coffee => DocumentUpdaterManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/HttpController/{HttpControllerTests.coffee => HttpControllerTests.js} (100%) rename services/track-changes/test/unit/coffee/LockManager/{LockManagerTests.coffee => LockManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/MongoManager/{MongoManagerTests.coffee => MongoManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/PackManager/{PackManagerTests.coffee => PackManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/RedisManager/{RedisManagerTests.coffee => RedisManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/RestoreManager/{RestoreManagerTests.coffee => RestoreManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/UpdateCompressor/{UpdateCompressorTests.coffee => UpdateCompressorTests.js} (100%) rename services/track-changes/test/unit/coffee/UpdateTrimmer/{UpdateTrimmerTests.coffee => UpdateTrimmerTests.js} (100%) rename services/track-changes/test/unit/coffee/UpdatesManager/{UpdatesManagerTests.coffee => UpdatesManagerTests.js} (100%) rename services/track-changes/test/unit/coffee/WebApiManager/{WebApiManagerTests.coffee => WebApiManagerTests.js} (100%) diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.coffee rename to services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee rename to services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js similarity index 100% rename from services/track-changes/test/unit/coffee/DocArchive/MongoAWS.coffee rename to services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.coffee rename to services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.coffee rename to services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/LockManager/LockManagerTests.coffee rename to services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee rename to services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/PackManager/PackManagerTests.coffee rename to services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.coffee rename to services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js diff --git a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.coffee rename to services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.coffee rename to services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.coffee rename to services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.coffee rename to services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.coffee rename to services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js From 86a9593ec0e27e21aa1bc04e6c6f1ba352082712 Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:34:50 +0100 Subject: [PATCH 471/549] decaffeinate: Convert DiffGeneratorTests.coffee and 13 other files to JS --- .../DiffGenerator/DiffGeneratorTests.js | 617 ++++--- .../coffee/DiffManager/DiffManagerTests.js | 455 ++--- .../test/unit/coffee/DocArchive/MongoAWS.js | 136 +- .../DocumentUpdaterManagerTests.js | 184 +- .../HttpController/HttpControllerTests.js | 274 +-- .../coffee/LockManager/LockManagerTests.js | 389 +++-- .../coffee/MongoManager/MongoManagerTests.js | 273 +-- .../coffee/PackManager/PackManagerTests.js | 653 ++++--- .../coffee/RedisManager/RedisManagerTests.js | 176 +- .../RestoreManager/RestoreManagerTests.js | 80 +- .../UpdateCompressor/UpdateCompressorTests.js | 756 +++++---- .../UpdateTrimmer/UpdateTrimmerTests.js | 227 +-- .../UpdatesManager/UpdatesManagerTests.js | 1494 ++++++++++------- .../WebApiManager/WebApiManagerTests.js | 229 +-- 14 files changed, 3409 insertions(+), 2534 deletions(-) diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js index b956606a7d..73379306fb 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js @@ -1,344 +1,409 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/DiffGenerator.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/DiffGenerator.js"; +const SandboxedModule = require('sandboxed-module'); -describe "DiffGenerator", -> - beforeEach -> - @DiffGenerator = SandboxedModule.require modulePath, requires: +describe("DiffGenerator", function() { + beforeEach(function() { + this.DiffGenerator = SandboxedModule.require(modulePath, { requires: { "logger-sharelatex": { warn: sinon.stub() } - @ts = Date.now() - @user_id = "mock-user-id" - @user_id_2 = "mock-user-id-2" - @meta = { - start_ts: @ts, end_ts: @ts, user_id: @user_id } + }); + this.ts = Date.now(); + this.user_id = "mock-user-id"; + this.user_id_2 = "mock-user-id-2"; + return this.meta = { + start_ts: this.ts, end_ts: this.ts, user_id: this.user_id + };}); - describe "rewindOp", -> - describe "rewinding an insert", -> - it "should undo the insert", -> - content = "hello world" - rewoundContent = @DiffGenerator.rewindOp content, { p: 6, i: "wo" } - rewoundContent.should.equal "hello rld" + describe("rewindOp", function() { + describe("rewinding an insert", () => + it("should undo the insert", function() { + const content = "hello world"; + const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, i: "wo" }); + return rewoundContent.should.equal("hello rld"); + }) + ); - describe "rewinding a delete", -> - it "should undo the delete", -> - content = "hello rld" - rewoundContent = @DiffGenerator.rewindOp content, { p: 6, d: "wo" } - rewoundContent.should.equal "hello world" + describe("rewinding a delete", () => + it("should undo the delete", function() { + const content = "hello rld"; + const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, d: "wo" }); + return rewoundContent.should.equal("hello world"); + }) + ); - describe "with an inconsistent update", -> - it "should throw an error", -> - content = "hello world" - expect( () => - @DiffGenerator.rewindOp content, { p: 6, i: "foo" } - ).to.throw(@DiffGenerator.ConsistencyError) + describe("with an inconsistent update", () => + it("should throw an error", function() { + const content = "hello world"; + return expect( () => { + return this.DiffGenerator.rewindOp(content, { p: 6, i: "foo" }); + }).to.throw(this.DiffGenerator.ConsistencyError); + }) + ); - describe "with an update which is beyond the length of the content", -> - it "should undo the insert as if it were at the end of the content", -> - content = "foobar" - rewoundContent = @DiffGenerator.rewindOp content, { p: 4, i: "bar" } - rewoundContent.should.equal "foo" + return describe("with an update which is beyond the length of the content", () => + it("should undo the insert as if it were at the end of the content", function() { + const content = "foobar"; + const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 4, i: "bar" }); + return rewoundContent.should.equal("foo"); + }) + ); + }); - describe "rewindUpdate", -> - it "should rewind ops in reverse", -> - content = "aaabbbccc" - update = - op: [{ p: 3, i: "bbb" }, { p: 6, i: "ccc" }] - rewoundContent = @DiffGenerator.rewindUpdate content, update - rewoundContent.should.equal "aaa" + describe("rewindUpdate", () => + it("should rewind ops in reverse", function() { + const content = "aaabbbccc"; + const update = + {op: [{ p: 3, i: "bbb" }, { p: 6, i: "ccc" }]}; + const rewoundContent = this.DiffGenerator.rewindUpdate(content, update); + return rewoundContent.should.equal("aaa"); + }) + ); - describe "rewindUpdates", -> - it "should rewind updates in reverse", -> - content = "aaabbbccc" - updates = [ + describe("rewindUpdates", () => + it("should rewind updates in reverse", function() { + const content = "aaabbbccc"; + const updates = [ { op: [{ p: 3, i: "bbb" }] }, { op: [{ p: 6, i: "ccc" }] } - ] - rewoundContent = @DiffGenerator.rewindUpdates content, updates - rewoundContent.should.equal "aaa" + ]; + const rewoundContent = this.DiffGenerator.rewindUpdates(content, updates); + return rewoundContent.should.equal("aaa"); + }) + ); - describe "buildDiff", -> - beforeEach -> - @diff = [ u: "mock-diff" ] - @content = "Hello world" - @updates = [ - { i: "mock-update-1" } - { i: "mock-update-2" } + describe("buildDiff", function() { + beforeEach(function() { + this.diff = [ {u: "mock-diff"} ]; + this.content = "Hello world"; + this.updates = [ + { i: "mock-update-1" }, + { i: "mock-update-2" }, { i: "mock-update-3" } - ] - @DiffGenerator.applyUpdateToDiff = sinon.stub().returns(@diff) - @DiffGenerator.compressDiff = sinon.stub().returns(@diff) - @result = @DiffGenerator.buildDiff(@content, @updates) + ]; + this.DiffGenerator.applyUpdateToDiff = sinon.stub().returns(this.diff); + this.DiffGenerator.compressDiff = sinon.stub().returns(this.diff); + return this.result = this.DiffGenerator.buildDiff(this.content, this.updates); + }); - it "should return the diff", -> - @result.should.deep.equal @diff + it("should return the diff", function() { + return this.result.should.deep.equal(this.diff); + }); - it "should build the content into an initial diff", -> - @DiffGenerator.applyUpdateToDiff + it("should build the content into an initial diff", function() { + return this.DiffGenerator.applyUpdateToDiff .calledWith([{ - u: @content - }], @updates[0]) - .should.equal true + u: this.content + }], this.updates[0]) + .should.equal(true); + }); - it "should apply each update", -> - for update in @updates - @DiffGenerator.applyUpdateToDiff + it("should apply each update", function() { + return Array.from(this.updates).map((update) => + this.DiffGenerator.applyUpdateToDiff .calledWith(sinon.match.any, update) - .should.equal true + .should.equal(true)); + }); - it "should compress the diff", -> - @DiffGenerator.compressDiff - .calledWith(@diff) - .should.equal true + return it("should compress the diff", function() { + return this.DiffGenerator.compressDiff + .calledWith(this.diff) + .should.equal(true); + }); + }); - describe "compressDiff", -> - describe "with adjacent inserts with the same user_id", -> - it "should create one update with combined meta data and min/max timestamps", -> - diff = @DiffGenerator.compressDiff([ - { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} - { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id } }} - ]) - expect(diff).to.deep.equal([ - { i: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: @user_id } }} - ]) + describe("compressDiff", function() { + describe("with adjacent inserts with the same user_id", () => + it("should create one update with combined meta data and min/max timestamps", function() { + const diff = this.DiffGenerator.compressDiff([ + { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, + { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }} + ]); + return expect(diff).to.deep.equal([ + { i: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }} + ]); + }) + ); - describe "with adjacent inserts with different user_ids", -> - it "should leave the inserts unchanged", -> - input = [ - { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} - { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id_2 } }} - ] - output = @DiffGenerator.compressDiff(input) - expect(output).to.deep.equal(input) + describe("with adjacent inserts with different user_ids", () => + it("should leave the inserts unchanged", function() { + const input = [ + { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, + { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }} + ]; + const output = this.DiffGenerator.compressDiff(input); + return expect(output).to.deep.equal(input); + }) + ); - describe "with adjacent deletes with the same user_id", -> - it "should create one update with combined meta data and min/max timestamps", -> - diff = @DiffGenerator.compressDiff([ - { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} - { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id } }} - ]) - expect(diff).to.deep.equal([ - { d: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: @user_id } }} - ]) + describe("with adjacent deletes with the same user_id", () => + it("should create one update with combined meta data and min/max timestamps", function() { + const diff = this.DiffGenerator.compressDiff([ + { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, + { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }} + ]); + return expect(diff).to.deep.equal([ + { d: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }} + ]); + }) + ); - describe "with adjacent deletes with different user_ids", -> - it "should leave the deletes unchanged", -> - input = [ - { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: @user_id } }} - { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: @user_id_2 } }} - ] - output = @DiffGenerator.compressDiff(input) - expect(output).to.deep.equal(input) + return describe("with adjacent deletes with different user_ids", () => + it("should leave the deletes unchanged", function() { + const input = [ + { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, + { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }} + ]; + const output = this.DiffGenerator.compressDiff(input); + return expect(output).to.deep.equal(input); + }) + ); + }); - describe "applyUpdateToDiff", -> - describe "an insert", -> - it "should insert into the middle of (u)nchanged text", -> - diff = @DiffGenerator.applyUpdateToDiff( + return describe("applyUpdateToDiff", function() { + describe("an insert", function() { + it("should insert into the middle of (u)nchanged text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foobar" } ], - { op: [{ p: 3, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "foo" } - { i: "baz", meta: @meta } + { op: [{ p: 3, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "foo" }, + { i: "baz", meta: this.meta }, { u: "bar" } - ]) + ]); + }); - it "should insert into the start of (u)changed text", -> - diff = @DiffGenerator.applyUpdateToDiff( + it("should insert into the start of (u)changed text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foobar" } ], - { op: [{ p: 0, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { i: "baz", meta: @meta } + { op: [{ p: 0, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { i: "baz", meta: this.meta }, { u: "foobar" } - ]) + ]); + }); - it "should insert into the end of (u)changed text", -> - diff = @DiffGenerator.applyUpdateToDiff( + it("should insert into the end of (u)changed text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foobar" } ], - { op: [{ p: 6, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "foobar" } - { i: "baz", meta: @meta } - ]) + { op: [{ p: 6, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "foobar" }, + { i: "baz", meta: this.meta } + ]); + }); - it "should insert into the middle of (i)inserted text", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { i: "foobar", meta: @meta } ], - { op: [{ p: 3, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { i: "foo", meta: @meta } - { i: "baz", meta: @meta } - { i: "bar", meta: @meta } - ]) + it("should insert into the middle of (i)inserted text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { i: "foobar", meta: this.meta } ], + { op: [{ p: 3, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { i: "foo", meta: this.meta }, + { i: "baz", meta: this.meta }, + { i: "bar", meta: this.meta } + ]); + }); - it "should not count deletes in the running length total", -> - diff = @DiffGenerator.applyUpdateToDiff( + return it("should not count deletes in the running length total", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ - { d: "deleted", meta: @meta } + { d: "deleted", meta: this.meta }, { u: "foobar" } ], - { op: [{ p: 3, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { d: "deleted", meta: @meta } - { u: "foo" } - { i: "baz", meta: @meta } + { op: [{ p: 3, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { d: "deleted", meta: this.meta }, + { u: "foo" }, + { i: "baz", meta: this.meta }, { u: "bar" } - ]) + ]); + }); + }); - describe "a delete", -> - describe "deleting unchanged text", -> - it "should delete from the middle of (u)nchanged text", -> - diff = @DiffGenerator.applyUpdateToDiff( + return describe("a delete", function() { + describe("deleting unchanged text", function() { + it("should delete from the middle of (u)nchanged text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: [{ p: 3, d: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "foo" } - { d: "baz", meta: @meta } + { op: [{ p: 3, d: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "foo" }, + { d: "baz", meta: this.meta }, { u: "bar" } - ]) + ]); + }); - it "should delete from the start of (u)nchanged text", -> - diff = @DiffGenerator.applyUpdateToDiff( + it("should delete from the start of (u)nchanged text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: [{ p: 0, d: "foo" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { d: "foo", meta: @meta } + { op: [{ p: 0, d: "foo" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { d: "foo", meta: this.meta }, { u: "bazbar" } - ]) + ]); + }); - it "should delete from the end of (u)nchanged text", -> - diff = @DiffGenerator.applyUpdateToDiff( + it("should delete from the end of (u)nchanged text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: [{ p: 6, d: "bar" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "foobaz" } - { d: "bar", meta: @meta } - ]) + { op: [{ p: 6, d: "bar" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "foobaz" }, + { d: "bar", meta: this.meta } + ]); + }); - it "should delete across multiple (u)changed text parts", -> - diff = @DiffGenerator.applyUpdateToDiff( + return it("should delete across multiple (u)changed text parts", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foo" }, { u: "baz" }, { u: "bar" } ], - { op: [{ p: 2, d: "obazb" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "fo" } - { d: "o", meta: @meta } - { d: "baz", meta: @meta } - { d: "b", meta: @meta } + { op: [{ p: 2, d: "obazb" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "fo" }, + { d: "o", meta: this.meta }, + { d: "baz", meta: this.meta }, + { d: "b", meta: this.meta }, { u: "ar" } - ]) + ]); + }); + }); - describe "deleting inserts", -> - it "should delete from the middle of (i)nserted text", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { i: "foobazbar", meta: @meta } ], - { op: [{ p: 3, d: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { i: "foo", meta: @meta } - { i: "bar", meta: @meta } - ]) + describe("deleting inserts", function() { + it("should delete from the middle of (i)nserted text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { i: "foobazbar", meta: this.meta } ], + { op: [{ p: 3, d: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { i: "foo", meta: this.meta }, + { i: "bar", meta: this.meta } + ]); + }); - it "should delete from the start of (u)nchanged text", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { i: "foobazbar", meta: @meta } ], - { op: [{ p: 0, d: "foo" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { i: "bazbar", meta: @meta } - ]) + it("should delete from the start of (u)nchanged text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { i: "foobazbar", meta: this.meta } ], + { op: [{ p: 0, d: "foo" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { i: "bazbar", meta: this.meta } + ]); + }); - it "should delete from the end of (u)nchanged text", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { i: "foobazbar", meta: @meta } ], - { op: [{ p: 6, d: "bar" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { i: "foobaz", meta: @meta } - ]) + it("should delete from the end of (u)nchanged text", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { i: "foobazbar", meta: this.meta } ], + { op: [{ p: 6, d: "bar" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { i: "foobaz", meta: this.meta } + ]); + }); - it "should delete across multiple (u)changed and (i)nserted text parts", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { i: "baz", meta: @meta }, { u: "bar" } ], - { op: [{ p: 2, d: "obazb" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "fo" } - { d: "o", meta: @meta } - { d: "b", meta: @meta } + return it("should delete across multiple (u)changed and (i)nserted text parts", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { i: "baz", meta: this.meta }, { u: "bar" } ], + { op: [{ p: 2, d: "obazb" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "fo" }, + { d: "o", meta: this.meta }, + { d: "b", meta: this.meta }, { u: "ar" } - ]) + ]); + }); + }); - describe "deleting over existing deletes", -> - it "should delete across multiple (u)changed and (d)deleted text parts", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { d: "baz", meta: @meta }, { u: "bar" } ], - { op: [{ p: 2, d: "ob" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "fo" } - { d: "o", meta: @meta } - { d: "baz", meta: @meta } - { d: "b", meta: @meta } + describe("deleting over existing deletes", () => + it("should delete across multiple (u)changed and (d)deleted text parts", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { d: "baz", meta: this.meta }, { u: "bar" } ], + { op: [{ p: 2, d: "ob" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "fo" }, + { d: "o", meta: this.meta }, + { d: "baz", meta: this.meta }, + { d: "b", meta: this.meta }, { u: "ar" } - ]) + ]); + }) + ); - describe "deleting when the text doesn't match", -> - it "should throw an error when deleting from the middle of (u)nchanged text", -> - expect( - () => @DiffGenerator.applyUpdateToDiff( + describe("deleting when the text doesn't match", function() { + it("should throw an error when deleting from the middle of (u)nchanged text", function() { + return expect( + () => this.DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: [{ p: 3, d: "xxx" }], meta: @meta } + { op: [{ p: 3, d: "xxx" }], meta: this.meta } ) - ).to.throw(@DiffGenerator.ConsistencyError) + ).to.throw(this.DiffGenerator.ConsistencyError); + }); - it "should throw an error when deleting from the start of (u)nchanged text", -> - expect( - () => @DiffGenerator.applyUpdateToDiff( + it("should throw an error when deleting from the start of (u)nchanged text", function() { + return expect( + () => this.DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: [{ p: 0, d: "xxx" }], meta: @meta } + { op: [{ p: 0, d: "xxx" }], meta: this.meta } ) - ).to.throw(@DiffGenerator.ConsistencyError) + ).to.throw(this.DiffGenerator.ConsistencyError); + }); - it "should throw an error when deleting from the end of (u)nchanged text", -> - expect( - () => @DiffGenerator.applyUpdateToDiff( + return it("should throw an error when deleting from the end of (u)nchanged text", function() { + return expect( + () => this.DiffGenerator.applyUpdateToDiff( [ { u: "foobazbar" } ], - { op: [{ p: 6, d: "xxx" }] , meta: @meta } + { op: [{ p: 6, d: "xxx" }] , meta: this.meta } ) - ).to.throw(@DiffGenerator.ConsistencyError) + ).to.throw(this.DiffGenerator.ConsistencyError); + }); + }); - describe "when the last update in the existing diff is a delete", -> - it "should insert the new update before the delete", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { d: "bar", meta: @meta } ], - { op: [{ p: 3, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { u: "foo" } - { i: "baz", meta: @meta } - { d: "bar", meta: @meta } - ]) + describe("when the last update in the existing diff is a delete", () => + it("should insert the new update before the delete", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { u: "foo" }, { d: "bar", meta: this.meta } ], + { op: [{ p: 3, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { u: "foo" }, + { i: "baz", meta: this.meta }, + { d: "bar", meta: this.meta } + ]); + }) + ); - describe "when the only update in the existing diff is a delete", -> - it "should insert the new update after the delete", -> - diff = @DiffGenerator.applyUpdateToDiff( - [ { d: "bar", meta: @meta } ], - { op: [{ p: 0, i: "baz" }], meta: @meta } - ) - expect(diff).to.deep.equal([ - { d: "bar", meta: @meta } - { i: "baz", meta: @meta } - ]) + return describe("when the only update in the existing diff is a delete", () => + it("should insert the new update after the delete", function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [ { d: "bar", meta: this.meta } ], + { op: [{ p: 0, i: "baz" }], meta: this.meta } + ); + return expect(diff).to.deep.equal([ + { d: "bar", meta: this.meta }, + { i: "baz", meta: this.meta } + ]); + }) + ); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js index f0bca22e90..18e048ee17 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js @@ -1,240 +1,303 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/DiffManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/DiffManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "DiffManager", -> - beforeEach -> - @DiffManager = SandboxedModule.require modulePath, requires: - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() } - "./UpdatesManager": @UpdatesManager = {} - "./DocumentUpdaterManager": @DocumentUpdaterManager = {} - "./DiffGenerator": @DiffGenerator = {} - @callback = sinon.stub() - @from = new Date() - @to = new Date(Date.now() + 10000) - @project_id = "mock-project-id" - @doc_id = "mock-doc-id" +describe("DiffManager", function() { + beforeEach(function() { + this.DiffManager = SandboxedModule.require(modulePath, { requires: { + "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }), + "./UpdatesManager": (this.UpdatesManager = {}), + "./DocumentUpdaterManager": (this.DocumentUpdaterManager = {}), + "./DiffGenerator": (this.DiffGenerator = {}) + } + }); + this.callback = sinon.stub(); + this.from = new Date(); + this.to = new Date(Date.now() + 10000); + this.project_id = "mock-project-id"; + return this.doc_id = "mock-doc-id"; + }); - describe "getLatestDocAndUpdates", -> - beforeEach -> - @content = "hello world" - @version = 42 - @updates = [ "mock-update-1", "mock-update-2" ] + describe("getLatestDocAndUpdates", function() { + beforeEach(function() { + this.content = "hello world"; + this.version = 42; + this.updates = [ "mock-update-1", "mock-update-2" ]; - @DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @content, @version) - @UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates) + this.DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, this.content, this.version); + return this.UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, this.updates); + }); - describe "with a fromVersion", -> - beforeEach -> - @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @callback + describe("with a fromVersion", function() { + beforeEach(function() { + return this.DiffManager.getLatestDocAndUpdates(this.project_id, this.doc_id, this.from, this.callback); + }); - it "should get the latest version of the doc", -> - @DocumentUpdaterManager.getDocument - .calledWith(@project_id, @doc_id) - .should.equal true + it("should get the latest version of the doc", function() { + return this.DocumentUpdaterManager.getDocument + .calledWith(this.project_id, this.doc_id) + .should.equal(true); + }); - it "should get the latest updates", -> - @UpdatesManager.getDocUpdatesWithUserInfo - .calledWith(@project_id, @doc_id, from: @from) - .should.equal true + it("should get the latest updates", function() { + return this.UpdatesManager.getDocUpdatesWithUserInfo + .calledWith(this.project_id, this.doc_id, {from: this.from}) + .should.equal(true); + }); - it "should call the callback with the content, version and updates", -> - @callback.calledWith(null, @content, @version, @updates).should.equal true + return it("should call the callback with the content, version and updates", function() { + return this.callback.calledWith(null, this.content, this.version, this.updates).should.equal(true); + }); + }); - describe "with no fromVersion", -> - beforeEach -> - @DiffManager.getLatestDocAndUpdates @project_id, @doc_id, null, @callback + return describe("with no fromVersion", function() { + beforeEach(function() { + return this.DiffManager.getLatestDocAndUpdates(this.project_id, this.doc_id, null, this.callback); + }); - it "should get the latest version of the doc", -> - @DocumentUpdaterManager.getDocument - .calledWith(@project_id, @doc_id) - .should.equal true + it("should get the latest version of the doc", function() { + return this.DocumentUpdaterManager.getDocument + .calledWith(this.project_id, this.doc_id) + .should.equal(true); + }); - it "should not get the latest updates", -> - @UpdatesManager.getDocUpdatesWithUserInfo - .called.should.equal false + it("should not get the latest updates", function() { + return this.UpdatesManager.getDocUpdatesWithUserInfo + .called.should.equal(false); + }); - it "should call the callback with the content, version and blank updates", -> - @callback.calledWith(null, @content, @version, []).should.equal true + return it("should call the callback with the content, version and blank updates", function() { + return this.callback.calledWith(null, this.content, this.version, []).should.equal(true); + }); + }); + }); - describe "getDiff", -> - beforeEach -> - @content = "hello world" - # Op versions are the version they were applied to, so doc is always one version - # ahead.s - @version = 43 - @updates = [ - { op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} } - { op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} } - { op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} } - { op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} } - ] - @fromVersion = 39 - @toVersion = 40 - @diffed_updates = @updates.slice(2) - @rewound_content = "rewound-content" - @diff = [ u: "mock-diff" ] + describe("getDiff", function() { + beforeEach(function() { + this.content = "hello world"; + // Op versions are the version they were applied to, so doc is always one version + // ahead.s + this.version = 43; + this.updates = [ + { op: "mock-4", v: 42, meta: { start_ts: new Date(this.to.getTime() + 20)} }, + { op: "mock-3", v: 41, meta: { start_ts: new Date(this.to.getTime() + 10)} }, + { op: "mock-2", v: 40, meta: { start_ts: new Date(this.to.getTime() - 10)} }, + { op: "mock-1", v: 39, meta: { start_ts: new Date(this.to.getTime() - 20)} } + ]; + this.fromVersion = 39; + this.toVersion = 40; + this.diffed_updates = this.updates.slice(2); + this.rewound_content = "rewound-content"; + return this.diff = [ {u: "mock-diff"} ];}); - describe "with matching versions", -> - beforeEach -> - @DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, @rewound_content, @updates) - @DiffGenerator.buildDiff = sinon.stub().returns(@diff) - @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback + describe("with matching versions", function() { + beforeEach(function() { + this.DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, this.rewound_content, this.updates); + this.DiffGenerator.buildDiff = sinon.stub().returns(this.diff); + return this.DiffManager.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, this.callback); + }); - it "should get the latest doc and version with all recent updates", -> - @DiffManager.getDocumentBeforeVersion - .calledWith(@project_id, @doc_id, @fromVersion) - .should.equal true + it("should get the latest doc and version with all recent updates", function() { + return this.DiffManager.getDocumentBeforeVersion + .calledWith(this.project_id, this.doc_id, this.fromVersion) + .should.equal(true); + }); - it "should generate the diff", -> - @DiffGenerator.buildDiff - .calledWith(@rewound_content, @diffed_updates.slice().reverse()) - .should.equal true + it("should generate the diff", function() { + return this.DiffGenerator.buildDiff + .calledWith(this.rewound_content, this.diffed_updates.slice().reverse()) + .should.equal(true); + }); - it "should call the callback with the diff", -> - @callback.calledWith(null, @diff).should.equal true + return it("should call the callback with the diff", function() { + return this.callback.calledWith(null, this.diff).should.equal(true); + }); + }); - describe "when the updates are inconsistent", -> - beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) - @DiffGenerator.buildDiff = sinon.stub().throws(@error = new Error("inconsistent!")) - @DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback + return describe("when the updates are inconsistent", function() { + beforeEach(function() { + this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); + this.DiffGenerator.buildDiff = sinon.stub().throws(this.error = new Error("inconsistent!")); + return this.DiffManager.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, this.callback); + }); - it "should call the callback with an error", -> - @callback - .calledWith(@error) - .should.equal true + return it("should call the callback with an error", function() { + return this.callback + .calledWith(this.error) + .should.equal(true); + }); + }); + }); - describe "getDocumentBeforeVersion", -> - beforeEach -> - @DiffManager._tryGetDocumentBeforeVersion = sinon.stub() - @document = "mock-documents" - @rewound_updates = "mock-rewound-updates" + describe("getDocumentBeforeVersion", function() { + beforeEach(function() { + this.DiffManager._tryGetDocumentBeforeVersion = sinon.stub(); + this.document = "mock-documents"; + return this.rewound_updates = "mock-rewound-updates"; + }); - describe "succesfully", -> - beforeEach -> - @DiffManager._tryGetDocumentBeforeVersion.yields(null, @document, @rewound_updates) - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + describe("succesfully", function() { + beforeEach(function() { + this.DiffManager._tryGetDocumentBeforeVersion.yields(null, this.document, this.rewound_updates); + return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); + }); - it "should call _tryGetDocumentBeforeVersion", -> - @DiffManager._tryGetDocumentBeforeVersion - .calledWith(@project_id, @doc_id, @version) - .should.equal true + it("should call _tryGetDocumentBeforeVersion", function() { + return this.DiffManager._tryGetDocumentBeforeVersion + .calledWith(this.project_id, this.doc_id, this.version) + .should.equal(true); + }); - it "should call the callback with the response", -> - @callback.calledWith(null, @document, @rewound_updates).should.equal true + return it("should call the callback with the response", function() { + return this.callback.calledWith(null, this.document, this.rewound_updates).should.equal(true); + }); + }); - describe "with a retry needed", -> - beforeEach -> - retried = false - @DiffManager._tryGetDocumentBeforeVersion = (project_id, doc_id, version, callback) => - if !retried - retried = true - error = new Error() - error.retry = true - callback error - else - callback(null, @document, @rewound_updates) - sinon.spy @DiffManager, "_tryGetDocumentBeforeVersion" - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + describe("with a retry needed", function() { + beforeEach(function() { + let retried = false; + this.DiffManager._tryGetDocumentBeforeVersion = (project_id, doc_id, version, callback) => { + if (!retried) { + retried = true; + const error = new Error(); + error.retry = true; + return callback(error); + } else { + return callback(null, this.document, this.rewound_updates); + } + }; + sinon.spy(this.DiffManager, "_tryGetDocumentBeforeVersion"); + return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); + }); - it "should call _tryGetDocumentBeforeVersion twice", -> - @DiffManager._tryGetDocumentBeforeVersion + it("should call _tryGetDocumentBeforeVersion twice", function() { + return this.DiffManager._tryGetDocumentBeforeVersion .calledTwice - .should.equal true + .should.equal(true); + }); - it "should call the callback with the response", -> - @callback.calledWith(null, @document, @rewound_updates).should.equal true + return it("should call the callback with the response", function() { + return this.callback.calledWith(null, this.document, this.rewound_updates).should.equal(true); + }); + }); - describe "with a non-retriable error", -> - beforeEach -> - @error = new Error("oops") - @DiffManager._tryGetDocumentBeforeVersion.yields(@error) - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + describe("with a non-retriable error", function() { + beforeEach(function() { + this.error = new Error("oops"); + this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error); + return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); + }); - it "should call _tryGetDocumentBeforeVersion once", -> - @DiffManager._tryGetDocumentBeforeVersion + it("should call _tryGetDocumentBeforeVersion once", function() { + return this.DiffManager._tryGetDocumentBeforeVersion .calledOnce - .should.equal true + .should.equal(true); + }); - it "should call the callback with the error", -> - @callback.calledWith(@error).should.equal true + return it("should call the callback with the error", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); - describe "when retry limit is matched", -> - beforeEach -> - @error = new Error("oops") - @error.retry = true - @DiffManager._tryGetDocumentBeforeVersion.yields(@error) - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback + return describe("when retry limit is matched", function() { + beforeEach(function() { + this.error = new Error("oops"); + this.error.retry = true; + this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error); + return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); + }); - it "should call _tryGetDocumentBeforeVersion three times (max retries)", -> - @DiffManager._tryGetDocumentBeforeVersion + it("should call _tryGetDocumentBeforeVersion three times (max retries)", function() { + return this.DiffManager._tryGetDocumentBeforeVersion .calledThrice - .should.equal true + .should.equal(true); + }); - it "should call the callback with the error", -> - @callback.calledWith(@error).should.equal true + return it("should call the callback with the error", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); + }); - describe "_tryGetDocumentBeforeVersion", -> - beforeEach -> - @content = "hello world" - # Op versions are the version they were applied to, so doc is always one version - # ahead.s - @version = 43 - @updates = [ - { op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} } - { op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} } - { op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} } - { op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} } - ] - @fromVersion = 39 - @rewound_content = "rewound-content" - @diff = [ u: "mock-diff" ] + return describe("_tryGetDocumentBeforeVersion", function() { + beforeEach(function() { + this.content = "hello world"; + // Op versions are the version they were applied to, so doc is always one version + // ahead.s + this.version = 43; + this.updates = [ + { op: "mock-4", v: 42, meta: { start_ts: new Date(this.to.getTime() + 20)} }, + { op: "mock-3", v: 41, meta: { start_ts: new Date(this.to.getTime() + 10)} }, + { op: "mock-2", v: 40, meta: { start_ts: new Date(this.to.getTime() - 10)} }, + { op: "mock-1", v: 39, meta: { start_ts: new Date(this.to.getTime() - 20)} } + ]; + this.fromVersion = 39; + this.rewound_content = "rewound-content"; + return this.diff = [ {u: "mock-diff"} ];}); - describe "with matching versions", -> - beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) - @DiffGenerator.rewindUpdates = sinon.spy (content, updates) => - # the rewindUpdates method reverses the 'updates' array - updates.reverse() - return @rewound_content - @rewindUpdatesWithArgs = @DiffGenerator.rewindUpdates.withArgs(@content, @updates.slice().reverse()) - @DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback + describe("with matching versions", function() { + beforeEach(function() { + this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); + this.DiffGenerator.rewindUpdates = sinon.spy((content, updates) => { + // the rewindUpdates method reverses the 'updates' array + updates.reverse(); + return this.rewound_content; + }); + this.rewindUpdatesWithArgs = this.DiffGenerator.rewindUpdates.withArgs(this.content, this.updates.slice().reverse()); + return this.DiffManager._tryGetDocumentBeforeVersion(this.project_id, this.doc_id, this.fromVersion, this.callback); + }); - it "should get the latest doc and version with all recent updates", -> - @DiffManager.getLatestDocAndUpdates - .calledWith(@project_id, @doc_id, @fromVersion) - .should.equal true + it("should get the latest doc and version with all recent updates", function() { + return this.DiffManager.getLatestDocAndUpdates + .calledWith(this.project_id, this.doc_id, this.fromVersion) + .should.equal(true); + }); - it "should rewind the diff", -> - sinon.assert.calledOnce(@rewindUpdatesWithArgs) + it("should rewind the diff", function() { + return sinon.assert.calledOnce(this.rewindUpdatesWithArgs); + }); - it "should call the callback with the rewound document and updates", -> - @callback.calledWith(null, @rewound_content, @updates).should.equal true + return it("should call the callback with the rewound document and updates", function() { + return this.callback.calledWith(null, this.rewound_content, this.updates).should.equal(true); + }); + }); - describe "with mismatching versions", -> - beforeEach -> - @version = 50 - @updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ] - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) - @DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback + describe("with mismatching versions", function() { + beforeEach(function() { + this.version = 50; + this.updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ]; + this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); + return this.DiffManager._tryGetDocumentBeforeVersion(this.project_id, this.doc_id, this.fromVersion, this.callback); + }); - it "should call the callback with an error with retry = true set", -> - @callback.calledOnce.should.equal true - error = @callback.args[0][0] - expect(error.retry).to.equal true + return it("should call the callback with an error with retry = true set", function() { + this.callback.calledOnce.should.equal(true); + const error = this.callback.args[0][0]; + return expect(error.retry).to.equal(true); + }); + }); - describe "when the updates are inconsistent", -> - beforeEach -> - @DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates) - @DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!")) - @DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback + return describe("when the updates are inconsistent", function() { + beforeEach(function() { + this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); + this.DiffGenerator.rewindUpdates = sinon.stub().throws(this.error = new Error("inconsistent!")); + return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.fromVersion, this.callback); + }); - it "should call the callback with an error", -> - @callback - .calledWith(@error) - .should.equal true + return it("should call the callback with an error", function() { + return this.callback + .calledWith(this.error) + .should.equal(true); + }); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js index 3424418935..1da7f37e49 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js @@ -1,70 +1,90 @@ -chai = require('chai') -chai.should() -sinon = require("sinon") -modulePath = "../../../../app/js/MongoAWS.js" -SandboxedModule = require('sandboxed-module') -{ObjectId} = require("mongojs") -MemoryStream = require('memorystream') -zlib = require "zlib" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const chai = require('chai'); +chai.should(); +const sinon = require("sinon"); +const modulePath = "../../../../app/js/MongoAWS.js"; +const SandboxedModule = require('sandboxed-module'); +const {ObjectId} = require("mongojs"); +const MemoryStream = require('memorystream'); +const zlib = require("zlib"); -describe "MongoAWS", -> - beforeEach -> - @MongoAWS = SandboxedModule.require modulePath, requires: - "settings-sharelatex": @settings = - trackchanges: - s3: - secret: "s3-secret" +describe("MongoAWS", function() { + beforeEach(function() { + this.MongoAWS = SandboxedModule.require(modulePath, { requires: { + "settings-sharelatex": (this.settings = { + trackchanges: { + s3: { + secret: "s3-secret", key: "s3-key" - stores: + }, + stores: { doc_history: "s3-bucket" - "child_process": @child_process = {} - "mongo-uri": @mongouri = {} - "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->} - "aws-sdk": @awssdk = {} - "fs": @fs = {} - "s3-streams": @S3S = {} - "./mongojs" : { db: @db = {}, ObjectId: ObjectId } - "JSONStream": @JSONStream = {} - "readline-stream": @readline = sinon.stub() - 'metrics-sharelatex': {inc: ()->} + } + } + }), + "child_process": (this.child_process = {}), + "mongo-uri": (this.mongouri = {}), + "logger-sharelatex": (this.logger = {log: sinon.stub(), error: sinon.stub(), err() {}}), + "aws-sdk": (this.awssdk = {}), + "fs": (this.fs = {}), + "s3-streams": (this.S3S = {}), + "./mongojs" : { db: (this.db = {}), ObjectId }, + "JSONStream": (this.JSONStream = {}), + "readline-stream": (this.readline = sinon.stub()), + 'metrics-sharelatex': {inc(){}} + } + }); - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @pack_id = ObjectId() - @update = { v:123 } - @callback = sinon.stub() + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.pack_id = ObjectId(); + this.update = { v:123 }; + return this.callback = sinon.stub(); + }); - describe "archivePack", -> + describe("archivePack", function() { - beforeEach (done) -> - @awssdk.config = { update: sinon.stub() } - @awssdk.S3 = sinon.stub() - @S3S.WriteStream = () -> - MemoryStream.createWriteStream() - @db.docHistory = {} - @db.docHistory.findOne = sinon.stub().callsArgWith(1, null, {"pack":"hello"}) + beforeEach(function(done) { + this.awssdk.config = { update: sinon.stub() }; + this.awssdk.S3 = sinon.stub(); + this.S3S.WriteStream = () => MemoryStream.createWriteStream(); + this.db.docHistory = {}; + this.db.docHistory.findOne = sinon.stub().callsArgWith(1, null, {"pack":"hello"}); - @MongoAWS.archivePack @project_id, @doc_id, @pack_id, (err, result) => - @callback() - done() + return this.MongoAWS.archivePack(this.project_id, this.doc_id, this.pack_id, (err, result) => { + this.callback(); + return done(); + }); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "unArchivePack", -> + return describe("unArchivePack", function() { - beforeEach (done) -> - zlib.gzip '{"pack":"123"}', (err, zbuf) => - @awssdk.config = { update: sinon.stub() } - @awssdk.S3 = sinon.stub() - @S3S.ReadStream = () -> - MemoryStream.createReadStream(zbuf, {readable:true}) - @db.docHistory = {} - @db.docHistory.insert = sinon.stub().callsArgWith(1, null, "pack") + beforeEach(function(done) { + return zlib.gzip('{"pack":"123"}', (err, zbuf) => { + this.awssdk.config = { update: sinon.stub() }; + this.awssdk.S3 = sinon.stub(); + this.S3S.ReadStream = () => MemoryStream.createReadStream(zbuf, {readable:true}); + this.db.docHistory = {}; + this.db.docHistory.insert = sinon.stub().callsArgWith(1, null, "pack"); - @MongoAWS.unArchivePack @project_id, @doc_id, @pack_id, (err, result) => - @callback() - done() + return this.MongoAWS.unArchivePack(this.project_id, this.doc_id, this.pack_id, (err, result) => { + this.callback(); + return done(); + }); + }); + }); - it "should call db.docHistory.insert", -> - @db.docHistory.insert.called.should.equal true + return it("should call db.docHistory.insert", function() { + return this.db.docHistory.insert.called.should.equal(true); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index 3339a4242d..ca15a8867d 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -1,95 +1,129 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/DocumentUpdaterManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/DocumentUpdaterManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "DocumentUpdaterManager", -> - beforeEach -> - @DocumentUpdaterManager = SandboxedModule.require modulePath, requires: - "request": @request = {} - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } - 'settings-sharelatex': @settings = - apis : documentupdater: url : "http://example.com" - @callback = sinon.stub() - @lines = ["one", "two", "three"] - @version = 42 +describe("DocumentUpdaterManager", function() { + beforeEach(function() { + this.DocumentUpdaterManager = SandboxedModule.require(modulePath, { requires: { + "request": (this.request = {}), + "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), + 'settings-sharelatex': (this.settings = + {apis : {documentupdater: {url : "http://example.com"}}}) + } + } + ); + this.callback = sinon.stub(); + this.lines = ["one", "two", "three"]; + return this.version = 42; + }); - describe "getDocument", -> - describe "successfully", -> - beforeEach -> - @body = JSON.stringify - lines: @lines - version: @version - ops: [] - @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) - @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + describe("getDocument", function() { + describe("successfully", function() { + beforeEach(function() { + this.body = JSON.stringify({ + lines: this.lines, + version: this.version, + ops: []}); + this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body); + return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.callback); + }); - it 'should get the document from the document updater', -> - url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}" - @request.get.calledWith(url).should.equal true + it('should get the document from the document updater', function() { + const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`; + return this.request.get.calledWith(url).should.equal(true); + }); - it "should call the callback with the content and version", -> - @callback.calledWith(null, @lines.join("\n"), @version).should.equal true + return it("should call the callback with the content and version", function() { + return this.callback.calledWith(null, this.lines.join("\n"), this.version).should.equal(true); + }); + }); - describe "when the document updater API returns an error", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) - @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + describe("when the document updater API returns an error", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); + return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.callback); + }); - it "should return an error to the callback", -> - @callback.calledWith(@error).should.equal true + return it("should return an error to the callback", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); - describe "when the document updater returns a failure error code", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") - @DocumentUpdaterManager.getDocument @project_id, @doc_id, @callback + return describe("when the document updater returns a failure error code", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, ""); + return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.callback); + }); - it "should return the callback with an error", -> - @callback + return it("should return the callback with an error", function() { + return this.callback .calledWith(sinon.match.has('message', "doc updater returned a non-success status code: 500")) - .should.equal true + .should.equal(true); + }); + }); + }); - describe "setDocument", -> - beforeEach -> - @content = "mock content" - @user_id = "user-id-123" + return describe("setDocument", function() { + beforeEach(function() { + this.content = "mock content"; + return this.user_id = "user-id-123"; + }); - describe "successfully", -> - beforeEach -> - @request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}) - @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @user_id, @callback + describe("successfully", function() { + beforeEach(function() { + this.request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}); + return this.DocumentUpdaterManager.setDocument(this.project_id, this.doc_id, this.content, this.user_id, this.callback); + }); - it 'should set the document in the document updater', -> - url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}/doc/#{@doc_id}" - @request.post + it('should set the document in the document updater', function() { + const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`; + return this.request.post .calledWith({ - url: url - json: - lines: @content.split("\n") - source: "restore" - user_id: @user_id + url, + json: { + lines: this.content.split("\n"), + source: "restore", + user_id: this.user_id, undoing: true - }).should.equal true + } + }).should.equal(true); + }); - it "should call the callback", -> - @callback.calledWith(null).should.equal true + return it("should call the callback", function() { + return this.callback.calledWith(null).should.equal(true); + }); + }); - describe "when the document updater API returns an error", -> - beforeEach -> - @request.post = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) - @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @user_id, @callback + describe("when the document updater API returns an error", function() { + beforeEach(function() { + this.request.post = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); + return this.DocumentUpdaterManager.setDocument(this.project_id, this.doc_id, this.content, this.user_id, this.callback); + }); - it "should return an error to the callback", -> - @callback.calledWith(@error).should.equal true + return it("should return an error to the callback", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); - describe "when the document updater returns a failure error code", -> - beforeEach -> - @request.post = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, "") - @DocumentUpdaterManager.setDocument @project_id, @doc_id, @content, @user_id, @callback + return describe("when the document updater returns a failure error code", function() { + beforeEach(function() { + this.request.post = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, ""); + return this.DocumentUpdaterManager.setDocument(this.project_id, this.doc_id, this.content, this.user_id, this.callback); + }); - it "should return the callback with an error", -> - @callback + return it("should return the callback with an error", function() { + return this.callback .calledWith(sinon.match.has('message', "doc updater returned a non-success status code: 500")) - .should.equal true + .should.equal(true); + }); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js index 8eded7ed7d..6acb0ae3eb 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js @@ -1,135 +1,177 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/HttpController.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/HttpController.js"; +const SandboxedModule = require('sandboxed-module'); -describe "HttpController", -> - beforeEach -> - @HttpController = SandboxedModule.require modulePath, requires: - "logger-sharelatex": { log: sinon.stub() } - "./UpdatesManager": @UpdatesManager = {} - "./DiffManager": @DiffManager = {} - "./RestoreManager": @RestoreManager = {} - "./PackManager": @PackManager = {} - "./DocArchiveManager": @DocArchiveManager = {} - "./HealthChecker": @HealthChecker = {} - @doc_id = "doc-id-123" - @project_id = "project-id-123" - @next = sinon.stub() - @user_id = "mock-user-123" - @now = Date.now() +describe("HttpController", function() { + beforeEach(function() { + this.HttpController = SandboxedModule.require(modulePath, { requires: { + "logger-sharelatex": { log: sinon.stub() }, + "./UpdatesManager": (this.UpdatesManager = {}), + "./DiffManager": (this.DiffManager = {}), + "./RestoreManager": (this.RestoreManager = {}), + "./PackManager": (this.PackManager = {}), + "./DocArchiveManager": (this.DocArchiveManager = {}), + "./HealthChecker": (this.HealthChecker = {}) + } + }); + this.doc_id = "doc-id-123"; + this.project_id = "project-id-123"; + this.next = sinon.stub(); + this.user_id = "mock-user-123"; + return this.now = Date.now(); + }); - describe "flushDoc", -> - beforeEach -> - @req = - params: - doc_id: @doc_id - project_id: @project_id - @res = - send: sinon.stub() - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - @HttpController.flushDoc @req, @res, @next + describe("flushDoc", function() { + beforeEach(function() { + this.req = { + params: { + doc_id: this.doc_id, + project_id: this.project_id + } + }; + this.res = + {send: sinon.stub()}; + this.UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2); + return this.HttpController.flushDoc(this.req, this.res, this.next); + }); - it "should process the updates", -> - @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@project_id, @doc_id) - .should.equal true + it("should process the updates", function() { + return this.UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(this.project_id, this.doc_id) + .should.equal(true); + }); - it "should return a success code", -> - @res.send.calledWith(204).should.equal true + return it("should return a success code", function() { + return this.res.send.calledWith(204).should.equal(true); + }); + }); - describe "flushProject", -> - beforeEach -> - @req = - params: - project_id: @project_id - @res = - send: sinon.stub() - @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) - @HttpController.flushProject @req, @res, @next + describe("flushProject", function() { + beforeEach(function() { + this.req = { + params: { + project_id: this.project_id + } + }; + this.res = + {send: sinon.stub()}; + this.UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1); + return this.HttpController.flushProject(this.req, this.res, this.next); + }); - it "should process the updates", -> - @UpdatesManager.processUncompressedUpdatesForProject - .calledWith(@project_id) - .should.equal true + it("should process the updates", function() { + return this.UpdatesManager.processUncompressedUpdatesForProject + .calledWith(this.project_id) + .should.equal(true); + }); - it "should return a success code", -> - @res.send.calledWith(204).should.equal true + return it("should return a success code", function() { + return this.res.send.calledWith(204).should.equal(true); + }); + }); - describe "getDiff", -> - beforeEach -> - @from = 42 - @to = 45 - @req = - params: - doc_id: @doc_id - project_id: @project_id - query: - from: @from.toString() - to: @to.toString() - @res = - json: sinon.stub() - @diff = [ u: "mock-diff" ] - @DiffManager.getDiff = sinon.stub().callsArgWith(4, null, @diff) - @HttpController.getDiff @req, @res, @next + describe("getDiff", function() { + beforeEach(function() { + this.from = 42; + this.to = 45; + this.req = { + params: { + doc_id: this.doc_id, + project_id: this.project_id + }, + query: { + from: this.from.toString(), + to: this.to.toString() + } + }; + this.res = + {json: sinon.stub()}; + this.diff = [ {u: "mock-diff"} ]; + this.DiffManager.getDiff = sinon.stub().callsArgWith(4, null, this.diff); + return this.HttpController.getDiff(this.req, this.res, this.next); + }); - it "should get the diff", -> - @DiffManager.getDiff - .calledWith(@project_id, @doc_id, parseInt(@from, 10), parseInt(@to, 10)) - .should.equal true + it("should get the diff", function() { + return this.DiffManager.getDiff + .calledWith(this.project_id, this.doc_id, parseInt(this.from, 10), parseInt(this.to, 10)) + .should.equal(true); + }); - it "should return the diff", -> - @res.json.calledWith({diff: @diff}).should.equal true + return it("should return the diff", function() { + return this.res.json.calledWith({diff: this.diff}).should.equal(true); + }); + }); - describe "getUpdates", -> - beforeEach -> - @before = Date.now() - @nextBeforeTimestamp = @before - 100 - @min_count = 10 - @req = - params: - project_id: @project_id - query: - before: @before.toString() - min_count: @min_count.toString() - @res = - json: sinon.stub() - @updates = ["mock-summarized-updates"] - @UpdatesManager.getSummarizedProjectUpdates = sinon.stub().callsArgWith(2, null, @updates, @nextBeforeTimestamp) - @HttpController.getUpdates @req, @res, @next + describe("getUpdates", function() { + beforeEach(function() { + this.before = Date.now(); + this.nextBeforeTimestamp = this.before - 100; + this.min_count = 10; + this.req = { + params: { + project_id: this.project_id + }, + query: { + before: this.before.toString(), + min_count: this.min_count.toString() + } + }; + this.res = + {json: sinon.stub()}; + this.updates = ["mock-summarized-updates"]; + this.UpdatesManager.getSummarizedProjectUpdates = sinon.stub().callsArgWith(2, null, this.updates, this.nextBeforeTimestamp); + return this.HttpController.getUpdates(this.req, this.res, this.next); + }); - it "should get the updates", -> - @UpdatesManager.getSummarizedProjectUpdates - .calledWith(@project_id, before: @before, min_count: @min_count) - .should.equal true + it("should get the updates", function() { + return this.UpdatesManager.getSummarizedProjectUpdates + .calledWith(this.project_id, {before: this.before, min_count: this.min_count}) + .should.equal(true); + }); - it "should return the formatted updates", -> - @res.json.calledWith({updates: @updates, nextBeforeTimestamp: @nextBeforeTimestamp}).should.equal true + return it("should return the formatted updates", function() { + return this.res.json.calledWith({updates: this.updates, nextBeforeTimestamp: this.nextBeforeTimestamp}).should.equal(true); + }); + }); - describe "RestoreManager", -> - beforeEach -> - @version = "42" - @req = - params: - doc_id: @doc_id - project_id: @project_id - version: @version - headers: - "x-user-id": @user_id - @res = - send: sinon.stub() + return describe("RestoreManager", function() { + beforeEach(function() { + this.version = "42"; + this.req = { + params: { + doc_id: this.doc_id, + project_id: this.project_id, + version: this.version + }, + headers: { + "x-user-id": this.user_id + } + }; + this.res = + {send: sinon.stub()}; - @RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(4) - @HttpController.restore @req, @res, @next + this.RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(4); + return this.HttpController.restore(this.req, this.res, this.next); + }); - it "should restore the document", -> - @RestoreManager.restoreToBeforeVersion - .calledWith(@project_id, @doc_id, parseInt(@version, 10), @user_id) - .should.equal true + it("should restore the document", function() { + return this.RestoreManager.restoreToBeforeVersion + .calledWith(this.project_id, this.doc_id, parseInt(this.version, 10), this.user_id) + .should.equal(true); + }); - it "should return a success code", -> - @res.send.calledWith(204).should.equal true + return it("should return a success code", function() { + return this.res.send.calledWith(204).should.equal(true); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js index ed30cb2815..7cd7461810 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js @@ -1,198 +1,275 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/LockManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/LockManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "LockManager", -> - beforeEach -> - @Settings = - redis: +describe("LockManager", function() { + beforeEach(function() { + this.Settings = { + redis: { lock:{} - @LockManager = SandboxedModule.require modulePath, requires: - "redis-sharelatex": - createClient: () => @rclient = - auth: sinon.stub() - "settings-sharelatex": @Settings - "logger-sharelatex": {error: ->} + } + }; + this.LockManager = SandboxedModule.require(modulePath, { requires: { + "redis-sharelatex": { + createClient: () => { return this.rclient = + {auth: sinon.stub()}; } + }, + "settings-sharelatex": this.Settings, + "logger-sharelatex": {error() {}} + } + }); - @key = "lock-key" - @callback = sinon.stub() + this.key = "lock-key"; + return this.callback = sinon.stub(); + }); - describe "checkLock", -> - describe "when the lock is taken", -> - beforeEach -> - @rclient.exists = sinon.stub().callsArgWith(1, null, "1") - @LockManager.checkLock @key, @callback + describe("checkLock", function() { + describe("when the lock is taken", function() { + beforeEach(function() { + this.rclient.exists = sinon.stub().callsArgWith(1, null, "1"); + return this.LockManager.checkLock(this.key, this.callback); + }); - it "should check the lock in redis", -> - @rclient.exists - .calledWith(@key) - .should.equal true + it("should check the lock in redis", function() { + return this.rclient.exists + .calledWith(this.key) + .should.equal(true); + }); - it "should return the callback with false", -> - @callback.calledWith(null, false).should.equal true + return it("should return the callback with false", function() { + return this.callback.calledWith(null, false).should.equal(true); + }); + }); - describe "when the lock is free", -> - beforeEach -> - @rclient.exists = sinon.stub().callsArgWith(1, null, "0") - @LockManager.checkLock @key, @callback + return describe("when the lock is free", function() { + beforeEach(function() { + this.rclient.exists = sinon.stub().callsArgWith(1, null, "0"); + return this.LockManager.checkLock(this.key, this.callback); + }); - it "should return the callback with true", -> - @callback.calledWith(null, true).should.equal true + return it("should return the callback with true", function() { + return this.callback.calledWith(null, true).should.equal(true); + }); + }); + }); - describe "tryLock", -> - describe "when the lock is taken", -> - beforeEach -> - @rclient.set = sinon.stub().callsArgWith(5, null, null) - @LockManager.randomLock = sinon.stub().returns "locked-random-value" - @LockManager.tryLock @key, @callback + describe("tryLock", function() { + describe("when the lock is taken", function() { + beforeEach(function() { + this.rclient.set = sinon.stub().callsArgWith(5, null, null); + this.LockManager.randomLock = sinon.stub().returns("locked-random-value"); + return this.LockManager.tryLock(this.key, this.callback); + }); - it "should check the lock in redis", -> - @rclient.set - .calledWith(@key, "locked-random-value", "EX", @LockManager.LOCK_TTL, "NX") - .should.equal true + it("should check the lock in redis", function() { + return this.rclient.set + .calledWith(this.key, "locked-random-value", "EX", this.LockManager.LOCK_TTL, "NX") + .should.equal(true); + }); - it "should return the callback with false", -> - @callback.calledWith(null, false).should.equal true + return it("should return the callback with false", function() { + return this.callback.calledWith(null, false).should.equal(true); + }); + }); - describe "when the lock is free", -> - beforeEach -> - @rclient.set = sinon.stub().callsArgWith(5, null, "OK") - @LockManager.tryLock @key, @callback + return describe("when the lock is free", function() { + beforeEach(function() { + this.rclient.set = sinon.stub().callsArgWith(5, null, "OK"); + return this.LockManager.tryLock(this.key, this.callback); + }); - it "should return the callback with true", -> - @callback.calledWith(null, true).should.equal true + return it("should return the callback with true", function() { + return this.callback.calledWith(null, true).should.equal(true); + }); + }); + }); - describe "deleteLock", -> - beforeEach -> - beforeEach -> - @rclient.del = sinon.stub().callsArg(1) - @LockManager.deleteLock @key, @callback + describe("deleteLock", () => + beforeEach(function() { + beforeEach(function() { + this.rclient.del = sinon.stub().callsArg(1); + return this.LockManager.deleteLock(this.key, this.callback); + }); - it "should delete the lock in redis", -> - @rclient.del + it("should delete the lock in redis", function() { + return this.rclient.del .calledWith(key) - .should.equal true + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }) + ); - describe "getLock", -> - describe "when the lock is not taken", -> - beforeEach (done) -> - @LockManager.tryLock = sinon.stub().callsArgWith(1, null, true) - @LockManager.getLock @key, (args...) => - @callback(args...) - done() + describe("getLock", function() { + describe("when the lock is not taken", function() { + beforeEach(function(done) { + this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, true); + return this.LockManager.getLock(this.key, (...args) => { + this.callback(...Array.from(args || [])); + return done(); + }); + }); - it "should try to get the lock", -> - @LockManager.tryLock - .calledWith(@key) - .should.equal true + it("should try to get the lock", function() { + return this.LockManager.tryLock + .calledWith(this.key) + .should.equal(true); + }); - it "should only need to try once", -> - @LockManager.tryLock.callCount.should.equal 1 + it("should only need to try once", function() { + return this.LockManager.tryLock.callCount.should.equal(1); + }); - it "should return the callback", -> - @callback.calledWith(null).should.equal true + return it("should return the callback", function() { + return this.callback.calledWith(null).should.equal(true); + }); + }); - describe "when the lock is initially set", -> - beforeEach (done) -> - startTime = Date.now() - @LockManager.LOCK_TEST_INTERVAL = 5 - @LockManager.tryLock = (doc_id, callback = (error, isFree) ->) -> - if Date.now() - startTime < 100 - callback null, false - else - callback null, true - sinon.spy @LockManager, "tryLock" + describe("when the lock is initially set", function() { + beforeEach(function(done) { + const startTime = Date.now(); + this.LockManager.LOCK_TEST_INTERVAL = 5; + this.LockManager.tryLock = function(doc_id, callback) { + if (callback == null) { callback = function(error, isFree) {}; } + if ((Date.now() - startTime) < 100) { + return callback(null, false); + } else { + return callback(null, true); + } + }; + sinon.spy(this.LockManager, "tryLock"); - @LockManager.getLock @key, (args...) => - @callback(args...) - done() + return this.LockManager.getLock(this.key, (...args) => { + this.callback(...Array.from(args || [])); + return done(); + }); + }); - it "should call tryLock multiple times until free", -> - (@LockManager.tryLock.callCount > 1).should.equal true + it("should call tryLock multiple times until free", function() { + return (this.LockManager.tryLock.callCount > 1).should.equal(true); + }); - it "should return the callback", -> - @callback.calledWith(null).should.equal true + return it("should return the callback", function() { + return this.callback.calledWith(null).should.equal(true); + }); + }); - describe "when the lock times out", -> - beforeEach (done) -> - time = Date.now() - @LockManager.MAX_LOCK_WAIT_TIME = 5 - @LockManager.tryLock = sinon.stub().callsArgWith(1, null, false) - @LockManager.getLock @key, (args...) => - @callback(args...) - done() + return describe("when the lock times out", function() { + beforeEach(function(done) { + const time = Date.now(); + this.LockManager.MAX_LOCK_WAIT_TIME = 5; + this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, false); + return this.LockManager.getLock(this.key, (...args) => { + this.callback(...Array.from(args || [])); + return done(); + }); + }); - it "should return the callback with an error", -> - @callback.calledWith(sinon.match.instanceOf(Error)).should.equal true + return it("should return the callback with an error", function() { + return this.callback.calledWith(sinon.match.instanceOf(Error)).should.equal(true); + }); + }); + }); - describe "runWithLock", -> - describe "with successful run", -> - beforeEach -> - @runner = (releaseLock = (error) ->) -> - releaseLock() - sinon.spy @, "runner" - @LockManager.getLock = sinon.stub().callsArg(1) - @LockManager.releaseLock = sinon.stub().callsArg(2) - @LockManager.runWithLock @key, @runner, @callback + return describe("runWithLock", function() { + describe("with successful run", function() { + beforeEach(function() { + this.runner = function(releaseLock) { + if (releaseLock == null) { releaseLock = function(error) {}; } + return releaseLock(); + }; + sinon.spy(this, "runner"); + this.LockManager.getLock = sinon.stub().callsArg(1); + this.LockManager.releaseLock = sinon.stub().callsArg(2); + return this.LockManager.runWithLock(this.key, this.runner, this.callback); + }); - it "should get the lock", -> - @LockManager.getLock - .calledWith(@key) - .should.equal true + it("should get the lock", function() { + return this.LockManager.getLock + .calledWith(this.key) + .should.equal(true); + }); - it "should run the passed function", -> - @runner.called.should.equal true + it("should run the passed function", function() { + return this.runner.called.should.equal(true); + }); - it "should release the lock", -> - @LockManager.releaseLock - .calledWith(@key) - .should.equal true + it("should release the lock", function() { + return this.LockManager.releaseLock + .calledWith(this.key) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when the runner function returns an error", -> - beforeEach -> - @error = new Error("oops") - @runner = (releaseLock = (error) ->) => - releaseLock(@error) - sinon.spy @, "runner" - @LockManager.getLock = sinon.stub().callsArg(1) - @LockManager.releaseLock = sinon.stub().callsArg(2) - @LockManager.runWithLock @key, @runner, @callback + describe("when the runner function returns an error", function() { + beforeEach(function() { + this.error = new Error("oops"); + this.runner = releaseLock => { + if (releaseLock == null) { releaseLock = function(error) {}; } + return releaseLock(this.error); + }; + sinon.spy(this, "runner"); + this.LockManager.getLock = sinon.stub().callsArg(1); + this.LockManager.releaseLock = sinon.stub().callsArg(2); + return this.LockManager.runWithLock(this.key, this.runner, this.callback); + }); - it "should release the lock", -> - @LockManager.releaseLock - .calledWith(@key) - .should.equal true + it("should release the lock", function() { + return this.LockManager.releaseLock + .calledWith(this.key) + .should.equal(true); + }); - it "should call the callback with the error", -> - @callback.calledWith(@error).should.equal true + return it("should call the callback with the error", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); - describe "releaseLock", -> - describe "when the lock is current", -> - beforeEach -> - @rclient.eval = sinon.stub().yields(null, 1) - @LockManager.releaseLock @key, @lockValue, @callback + return describe("releaseLock", function() { + describe("when the lock is current", function() { + beforeEach(function() { + this.rclient.eval = sinon.stub().yields(null, 1); + return this.LockManager.releaseLock(this.key, this.lockValue, this.callback); + }); - it 'should clear the data from redis', -> - @rclient.eval.calledWith(@LockManager.unlockScript, 1, @key, @lockValue).should.equal true + it('should clear the data from redis', function() { + return this.rclient.eval.calledWith(this.LockManager.unlockScript, 1, this.key, this.lockValue).should.equal(true); + }); - it 'should call the callback', -> - @callback.called.should.equal true + return it('should call the callback', function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when the lock has expired", -> - beforeEach -> - @rclient.eval = sinon.stub().yields(null, 0) - @LockManager.releaseLock @key, @lockValue, @callback + return describe("when the lock has expired", function() { + beforeEach(function() { + this.rclient.eval = sinon.stub().yields(null, 0); + return this.LockManager.releaseLock(this.key, this.lockValue, this.callback); + }); - it 'should return an error if the lock has expired', -> - @callback.calledWith(sinon.match.has('message', "tried to release timed out lock")).should.equal true + return it('should return an error if the lock has expired', function() { + return this.callback.calledWith(sinon.match.has('message', "tried to release timed out lock")).should.equal(true); + }); + }); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js index 85f4357a58..9f787a56a4 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js @@ -1,158 +1,197 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/MongoManager.js" -packModulePath = "../../../../app/js/PackManager.js" -SandboxedModule = require('sandboxed-module') -{ObjectId} = require("mongojs") -tk = require "timekeeper" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/MongoManager.js"; +const packModulePath = "../../../../app/js/PackManager.js"; +const SandboxedModule = require('sandboxed-module'); +const {ObjectId} = require("mongojs"); +const tk = require("timekeeper"); -describe "MongoManager", -> - beforeEach -> - tk.freeze(new Date()) - @MongoManager = SandboxedModule.require modulePath, requires: - "./mongojs" : { db: @db = {}, ObjectId: ObjectId } - "./PackManager" : @PackManager = {} - 'metrics-sharelatex': {timeAsyncMethod: ()->} - 'logger-sharelatex': {log: ()->} - @callback = sinon.stub() - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() +describe("MongoManager", function() { + beforeEach(function() { + tk.freeze(new Date()); + this.MongoManager = SandboxedModule.require(modulePath, { requires: { + "./mongojs" : { db: (this.db = {}), ObjectId }, + "./PackManager" : (this.PackManager = {}), + 'metrics-sharelatex': {timeAsyncMethod(){}}, + 'logger-sharelatex': {log(){}} + } + }); + this.callback = sinon.stub(); + this.doc_id = ObjectId().toString(); + return this.project_id = ObjectId().toString(); + }); - afterEach -> - tk.reset() + afterEach(() => tk.reset()); - describe "getLastCompressedUpdate", -> - beforeEach -> - @update = "mock-update" - @db.docHistory = {} - @db.docHistory.find = sinon.stub().returns @db.docHistory - @db.docHistory.findOne = sinon.stub().returns @db.docHistory - @db.docHistory.sort = sinon.stub().returns @db.docHistory - @db.docHistory.limit = sinon.stub().returns @db.docHistory - @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, [@update]) + describe("getLastCompressedUpdate", function() { + beforeEach(function() { + this.update = "mock-update"; + this.db.docHistory = {}; + this.db.docHistory.find = sinon.stub().returns(this.db.docHistory); + this.db.docHistory.findOne = sinon.stub().returns(this.db.docHistory); + this.db.docHistory.sort = sinon.stub().returns(this.db.docHistory); + this.db.docHistory.limit = sinon.stub().returns(this.db.docHistory); + this.db.docHistory.toArray = sinon.stub().callsArgWith(0, null, [this.update]); - @MongoManager.getLastCompressedUpdate @doc_id, @callback + return this.MongoManager.getLastCompressedUpdate(this.doc_id, this.callback); + }); - it "should find the updates for the doc", -> - @db.docHistory.find - .calledWith(doc_id: ObjectId(@doc_id)) - .should.equal true + it("should find the updates for the doc", function() { + return this.db.docHistory.find + .calledWith({doc_id: ObjectId(this.doc_id)}) + .should.equal(true); + }); - it "should limit to one result", -> - @db.docHistory.limit + it("should limit to one result", function() { + return this.db.docHistory.limit .calledWith(1) - .should.equal true + .should.equal(true); + }); - it "should sort in descending version order", -> - @db.docHistory.sort - .calledWith(v: -1) - .should.equal true + it("should sort in descending version order", function() { + return this.db.docHistory.sort + .calledWith({v: -1}) + .should.equal(true); + }); - it "should call the call back with the update", -> - @callback.calledWith(null, @update).should.equal true + return it("should call the call back with the update", function() { + return this.callback.calledWith(null, this.update).should.equal(true); + }); + }); - describe "peekLastCompressedUpdate", -> - describe "when there is no last update", -> - beforeEach -> - @PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, null) - @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @MongoManager.peekLastCompressedUpdate @doc_id, @callback + describe("peekLastCompressedUpdate", function() { + describe("when there is no last update", function() { + beforeEach(function() { + this.PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, null); + this.MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null); + return this.MongoManager.peekLastCompressedUpdate(this.doc_id, this.callback); + }); - it "should get the last update", -> - @MongoManager.getLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should get the last update", function() { + return this.MongoManager.getLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should call the callback with no update", -> - @callback.calledWith(null, null).should.equal true + return it("should call the callback with no update", function() { + return this.callback.calledWith(null, null).should.equal(true); + }); + }); - describe "when there is an update", -> - beforeEach -> - @update = { _id: Object() } - @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update) - @MongoManager.peekLastCompressedUpdate @doc_id, @callback + describe("when there is an update", function() { + beforeEach(function() { + this.update = { _id: Object() }; + this.MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, this.update); + return this.MongoManager.peekLastCompressedUpdate(this.doc_id, this.callback); + }); - it "should get the last update", -> - @MongoManager.getLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should get the last update", function() { + return this.MongoManager.getLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should call the callback with the update", -> - @callback.calledWith(null, @update).should.equal true + return it("should call the callback with the update", function() { + return this.callback.calledWith(null, this.update).should.equal(true); + }); + }); - describe "when there is a last update in S3", -> - beforeEach -> - @update = { _id: Object(), v: 12345, v_end: 12345, inS3:true} - @PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, @update) - @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null) - @MongoManager.peekLastCompressedUpdate @doc_id, @callback + return describe("when there is a last update in S3", function() { + beforeEach(function() { + this.update = { _id: Object(), v: 12345, v_end: 12345, inS3:true}; + this.PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, this.update); + this.MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null); + return this.MongoManager.peekLastCompressedUpdate(this.doc_id, this.callback); + }); - it "should get the last update", -> - @MongoManager.getLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should get the last update", function() { + return this.MongoManager.getLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should call the callback with a null update and the correct version", -> - @callback.calledWith(null, null, @update.v_end).should.equal true + return it("should call the callback with a null update and the correct version", function() { + return this.callback.calledWith(null, null, this.update.v_end).should.equal(true); + }); + }); + }); - describe "backportProjectId", -> - beforeEach -> - @db.docHistory = - update: sinon.stub().callsArg(3) - @MongoManager.backportProjectId @project_id, @doc_id, @callback + describe("backportProjectId", function() { + beforeEach(function() { + this.db.docHistory = + {update: sinon.stub().callsArg(3)}; + return this.MongoManager.backportProjectId(this.project_id, this.doc_id, this.callback); + }); - it "should insert the project_id into all entries for the doc_id which don't have it set", -> - @db.docHistory.update + it("should insert the project_id into all entries for the doc_id which don't have it set", function() { + return this.db.docHistory.update .calledWith({ - doc_id: ObjectId(@doc_id) + doc_id: ObjectId(this.doc_id), project_id: { $exists: false } }, { - $set: { project_id: ObjectId(@project_id) } + $set: { project_id: ObjectId(this.project_id) } }, { multi: true }) - .should.equal true + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "getProjectMetaData", -> - beforeEach -> - @metadata = { "mock": "metadata" } - @db.projectHistoryMetaData = - find: sinon.stub().callsArgWith(1, null, [@metadata]) - @MongoManager.getProjectMetaData @project_id, @callback + describe("getProjectMetaData", function() { + beforeEach(function() { + this.metadata = { "mock": "metadata" }; + this.db.projectHistoryMetaData = + {find: sinon.stub().callsArgWith(1, null, [this.metadata])}; + return this.MongoManager.getProjectMetaData(this.project_id, this.callback); + }); - it "should look up the meta data in the db", -> - @db.projectHistoryMetaData.find - .calledWith({ project_id: ObjectId(@project_id) }) - .should.equal true + it("should look up the meta data in the db", function() { + return this.db.projectHistoryMetaData.find + .calledWith({ project_id: ObjectId(this.project_id) }) + .should.equal(true); + }); - it "should return the metadata", -> - @callback.calledWith(null, @metadata).should.equal true + return it("should return the metadata", function() { + return this.callback.calledWith(null, this.metadata).should.equal(true); + }); + }); - describe "setProjectMetaData", -> - beforeEach -> - @metadata = { "mock": "metadata" } - @db.projectHistoryMetaData = - update: sinon.stub().callsArgWith(3, null, [@metadata]) - @MongoManager.setProjectMetaData @project_id, @metadata, @callback + return describe("setProjectMetaData", function() { + beforeEach(function() { + this.metadata = { "mock": "metadata" }; + this.db.projectHistoryMetaData = + {update: sinon.stub().callsArgWith(3, null, [this.metadata])}; + return this.MongoManager.setProjectMetaData(this.project_id, this.metadata, this.callback); + }); - it "should upsert the metadata into the DB", -> - @db.projectHistoryMetaData.update + it("should upsert the metadata into the DB", function() { + return this.db.projectHistoryMetaData.update .calledWith({ - project_id: ObjectId(@project_id) + project_id: ObjectId(this.project_id) }, { - $set: @metadata + $set: this.metadata }, { upsert: true }) - .should.equal true + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js index f6243619d2..58bbc0312e 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js @@ -1,369 +1,464 @@ -sinon = require('sinon') -chai = require('chai') -assert = require('chai').assert -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/PackManager.js" -SandboxedModule = require('sandboxed-module') -{ObjectId} = require("mongojs") -bson = require("bson") -BSON = new bson.BSONPure() -_ = require("underscore") +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const { assert } = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/PackManager.js"; +const SandboxedModule = require('sandboxed-module'); +const {ObjectId} = require("mongojs"); +const bson = require("bson"); +const BSON = new bson.BSONPure(); +const _ = require("underscore"); -tk = require "timekeeper" +const tk = require("timekeeper"); -describe "PackManager", -> - beforeEach -> - tk.freeze(new Date()) - @PackManager = SandboxedModule.require modulePath, requires: - "./mongojs" : { db: @db = {}, ObjectId: ObjectId, BSON: BSON } - "./LockManager" : {} - "./MongoAWS": {} - "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } - 'metrics-sharelatex': {inc: ()->} - "./ProjectIterator": require("../../../../app/js/ProjectIterator.js") # Cache for speed - "settings-sharelatex": - redis: lock: key_schema: {} - @callback = sinon.stub() - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() - @PackManager.MAX_COUNT = 512 +describe("PackManager", function() { + beforeEach(function() { + tk.freeze(new Date()); + this.PackManager = SandboxedModule.require(modulePath, { requires: { + "./mongojs" : { db: (this.db = {}), ObjectId, BSON }, + "./LockManager" : {}, + "./MongoAWS": {}, + "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }, + 'metrics-sharelatex': {inc(){}}, + "./ProjectIterator": require("../../../../app/js/ProjectIterator.js"), // Cache for speed + "settings-sharelatex": { + redis: {lock: {key_schema: {}}} + } + } + }); + this.callback = sinon.stub(); + this.doc_id = ObjectId().toString(); + this.project_id = ObjectId().toString(); + return this.PackManager.MAX_COUNT = 512; + }); - afterEach -> - tk.reset() + afterEach(() => tk.reset()); - describe "insertCompressedUpdates", -> - beforeEach -> - @lastUpdate = { - _id: "12345" + describe("insertCompressedUpdates", function() { + beforeEach(function() { + this.lastUpdate = { + _id: "12345", pack: [ { op: "op-1", meta: "meta-1", v: 1}, { op: "op-2", meta: "meta-2", v: 2} - ] - n : 2 + ], + n : 2, sz : 100 - } - @newUpdates = [ + }; + this.newUpdates = [ { op: "op-3", meta: "meta-3", v: 3}, { op: "op-4", meta: "meta-4", v: 4} - ] - @db.docHistory = - save: sinon.stub().callsArg(1) - insert: sinon.stub().callsArg(1) + ]; + return this.db.docHistory = { + save: sinon.stub().callsArg(1), + insert: sinon.stub().callsArg(1), findAndModify: sinon.stub().callsArg(1) + }; + }); - describe "with no last update", -> - beforeEach -> - @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) - @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback + describe("with no last update", function() { + beforeEach(function() { + this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4); + return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, true, this.callback); + }); - describe "for a small update", -> - it "should insert the update into a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates, true).should.equal true + describe("for a small update", function() { + it("should insert the update into a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates, true).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "for many small updates", -> - beforeEach -> - @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048]) - @PackManager.insertCompressedUpdates @project_id, @doc_id, null, @newUpdates, false, @callback + return describe("for many small updates", function() { + beforeEach(function() { + this.newUpdates = (__range__(0, 2048, true).map((i) => ({ op: `op-${i}`, meta: `meta-${i}`, v: i}))); + return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, false, this.callback); + }); - it "should append the initial updates to the existing pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[0...512], false).should.equal true + it("should append the initial updates to the existing pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(0, 512), false).should.equal(true); + }); - it "should insert the first set remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[512...1024], false).should.equal true + it("should insert the first set remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(512, 1024), false).should.equal(true); + }); - it "should insert the second set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1024...1536], false).should.equal true + it("should insert the second set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1024, 1536), false).should.equal(true); + }); - it "should insert the third set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1536...2048], false).should.equal true + it("should insert the third set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1536, 2048), false).should.equal(true); + }); - it "should insert the final set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2048..2048], false).should.equal true + it("should insert the final set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(2048, 2049), false).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); - describe "with an existing pack as the last update", -> - beforeEach -> - @PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5) - @PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) - @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + describe("with an existing pack as the last update", function() { + beforeEach(function() { + this.PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5); + this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4); + return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); + }); - describe "for a small update", -> - it "should append the update to the existing pack", -> - @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates, false).should.equal true - it "should not insert any new packs", -> - @PackManager.insertUpdatesIntoNewPack.called.should.equal false - it "should call the callback", -> - @callback.called.should.equal true + describe("for a small update", function() { + it("should append the update to the existing pack", function() { + return this.PackManager.appendUpdatesToExistingPack.calledWith(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false).should.equal(true); + }); + it("should not insert any new packs", function() { + return this.PackManager.insertUpdatesIntoNewPack.called.should.equal(false); + }); + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "for many small updates", -> - beforeEach -> - @newUpdates = ({ op: "op-#{i}", meta: "meta-#{i}", v: i} for i in [0..2048]) - @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + describe("for many small updates", function() { + beforeEach(function() { + this.newUpdates = (__range__(0, 2048, true).map((i) => ({ op: `op-${i}`, meta: `meta-${i}`, v: i}))); + return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); + }); - it "should append the initial updates to the existing pack", -> - @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0...510], false).should.equal true + it("should append the initial updates to the existing pack", function() { + return this.PackManager.appendUpdatesToExistingPack.calledWith(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates.slice(0, 510), false).should.equal(true); + }); - it "should insert the first set remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[510...1022], false).should.equal true + it("should insert the first set remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(510, 1022), false).should.equal(true); + }); - it "should insert the second set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1022...1534], false).should.equal true + it("should insert the second set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1022, 1534), false).should.equal(true); + }); - it "should insert the third set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1534...2046], false).should.equal true + it("should insert the third set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1534, 2046), false).should.equal(true); + }); - it "should insert the final set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2046..2048], false).should.equal true + it("should insert the final set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(2046, 2049), false).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "for many big updates", -> - beforeEach -> - longString = ("a" for [0 .. (0.75*@PackManager.MAX_SIZE)]).join("") - @newUpdates = ({ op: "op-#{i}-#{longString}", meta: "meta-#{i}", v: i} for i in [0..4]) - @PackManager.insertCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + return describe("for many big updates", function() { + beforeEach(function() { + const longString = (__range__(0, (0.75*this.PackManager.MAX_SIZE), true).map((j) => "a")).join(""); + this.newUpdates = ([0, 1, 2, 3, 4].map((i) => ({ op: `op-${i}-${longString}`, meta: `meta-${i}`, v: i}))); + return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); + }); - it "should append the initial updates to the existing pack", -> - @PackManager.appendUpdatesToExistingPack.calledWith(@project_id, @doc_id, @lastUpdate, @newUpdates[0..0], false).should.equal true + it("should append the initial updates to the existing pack", function() { + return this.PackManager.appendUpdatesToExistingPack.calledWith(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates.slice(0, 1), false).should.equal(true); + }); - it "should insert the first set remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[1..1], false).should.equal true + it("should insert the first set remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1, 2), false).should.equal(true); + }); - it "should insert the second set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[2..2], false).should.equal true + it("should insert the second set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(2, 3), false).should.equal(true); + }); - it "should insert the third set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[3..3], false).should.equal true + it("should insert the third set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(3, 4), false).should.equal(true); + }); - it "should insert the final set of remaining updates as a new pack", -> - @PackManager.insertUpdatesIntoNewPack.calledWith(@project_id, @doc_id, @newUpdates[4..4], false).should.equal true + it("should insert the final set of remaining updates as a new pack", function() { + return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(4, 5), false).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); - describe "flushCompressedUpdates", -> - describe "when there is no previous update", -> - beforeEach -> - @PackManager.flushCompressedUpdates @project_id, @doc_id, null, @newUpdates, true, @callback + describe("flushCompressedUpdates", () => + describe("when there is no previous update", function() { + beforeEach(function() { + return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, true, this.callback); + }); - describe "for a small update that will expire", -> - it "should insert the update into mongo", -> - @db.docHistory.save.calledWithMatch({ - pack: @newUpdates, - project_id: ObjectId(@project_id), - doc_id: ObjectId(@doc_id) - n: @newUpdates.length - v: @newUpdates[0].v - v_end: @newUpdates[@newUpdates.length-1].v - }).should.equal true + return describe("for a small update that will expire", function() { + it("should insert the update into mongo", function() { + return this.db.docHistory.save.calledWithMatch({ + pack: this.newUpdates, + project_id: ObjectId(this.project_id), + doc_id: ObjectId(this.doc_id), + n: this.newUpdates.length, + v: this.newUpdates[0].v, + v_end: this.newUpdates[this.newUpdates.length-1].v + }).should.equal(true); + }); - it "should set an expiry time in the future", -> - @db.docHistory.save.calledWithMatch({ - expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) - }).should.equal true + it("should set an expiry time in the future", function() { + return this.db.docHistory.save.calledWithMatch({ + expiresAt: new Date(Date.now() + (7 * 24 * 3600 * 1000)) + }).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }) + ); - describe "when there is a recent previous update in mongo that expires", -> - beforeEach -> - @lastUpdate = { - _id: "12345" + describe("when there is a recent previous update in mongo that expires", function() { + beforeEach(function() { + this.lastUpdate = { + _id: "12345", pack: [ { op: "op-1", meta: "meta-1", v: 1}, { op: "op-2", meta: "meta-2", v: 2} - ] - n : 2 - sz : 100 - meta: {start_ts: Date.now() - 6 * 3600 * 1000} + ], + n : 2, + sz : 100, + meta: {start_ts: Date.now() - (6 * 3600 * 1000)}, expiresAt: new Date(Date.now()) - } + }; - @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback + return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, true, this.callback); + }); - describe "for a small update that will expire", -> - it "should append the update in mongo", -> - @db.docHistory.findAndModify.calledWithMatch({ - query: {_id: @lastUpdate._id} - update: { $push: {"pack" : {$each: @newUpdates}}, $set: {v_end: @newUpdates[@newUpdates.length-1].v}} - }).should.equal true + return describe("for a small update that will expire", function() { + it("should append the update in mongo", function() { + return this.db.docHistory.findAndModify.calledWithMatch({ + query: {_id: this.lastUpdate._id}, + update: { $push: {"pack" : {$each: this.newUpdates}}, $set: {v_end: this.newUpdates[this.newUpdates.length-1].v}} + }).should.equal(true); + }); - it "should set an expiry time in the future", -> - @db.docHistory.findAndModify.calledWithMatch({ - update: {$set: {expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000)}} - }).should.equal true + it("should set an expiry time in the future", function() { + return this.db.docHistory.findAndModify.calledWithMatch({ + update: {$set: {expiresAt: new Date(Date.now() + (7 * 24 * 3600 * 1000))}} + }).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); - describe "when there is a recent previous update in mongo that expires", -> - beforeEach -> - @PackManager.updateIndex = sinon.stub().callsArg(2) + describe("when there is a recent previous update in mongo that expires", function() { + beforeEach(function() { + this.PackManager.updateIndex = sinon.stub().callsArg(2); - @lastUpdate = { - _id: "12345" + this.lastUpdate = { + _id: "12345", pack: [ { op: "op-1", meta: "meta-1", v: 1}, { op: "op-2", meta: "meta-2", v: 2} - ] - n : 2 - sz : 100 - meta: {start_ts: Date.now() - 6 * 3600 * 1000} + ], + n : 2, + sz : 100, + meta: {start_ts: Date.now() - (6 * 3600 * 1000)}, expiresAt: new Date(Date.now()) - } + }; - @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, false, @callback + return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); + }); - describe "for a small update that will not expire", -> - it "should insert the update into mongo", -> - @db.docHistory.save.calledWithMatch({ - pack: @newUpdates, - project_id: ObjectId(@project_id), - doc_id: ObjectId(@doc_id) - n: @newUpdates.length - v: @newUpdates[0].v - v_end: @newUpdates[@newUpdates.length-1].v - }).should.equal true + return describe("for a small update that will not expire", function() { + it("should insert the update into mongo", function() { + return this.db.docHistory.save.calledWithMatch({ + pack: this.newUpdates, + project_id: ObjectId(this.project_id), + doc_id: ObjectId(this.doc_id), + n: this.newUpdates.length, + v: this.newUpdates[0].v, + v_end: this.newUpdates[this.newUpdates.length-1].v + }).should.equal(true); + }); - it "should not set any expiry time", -> - @db.docHistory.save.neverCalledWithMatch(sinon.match.has("expiresAt")).should.equal true + it("should not set any expiry time", function() { + return this.db.docHistory.save.neverCalledWithMatch(sinon.match.has("expiresAt")).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); - describe "when there is an old previous update in mongo", -> - beforeEach -> - @lastUpdate = { - _id: "12345" + return describe("when there is an old previous update in mongo", function() { + beforeEach(function() { + this.lastUpdate = { + _id: "12345", pack: [ { op: "op-1", meta: "meta-1", v: 1}, { op: "op-2", meta: "meta-2", v: 2} - ] - n : 2 - sz : 100 - meta: {start_ts: Date.now() - 30 * 24 * 3600 * 1000} - expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000) - } + ], + n : 2, + sz : 100, + meta: {start_ts: Date.now() - (30 * 24 * 3600 * 1000)}, + expiresAt: new Date(Date.now() - (30 * 24 * 3600 * 1000)) + }; - @PackManager.flushCompressedUpdates @project_id, @doc_id, @lastUpdate, @newUpdates, true, @callback + return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, true, this.callback); + }); - describe "for a small update that will expire", -> - it "should insert the update into mongo", -> - @db.docHistory.save.calledWithMatch({ - pack: @newUpdates, - project_id: ObjectId(@project_id), - doc_id: ObjectId(@doc_id) - n: @newUpdates.length - v: @newUpdates[0].v - v_end: @newUpdates[@newUpdates.length-1].v - }).should.equal true + return describe("for a small update that will expire", function() { + it("should insert the update into mongo", function() { + return this.db.docHistory.save.calledWithMatch({ + pack: this.newUpdates, + project_id: ObjectId(this.project_id), + doc_id: ObjectId(this.doc_id), + n: this.newUpdates.length, + v: this.newUpdates[0].v, + v_end: this.newUpdates[this.newUpdates.length-1].v + }).should.equal(true); + }); - it "should set an expiry time in the future", -> - @db.docHistory.save.calledWithMatch({ - expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) - }).should.equal true + it("should set an expiry time in the future", function() { + return this.db.docHistory.save.calledWithMatch({ + expiresAt: new Date(Date.now() + (7 * 24 * 3600 * 1000)) + }).should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); + }); - describe "getOpsByVersionRange", -> + describe("getOpsByVersionRange", function() {}); - describe "loadPacksByVersionRange", -> + describe("loadPacksByVersionRange", function() {}); - describe "fetchPacksIfNeeded", -> + describe("fetchPacksIfNeeded", function() {}); - describe "makeProjectIterator", -> + describe("makeProjectIterator", function() {}); - describe "getPackById", -> + describe("getPackById", function() {}); - describe "increaseTTL", -> + describe("increaseTTL", function() {}); - describe "getIndex", -> + describe("getIndex", function() {}); - describe "getPackFromIndex", -> -# getLastPackFromIndex: -# getIndexWithKeys -# initialiseIndex -# updateIndex -# findCompletedPacks -# findUnindexedPacks -# insertPacksIntoIndexWithLock -# _insertPacksIntoIndex -# archivePack -# checkArchivedPack -# processOldPack -# updateIndexIfNeeded -# findUnarchivedPacks + describe("getPackFromIndex", function() {}); +// getLastPackFromIndex: +// getIndexWithKeys +// initialiseIndex +// updateIndex +// findCompletedPacks +// findUnindexedPacks +// insertPacksIntoIndexWithLock +// _insertPacksIntoIndex +// archivePack +// checkArchivedPack +// processOldPack +// updateIndexIfNeeded +// findUnarchivedPacks - describe "checkArchiveNotInProgress", -> + return describe("checkArchiveNotInProgress", function() { - describe "when an archive is in progress", -> - beforeEach -> - @db.docHistoryIndex = - findOne: sinon.stub().callsArgWith(2, null, {inS3:false}) - @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback - it "should call the callback", -> - @callback.called.should.equal true - it "should return an error", -> - @callback.calledWith(sinon.match.has('message')).should.equal true + describe("when an archive is in progress", function() { + beforeEach(function() { + this.db.docHistoryIndex = + {findOne: sinon.stub().callsArgWith(2, null, {inS3:false})}; + return this.PackManager.checkArchiveNotInProgress(this.project_id, this.doc_id, this.pack_id, this.callback); + }); + it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + return it("should return an error", function() { + return this.callback.calledWith(sinon.match.has('message')).should.equal(true); + }); + }); - describe "when an archive is completed", -> - beforeEach -> - @db.docHistoryIndex = - findOne: sinon.stub().callsArgWith(2, null, {inS3:true}) - @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback - it "should call the callback", -> - @callback.called.should.equal true - it "should return an error", -> - @callback.calledWith(sinon.match.has('message')).should.equal true + describe("when an archive is completed", function() { + beforeEach(function() { + this.db.docHistoryIndex = + {findOne: sinon.stub().callsArgWith(2, null, {inS3:true})}; + return this.PackManager.checkArchiveNotInProgress(this.project_id, this.doc_id, this.pack_id, this.callback); + }); + it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + return it("should return an error", function() { + return this.callback.calledWith(sinon.match.has('message')).should.equal(true); + }); + }); - describe "when the archive has not started or completed", -> - beforeEach -> - @db.docHistoryIndex = - findOne: sinon.stub().callsArgWith(2, null, {}) - @PackManager.checkArchiveNotInProgress @project_id, @doc_id, @pack_id, @callback - it "should call the callback with no error", -> - @callback.called.should.equal true - it "should return with no error", -> - (typeof @callback.lastCall.args[0]).should.equal 'undefined' + return describe("when the archive has not started or completed", function() { + beforeEach(function() { + this.db.docHistoryIndex = + {findOne: sinon.stub().callsArgWith(2, null, {})}; + return this.PackManager.checkArchiveNotInProgress(this.project_id, this.doc_id, this.pack_id, this.callback); + }); + it("should call the callback with no error", function() { + return this.callback.called.should.equal(true); + }); + return it("should return with no error", function() { + return (typeof this.callback.lastCall.args[0]).should.equal('undefined'); + }); + }); + }); +}); - # describe "setTTLOnArchivedPack", -> - # beforeEach -> - # @pack_id = "somepackid" - # @onedayinms = 86400000 - # @db.docHistory = - # findAndModify : sinon.stub().callsArgWith(1) + // describe "setTTLOnArchivedPack", -> + // beforeEach -> + // @pack_id = "somepackid" + // @onedayinms = 86400000 + // @db.docHistory = + // findAndModify : sinon.stub().callsArgWith(1) - # it "should set expires to 1 day", (done)-> - # #@PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) - # @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => - # args = @db.docHistory.findAndModify.args[0][0] - # args.query._id.should.equal @pack_id - # args.update['$set'].expiresAt.should.equal @onedayinms - # done() + // it "should set expires to 1 day", (done)-> + // #@PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) + // @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => + // args = @db.docHistory.findAndModify.args[0][0] + // args.query._id.should.equal @pack_id + // args.update['$set'].expiresAt.should.equal @onedayinms + // done() - # describe "_getOneDayInFutureWithRandomDelay", -> - # beforeEach -> - # @onedayinms = 86400000 - # @thirtyMins = 1000 * 60 * 30 + // describe "_getOneDayInFutureWithRandomDelay", -> + // beforeEach -> + // @onedayinms = 86400000 + // @thirtyMins = 1000 * 60 * 30 - # it "should give 1 day + 30 mins random time", (done)-> - # loops = 10000 - # while --loops > 0 - # randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) - # randomDelay.should.be.above(0) - # randomDelay.should.be.below(@thirtyMins + 1) - # done() + // it "should give 1 day + 30 mins random time", (done)-> + // loops = 10000 + // while --loops > 0 + // randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) + // randomDelay.should.be.above(0) + // randomDelay.should.be.below(@thirtyMins + 1) + // done() + +function __range__(left, right, inclusive) { + let range = []; + let ascending = left < right; + let end = !inclusive ? right : ascending ? right + 1 : right - 1; + for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { + range.push(i); + } + return range; +} \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js index b7009f5d74..502762810b 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js @@ -1,87 +1,121 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/RedisManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/RedisManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "RedisManager", -> - beforeEach -> - @RedisManager = SandboxedModule.require modulePath, requires: - "redis-sharelatex" : - createClient: () => @rclient = - auth: sinon.stub() - multi: () => @rclient - "settings-sharelatex": - redis: - history: - key_schema: - uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:#{doc_id}" - docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:#{project_id}" - @doc_id = "doc-id-123" - @project_id = "project-id-123" - @batchSize = 100 - @callback = sinon.stub() +describe("RedisManager", function() { + beforeEach(function() { + this.RedisManager = SandboxedModule.require(modulePath, { requires: { + "redis-sharelatex" : { + createClient: () => { return this.rclient = { + auth: sinon.stub(), + multi: () => this.rclient + }; } + }, + "settings-sharelatex": { + redis: { + history: { + key_schema: { + uncompressedHistoryOps({doc_id}) { return `UncompressedHistoryOps:${doc_id}`; }, + docsWithHistoryOps({project_id}) { return `DocsWithHistoryOps:${project_id}`; } + } + } + } + } + } + } + ); + this.doc_id = "doc-id-123"; + this.project_id = "project-id-123"; + this.batchSize = 100; + return this.callback = sinon.stub(); + }); - describe "getOldestDocUpdates", -> - beforeEach -> - @rawUpdates = [ {v: 42, op: "mock-op-42"}, { v: 45, op: "mock-op-45" }] - @jsonUpdates = (JSON.stringify(update) for update in @rawUpdates) - @rclient.lrange = sinon.stub().callsArgWith(3, null, @jsonUpdates) - @RedisManager.getOldestDocUpdates @doc_id, @batchSize, @callback + describe("getOldestDocUpdates", function() { + beforeEach(function() { + this.rawUpdates = [ {v: 42, op: "mock-op-42"}, { v: 45, op: "mock-op-45" }]; + this.jsonUpdates = (Array.from(this.rawUpdates).map((update) => JSON.stringify(update))); + this.rclient.lrange = sinon.stub().callsArgWith(3, null, this.jsonUpdates); + return this.RedisManager.getOldestDocUpdates(this.doc_id, this.batchSize, this.callback); + }); - it "should read the updates from redis", -> - @rclient.lrange - .calledWith("UncompressedHistoryOps:#{@doc_id}", 0, @batchSize - 1) - .should.equal true + it("should read the updates from redis", function() { + return this.rclient.lrange + .calledWith(`UncompressedHistoryOps:${this.doc_id}`, 0, this.batchSize - 1) + .should.equal(true); + }); - it "should call the callback with the unparsed ops", -> - @callback.calledWith(null, @jsonUpdates).should.equal true + it("should call the callback with the unparsed ops", function() { + return this.callback.calledWith(null, this.jsonUpdates).should.equal(true); + }); - describe "expandDocUpdates", -> - beforeEach -> - @RedisManager.expandDocUpdates @jsonUpdates, @callback + describe("expandDocUpdates", function() { + beforeEach(function() { + return this.RedisManager.expandDocUpdates(this.jsonUpdates, this.callback); + }); - it "should call the callback with the parsed ops", -> - @callback.calledWith(null, @rawUpdates).should.equal true + return it("should call the callback with the parsed ops", function() { + return this.callback.calledWith(null, this.rawUpdates).should.equal(true); + }); + }); - describe "deleteAppliedDocUpdates", -> - beforeEach -> - @rclient.lrem = sinon.stub() - @rclient.srem = sinon.stub() - @rclient.exec = sinon.stub().callsArgWith(0) - @RedisManager.deleteAppliedDocUpdates @project_id, @doc_id, @jsonUpdates, @callback + return describe("deleteAppliedDocUpdates", function() { + beforeEach(function() { + this.rclient.lrem = sinon.stub(); + this.rclient.srem = sinon.stub(); + this.rclient.exec = sinon.stub().callsArgWith(0); + return this.RedisManager.deleteAppliedDocUpdates(this.project_id, this.doc_id, this.jsonUpdates, this.callback); + }); - it "should delete the first update from redis", -> - @rclient.lrem - .calledWith("UncompressedHistoryOps:#{@doc_id}", 1, @jsonUpdates[0]) - .should.equal true + it("should delete the first update from redis", function() { + return this.rclient.lrem + .calledWith(`UncompressedHistoryOps:${this.doc_id}`, 1, this.jsonUpdates[0]) + .should.equal(true); + }); - it "should delete the second update from redis", -> - @rclient.lrem - .calledWith("UncompressedHistoryOps:#{@doc_id}", 1, @jsonUpdates[1]) - .should.equal true + it("should delete the second update from redis", function() { + return this.rclient.lrem + .calledWith(`UncompressedHistoryOps:${this.doc_id}`, 1, this.jsonUpdates[1]) + .should.equal(true); + }); - it "should delete the doc from the set of docs with history ops", -> - @rclient.srem - .calledWith("DocsWithHistoryOps:#{@project_id}", @doc_id) - .should.equal true + it("should delete the doc from the set of docs with history ops", function() { + return this.rclient.srem + .calledWith(`DocsWithHistoryOps:${this.project_id}`, this.doc_id) + .should.equal(true); + }); - it "should call the callback ", -> - @callback.called.should.equal true + return it("should call the callback ", function() { + return this.callback.called.should.equal(true); + }); + }); + }); - describe "getDocIdsWithHistoryOps", -> - beforeEach -> - @doc_ids = ["mock-id-1", "mock-id-2"] - @rclient.smembers = sinon.stub().callsArgWith(1, null, @doc_ids) - @RedisManager.getDocIdsWithHistoryOps @project_id, @callback + return describe("getDocIdsWithHistoryOps", function() { + beforeEach(function() { + this.doc_ids = ["mock-id-1", "mock-id-2"]; + this.rclient.smembers = sinon.stub().callsArgWith(1, null, this.doc_ids); + return this.RedisManager.getDocIdsWithHistoryOps(this.project_id, this.callback); + }); - it "should read the doc_ids from redis", -> - @rclient.smembers - .calledWith("DocsWithHistoryOps:#{@project_id}") - .should.equal true + it("should read the doc_ids from redis", function() { + return this.rclient.smembers + .calledWith(`DocsWithHistoryOps:${this.project_id}`) + .should.equal(true); + }); - it "should call the callback with the doc_ids", -> - @callback.calledWith(null, @doc_ids).should.equal true + return it("should call the callback with the doc_ids", function() { + return this.callback.calledWith(null, this.doc_ids).should.equal(true); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js index 11941dab91..4396f92b9c 100644 --- a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js +++ b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js @@ -1,39 +1,53 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/RestoreManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/RestoreManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "RestoreManager", -> - beforeEach -> - @RestoreManager = SandboxedModule.require modulePath, requires: - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } - "./DocumentUpdaterManager": @DocumentUpdaterManager = {} - "./DiffManager": @DiffManager = {} - @callback = sinon.stub() - @project_id = "mock-project-id" - @doc_id = "mock-doc-id" - @user_id = "mock-user-id" - @version = 42 +describe("RestoreManager", function() { + beforeEach(function() { + this.RestoreManager = SandboxedModule.require(modulePath, { requires: { + "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), + "./DocumentUpdaterManager": (this.DocumentUpdaterManager = {}), + "./DiffManager": (this.DiffManager = {}) + } + }); + this.callback = sinon.stub(); + this.project_id = "mock-project-id"; + this.doc_id = "mock-doc-id"; + this.user_id = "mock-user-id"; + return this.version = 42; + }); - describe "restoreToBeforeVersion", -> - beforeEach -> - @content = "mock content" - @DocumentUpdaterManager.setDocument = sinon.stub().callsArg(4) - @DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, @content) - @RestoreManager.restoreToBeforeVersion @project_id, @doc_id, @version, @user_id, @callback + return describe("restoreToBeforeVersion", function() { + beforeEach(function() { + this.content = "mock content"; + this.DocumentUpdaterManager.setDocument = sinon.stub().callsArg(4); + this.DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, this.content); + return this.RestoreManager.restoreToBeforeVersion(this.project_id, this.doc_id, this.version, this.user_id, this.callback); + }); - it "should get the content before the requested version", -> - @DiffManager.getDocumentBeforeVersion - .calledWith(@project_id, @doc_id, @version) - .should.equal true + it("should get the content before the requested version", function() { + return this.DiffManager.getDocumentBeforeVersion + .calledWith(this.project_id, this.doc_id, this.version) + .should.equal(true); + }); - it "should set the document in the document updater", -> - @DocumentUpdaterManager.setDocument - .calledWith(@project_id, @doc_id, @content, @user_id) - .should.equal true + it("should set the document in the document updater", function() { + return this.DocumentUpdaterManager.setDocument + .calledWith(this.project_id, this.doc_id, this.content, this.user_id) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); +}); \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js index b4488c17bc..780ddd483c 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js @@ -1,466 +1,590 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/UpdateCompressor.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/UpdateCompressor.js"; +const SandboxedModule = require('sandboxed-module'); -bigstring = ("a" for [0 .. 2*1024*1024]).join("") -mediumstring = ("a" for [0 .. 1024*1024]).join("") +const bigstring = (__range__(0, 2*1024*1024, true).map((i) => "a")).join(""); +const mediumstring = (__range__(0, 1024*1024, true).map((j) => "a")).join(""); -describe "UpdateCompressor", -> - beforeEach -> - @UpdateCompressor = SandboxedModule.require modulePath, requires: +describe("UpdateCompressor", function() { + beforeEach(function() { + this.UpdateCompressor = SandboxedModule.require(modulePath, { requires: { "../lib/diff_match_patch": require("../../../../app/lib/diff_match_patch") - @user_id = "user-id-1" - @other_user_id = "user-id-2" - @ts1 = Date.now() - @ts2 = Date.now() + 1000 + } + } + ); + this.user_id = "user-id-1"; + this.other_user_id = "user-id-2"; + this.ts1 = Date.now(); + return this.ts2 = Date.now() + 1000; + }); - describe "convertToSingleOpUpdates", -> - it "should split grouped updates into individual updates", -> - expect(@UpdateCompressor.convertToSingleOpUpdates [{ - op: [ @op1 = { p: 0, i: "Foo" }, @op2 = { p: 6, i: "bar"} ] - meta: { ts: @ts1, user_id: @user_id } + describe("convertToSingleOpUpdates", function() { + it("should split grouped updates into individual updates", function() { + return expect(this.UpdateCompressor.convertToSingleOpUpdates([{ + op: [ (this.op1 = { p: 0, i: "Foo" }), (this.op2 = { p: 6, i: "bar"}) ], + meta: { ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: [ @op3 = { p: 10, i: "baz" } ] - meta: { ts: @ts2, user_id: @other_user_id } + op: [ (this.op3 = { p: 10, i: "baz" }) ], + meta: { ts: this.ts2, user_id: this.other_user_id }, v: 43 - }]) - .to.deep.equal [{ - op: @op1, - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + }])) + .to.deep.equal([{ + op: this.op1, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: @op2, - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + op: this.op2, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: @op3, - meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id }, + op: this.op3, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.other_user_id }, v: 43 - }] + }]); + }); - it "should return no-op updates when the op list is empty", -> - expect(@UpdateCompressor.convertToSingleOpUpdates [{ - op: [] - meta: { ts: @ts1, user_id: @user_id } + it("should return no-op updates when the op list is empty", function() { + return expect(this.UpdateCompressor.convertToSingleOpUpdates([{ + op: [], + meta: { ts: this.ts1, user_id: this.user_id }, v: 42 - }]) - .to.deep.equal [{ - op: @UpdateCompressor.NOOP - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + }])) + .to.deep.equal([{ + op: this.UpdateCompressor.NOOP, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 - }] + }]); + }); - it "should ignore comment ops", -> - expect(@UpdateCompressor.convertToSingleOpUpdates [{ - op: [ @op1 = { p: 0, i: "Foo" }, @op2 = { p: 9, c: "baz"}, @op3 = { p: 6, i: "bar"} ] - meta: { ts: @ts1, user_id: @user_id } + return it("should ignore comment ops", function() { + return expect(this.UpdateCompressor.convertToSingleOpUpdates([{ + op: [ (this.op1 = { p: 0, i: "Foo" }), (this.op2 = { p: 9, c: "baz"}), (this.op3 = { p: 6, i: "bar"}) ], + meta: { ts: this.ts1, user_id: this.user_id }, v: 42 - }]) - .to.deep.equal [{ - op: @op1, - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + }])) + .to.deep.equal([{ + op: this.op1, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: @op3, - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id }, + op: this.op3, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 - }] + }]); + }); +}); - describe "concatUpdatesWithSameVersion", -> - it "should concat updates with the same version", -> - expect(@UpdateCompressor.concatUpdatesWithSameVersion [{ - op: @op1 = { p: 0, i: "Foo" } - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + describe("concatUpdatesWithSameVersion", function() { + it("should concat updates with the same version", function() { + return expect(this.UpdateCompressor.concatUpdatesWithSameVersion([{ + op: (this.op1 = { p: 0, i: "Foo" }), + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: @op2 = { p: 6, i: "bar" } - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + op: (this.op2 = { p: 6, i: "bar" }), + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: @op3 = { p: 10, i: "baz" } - meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id } + op: (this.op3 = { p: 10, i: "baz" }), + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.other_user_id }, v: 43 - }]) - .to.deep.equal [{ - op: [ @op1, @op2 ] - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + }])) + .to.deep.equal([{ + op: [ this.op1, this.op2 ], + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 }, { - op: [ @op3 ] - meta: { start_ts: @ts2, end_ts: @ts2, user_id: @other_user_id } + op: [ this.op3 ], + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.other_user_id }, v: 43 - }] + }]); + }); - it "should turn a noop into an empty op", -> - expect(@UpdateCompressor.concatUpdatesWithSameVersion [{ - op: @UpdateCompressor.NOOP - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + return it("should turn a noop into an empty op", function() { + return expect(this.UpdateCompressor.concatUpdatesWithSameVersion([{ + op: this.UpdateCompressor.NOOP, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 - }]) - .to.deep.equal [{ - op: [] - meta: { start_ts: @ts1, end_ts: @ts1, user_id: @user_id } + }])) + .to.deep.equal([{ + op: [], + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, v: 42 - }] + }]); + }); +}); - describe "compress", -> - describe "insert - insert", -> - it "should append one insert to the other", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + describe("compress", function() { + describe("insert - insert", function() { + it("should append one insert to the other", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, i: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 6, i: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "foobar" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "foobar" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should insert one insert inside the other", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + it("should insert one insert inside the other", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 5, i: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 5, i: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "fobaro" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "fobaro" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should not append separated inserts", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + it("should not append separated inserts", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 9, i: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 9, i: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "foo" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "foo" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 9, i: "bar" } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 9, i: "bar" }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should not append inserts that are too big (second op)", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + it("should not append inserts that are too big (second op)", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, i: bigstring } - meta: ts: @ts2, user_id: @user_id + op: { p: 6, i: bigstring }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "foo" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "foo" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, i: bigstring } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 6, i: bigstring }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should not append inserts that are too big (first op)", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: bigstring } - meta: ts: @ts1, user_id: @user_id + it("should not append inserts that are too big (first op)", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: bigstring }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3 + bigstring.length, i: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 3 + bigstring.length, i: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: bigstring } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: bigstring }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3 + bigstring.length, i: "bar" } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 3 + bigstring.length, i: "bar" }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should not append inserts that are too big (first and second op)", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: mediumstring } - meta: ts: @ts1, user_id: @user_id + return it("should not append inserts that are too big (first and second op)", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: mediumstring }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3 + mediumstring.length, i: mediumstring } - meta: ts: @ts2, user_id: @user_id + op: { p: 3 + mediumstring.length, i: mediumstring }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: mediumstring } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: mediumstring }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3 + mediumstring.length, i: mediumstring } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 3 + mediumstring.length, i: mediumstring }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); + }); - describe "delete - delete", -> - it "should append one delete to the other", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, d: "foo" } - meta: ts: @ts1, user_id: @user_id + describe("delete - delete", function() { + it("should append one delete to the other", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, d: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3, d: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 3, d: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, d: "foobar" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, d: "foobar" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should insert one delete inside the other", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, d: "foo" } - meta: ts: @ts1, user_id: @user_id + it("should insert one delete inside the other", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, d: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 1, d: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 1, d: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 1, d: "bafoor" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 1, d: "bafoor" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should not append separated deletes", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, d: "foo" } - meta: ts: @ts1, user_id: @user_id + return it("should not append separated deletes", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, d: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 9, d: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 9, d: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, d: "foo" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, d: "foo" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 9, d: "bar" } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 9, d: "bar" }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); + }); - describe "insert - delete", -> - it "should undo a previous insert", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + describe("insert - delete", function() { + it("should undo a previous insert", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 5, d: "o" } - meta: ts: @ts2, user_id: @user_id + op: { p: 5, d: "o" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "fo" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "fo" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should remove part of an insert from the middle", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "fobaro" } - meta: ts: @ts1, user_id: @user_id + it("should remove part of an insert from the middle", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "fobaro" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 5, d: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 5, d: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "foo" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "foo" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should cancel out two opposite updates", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + it("should cancel out two opposite updates", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3, d: "foo" } - meta: ts: @ts2, user_id: @user_id + op: { p: 3, d: "foo" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [ - op: { p: 3, i: "" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - ] + } + ]); + }); - it "should not combine separated updates", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foo" } - meta: ts: @ts1, user_id: @user_id + it("should not combine separated updates", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foo" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 9, d: "bar" } - meta: ts: @ts2, user_id: @user_id + op: { p: 9, d: "bar" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "foo" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "foo" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 9, d: "bar" } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 9, d: "bar" }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should not combine updates with overlap beyond the end", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, i: "foobar" } - meta: ts: @ts1, user_id: @user_id + return it("should not combine updates with overlap beyond the end", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, i: "foobar" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, d: "bardle" } - meta: ts: @ts2, user_id: @user_id + op: { p: 6, d: "bardle" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "foobar" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "foobar" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, d: "bardle" } - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: { p: 6, d: "bardle" }, + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); + }); - describe "delete - insert", -> - it "should do a diff of the content", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, d: "one two three four five six seven eight" } - meta: ts: @ts1, user_id: @user_id + describe("delete - insert", function() { + it("should do a diff of the content", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, d: "one two three four five six seven eight" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3, i: "one 2 three four five six seven eight" } - meta: ts: @ts2, user_id: @user_id + op: { p: 3, i: "one 2 three four five six seven eight" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 7, d: "two" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 7, d: "two" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 }, { - op: { p: 7, i: "2" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + op: { p: 7, i: "2" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); - it "should return a no-op if the delete and insert are the same", -> - expect(@UpdateCompressor.compressUpdates [{ - op: { p: 3, d: "one two three four five six seven eight" } - meta: ts: @ts1, user_id: @user_id + return it("should return a no-op if the delete and insert are the same", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: { p: 3, d: "one two three four five six seven eight" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 3, i: "one two three four five six seven eight" } - meta: ts: @ts2, user_id: @user_id + op: { p: 3, i: "one two three four five six seven eight" }, + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: { p: 3, i: "" } - meta: start_ts: @ts1, end_ts: @ts2, user_id: @user_id + }])) + .to.deep.equal([{ + op: { p: 3, i: "" }, + meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }); + }); - describe "noop - insert", -> - it "should leave them untouched", -> - expect(@UpdateCompressor.compressUpdates [{ - op: @UpdateCompressor.NOOP - meta: ts: @ts1, user_id: @user_id + describe("noop - insert", () => + it("should leave them untouched", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: this.UpdateCompressor.NOOP, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, i: "bar" } - meta: ts: @ts1, user_id: @user_id + op: { p: 6, i: "bar" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: @UpdateCompressor.NOOP - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: this.UpdateCompressor.NOOP, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, i: "bar" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + op: { p: 6, i: "bar" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 43 - }] + }]); + }) + ); - describe "noop - delete", -> - it "should leave them untouched", -> - expect(@UpdateCompressor.compressUpdates [{ - op: @UpdateCompressor.NOOP - meta: ts: @ts1, user_id: @user_id + return describe("noop - delete", () => + it("should leave them untouched", function() { + return expect(this.UpdateCompressor.compressUpdates([{ + op: this.UpdateCompressor.NOOP, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, d: "bar" } - meta: ts: @ts1, user_id: @user_id + op: { p: 6, d: "bar" }, + meta: { ts: this.ts1, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: @UpdateCompressor.NOOP - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: this.UpdateCompressor.NOOP, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, { - op: { p: 6, d: "bar" } - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + op: { p: 6, d: "bar" }, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 43 - }] + }]); + }) + ); +}); - describe "compressRawUpdates", -> - describe "merging in-place with an array op", -> - it "should not change the existing last updates", -> - expect(@UpdateCompressor.compressRawUpdates { - op: [ {"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + return describe("compressRawUpdates", () => + describe("merging in-place with an array op", () => + it("should not change the existing last updates", function() { + return expect(this.UpdateCompressor.compressRawUpdates({ + op: [ {"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ], + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 }, [{ - op: [{ p: 1006, i: "WORLD" }] - meta: ts: @ts2, user_id: @user_id + op: [{ p: 1006, i: "WORLD" }], + meta: { ts: this.ts2, user_id: this.user_id + }, v: 43 - }]) - .to.deep.equal [{ - op: [{"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ] - meta: start_ts: @ts1, end_ts: @ts1, user_id: @user_id + }])) + .to.deep.equal([{ + op: [{"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ], + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id + }, v: 42 },{ - op: [{"p":1006,"i":"WORLD"}] - meta: start_ts: @ts2, end_ts: @ts2, user_id: @user_id + op: [{"p":1006,"i":"WORLD"}], + meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id + }, v: 43 - }] + }]); + }) + ) +); +}); + +function __range__(left, right, inclusive) { + let range = []; + let ascending = left < right; + let end = !inclusive ? right : ascending ? right + 1 : right - 1; + for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { + range.push(i); + } + return range; +} \ No newline at end of file diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js index a25c3f4ce2..5976fd3335 100644 --- a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js +++ b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js @@ -1,122 +1,159 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/UpdateTrimmer.js" -SandboxedModule = require('sandboxed-module') -tk = require "timekeeper" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/UpdateTrimmer.js"; +const SandboxedModule = require('sandboxed-module'); +const tk = require("timekeeper"); -describe "UpdateTrimmer", -> - beforeEach -> - @now = new Date() - tk.freeze(@now) +describe("UpdateTrimmer", function() { + beforeEach(function() { + this.now = new Date(); + tk.freeze(this.now); - @UpdateTrimmer = SandboxedModule.require modulePath, requires: - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } - "./WebApiManager": @WebApiManager = {} - "./MongoManager": @MongoManager = {} + this.UpdateTrimmer = SandboxedModule.require(modulePath, { requires: { + "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), + "./WebApiManager": (this.WebApiManager = {}), + "./MongoManager": (this.MongoManager = {}) + } + }); - @callback = sinon.stub() - @project_id = "mock-project-id" + this.callback = sinon.stub(); + return this.project_id = "mock-project-id"; + }); - afterEach -> - tk.reset() + afterEach(() => tk.reset()); - describe "shouldTrimUpdates", -> - beforeEach -> - @metadata = {} - @details = - features: {} - @MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, @metadata) - @MongoManager.setProjectMetaData = sinon.stub().callsArgWith(2) - @MongoManager.upgradeHistory = sinon.stub().callsArgWith(1) - @WebApiManager.getProjectDetails = sinon.stub().callsArgWith(1, null, @details) + return describe("shouldTrimUpdates", function() { + beforeEach(function() { + this.metadata = {}; + this.details = + {features: {}}; + this.MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, this.metadata); + this.MongoManager.setProjectMetaData = sinon.stub().callsArgWith(2); + this.MongoManager.upgradeHistory = sinon.stub().callsArgWith(1); + return this.WebApiManager.getProjectDetails = sinon.stub().callsArgWith(1, null, this.details); + }); - describe "with preserveHistory set in the project meta data", -> - beforeEach -> - @metadata.preserveHistory = true - @UpdateTrimmer.shouldTrimUpdates @project_id, @callback + describe("with preserveHistory set in the project meta data", function() { + beforeEach(function() { + this.metadata.preserveHistory = true; + return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); + }); - it "should look up the meta data", -> - @MongoManager.getProjectMetaData - .calledWith(@project_id) - .should.equal true + it("should look up the meta data", function() { + return this.MongoManager.getProjectMetaData + .calledWith(this.project_id) + .should.equal(true); + }); - it "should not look up the project details", -> - @WebApiManager.getProjectDetails + it("should not look up the project details", function() { + return this.WebApiManager.getProjectDetails .called - .should.equal false + .should.equal(false); + }); - it "should return false", -> - @callback.calledWith(null, false).should.equal true + return it("should return false", function() { + return this.callback.calledWith(null, false).should.equal(true); + }); + }); - describe "without preserveHistory set in the project meta data", -> - beforeEach -> - @metadata.preserveHistory = false + describe("without preserveHistory set in the project meta data", function() { + beforeEach(function() { + return this.metadata.preserveHistory = false; + }); - describe "when the project has the versioning feature", -> - beforeEach -> - @details.features.versioning = true - @UpdateTrimmer.shouldTrimUpdates @project_id, @callback + describe("when the project has the versioning feature", function() { + beforeEach(function() { + this.details.features.versioning = true; + return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); + }); - it "should look up the meta data", -> - @MongoManager.getProjectMetaData - .calledWith(@project_id) - .should.equal true + it("should look up the meta data", function() { + return this.MongoManager.getProjectMetaData + .calledWith(this.project_id) + .should.equal(true); + }); - it "should look up the project details", -> - @WebApiManager.getProjectDetails - .calledWith(@project_id) - .should.equal true + it("should look up the project details", function() { + return this.WebApiManager.getProjectDetails + .calledWith(this.project_id) + .should.equal(true); + }); - it "should insert preserveHistory into the metadata", -> - @MongoManager.setProjectMetaData - .calledWith(@project_id, {preserveHistory: true}) - .should.equal true + it("should insert preserveHistory into the metadata", function() { + return this.MongoManager.setProjectMetaData + .calledWith(this.project_id, {preserveHistory: true}) + .should.equal(true); + }); - it "should upgrade any existing history", -> - @MongoManager.upgradeHistory - .calledWith(@project_id) - .should.equal true + it("should upgrade any existing history", function() { + return this.MongoManager.upgradeHistory + .calledWith(this.project_id) + .should.equal(true); + }); - it "should return false", -> - @callback.calledWith(null, false).should.equal true + return it("should return false", function() { + return this.callback.calledWith(null, false).should.equal(true); + }); + }); - describe "when the project does not have the versioning feature", -> - beforeEach -> - @details.features.versioning = false - @UpdateTrimmer.shouldTrimUpdates @project_id, @callback + return describe("when the project does not have the versioning feature", function() { + beforeEach(function() { + this.details.features.versioning = false; + return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); + }); - it "should return true", -> - @callback.calledWith(null, true).should.equal true + return it("should return true", function() { + return this.callback.calledWith(null, true).should.equal(true); + }); + }); + }); - describe "without any meta data", -> - beforeEach -> - @MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, null) + return describe("without any meta data", function() { + beforeEach(function() { + return this.MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, null); + }); - describe "when the project has the versioning feature", -> - beforeEach -> - @details.features.versioning = true - @UpdateTrimmer.shouldTrimUpdates @project_id, @callback + describe("when the project has the versioning feature", function() { + beforeEach(function() { + this.details.features.versioning = true; + return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); + }); - it "should insert preserveHistory into the metadata", -> - @MongoManager.setProjectMetaData - .calledWith(@project_id, {preserveHistory: true}) - .should.equal true + it("should insert preserveHistory into the metadata", function() { + return this.MongoManager.setProjectMetaData + .calledWith(this.project_id, {preserveHistory: true}) + .should.equal(true); + }); - it "should upgrade any existing history", -> - @MongoManager.upgradeHistory - .calledWith(@project_id) - .should.equal true + it("should upgrade any existing history", function() { + return this.MongoManager.upgradeHistory + .calledWith(this.project_id) + .should.equal(true); + }); - it "should return false", -> - @callback.calledWith(null, false).should.equal true + return it("should return false", function() { + return this.callback.calledWith(null, false).should.equal(true); + }); + }); - describe "when the project does not have the versioning feature", -> - beforeEach -> - @details.features.versioning = false - @UpdateTrimmer.shouldTrimUpdates @project_id, @callback + return describe("when the project does not have the versioning feature", function() { + beforeEach(function() { + this.details.features.versioning = false; + return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); + }); - it "should return true", -> - @callback.calledWith(null, true).should.equal true + return it("should return true", function() { + return this.callback.calledWith(null, true).should.equal(true); + }); + }); + }); + }); +}); diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js index 905a48aa28..53b9fefe87 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js @@ -1,823 +1,1013 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/UpdatesManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/UpdatesManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "UpdatesManager", -> - beforeEach -> - @UpdatesManager = SandboxedModule.require modulePath, requires: - "./UpdateCompressor": @UpdateCompressor = {} - "./MongoManager" : @MongoManager = {} - "./PackManager" : @PackManager = {} - "./RedisManager" : @RedisManager = {} - "./LockManager" : @LockManager = {} - "./WebApiManager": @WebApiManager = {} - "./UpdateTrimmer": @UpdateTrimmer = {} - "./DocArchiveManager": @DocArchiveManager = {} - "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() } - "settings-sharelatex": - redis: lock: key_schema: - historyLock: ({doc_id}) -> "HistoryLock:#{doc_id}" - @doc_id = "doc-id-123" - @project_id = "project-id-123" - @callback = sinon.stub() - @temporary = "temp-mock" +describe("UpdatesManager", function() { + beforeEach(function() { + this.UpdatesManager = SandboxedModule.require(modulePath, { requires: { + "./UpdateCompressor": (this.UpdateCompressor = {}), + "./MongoManager" : (this.MongoManager = {}), + "./PackManager" : (this.PackManager = {}), + "./RedisManager" : (this.RedisManager = {}), + "./LockManager" : (this.LockManager = {}), + "./WebApiManager": (this.WebApiManager = {}), + "./UpdateTrimmer": (this.UpdateTrimmer = {}), + "./DocArchiveManager": (this.DocArchiveManager = {}), + "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }, + "settings-sharelatex": { + redis: { lock: { key_schema: { + historyLock({doc_id}) { return `HistoryLock:${doc_id}`; } + } + } + } + } + } + } + ); + this.doc_id = "doc-id-123"; + this.project_id = "project-id-123"; + this.callback = sinon.stub(); + return this.temporary = "temp-mock"; + }); - describe "compressAndSaveRawUpdates", -> - describe "when there are no raw ops", -> - beforeEach -> - @MongoManager.peekLastCompressedUpdate = sinon.stub() - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, [], @temporary, @callback + describe("compressAndSaveRawUpdates", function() { + describe("when there are no raw ops", function() { + beforeEach(function() { + this.MongoManager.peekLastCompressedUpdate = sinon.stub(); + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, [], this.temporary, this.callback); + }); - it "should not need to access the database", -> - @MongoManager.peekLastCompressedUpdate.called.should.equal false + it("should not need to access the database", function() { + return this.MongoManager.peekLastCompressedUpdate.called.should.equal(false); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when there is no compressed history to begin with", -> - beforeEach -> - @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @compressedUpdates = [ { v: 13, op: "compressed-op-12" } ] + describe("when there is no compressed history to begin with", function() { + beforeEach(function() { + this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; + this.compressedUpdates = [ { v: 13, op: "compressed-op-12" } ]; - @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null) - @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) - @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null); + this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5); + this.UpdateCompressor.compressRawUpdates = sinon.stub().returns(this.compressedUpdates); + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should look at the last compressed op", -> - @MongoManager.peekLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should look at the last compressed op", function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should save the compressed ops as a pack", -> - @PackManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, null, @compressedUpdates, @temporary) - .should.equal true + it("should save the compressed ops as a pack", function() { + return this.PackManager.insertCompressedUpdates + .calledWith(this.project_id, this.doc_id, null, this.compressedUpdates, this.temporary) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when the raw ops need appending to existing history", -> - beforeEach -> - @lastCompressedUpdate = { v: 11, op: "compressed-op-11" } - @compressedUpdates = [ { v: 12, op: "compressed-op-11+12" }, { v: 13, op: "compressed-op-12" } ] + describe("when the raw ops need appending to existing history", function() { + beforeEach(function() { + this.lastCompressedUpdate = { v: 11, op: "compressed-op-11" }; + this.compressedUpdates = [ { v: 12, op: "compressed-op-11+12" }, { v: 13, op: "compressed-op-12" } ]; - @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) - @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) - @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) + this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, this.lastCompressedUpdate, this.lastCompressedUpdate.v); + this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5); + return this.UpdateCompressor.compressRawUpdates = sinon.stub().returns(this.compressedUpdates); + }); - describe "when the raw ops start where the existing history ends", -> - beforeEach -> - @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + describe("when the raw ops start where the existing history ends", function() { + beforeEach(function() { + this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should look at the last compressed op", -> - @MongoManager.peekLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should look at the last compressed op", function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should compress the raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(null, @rawUpdates) - .should.equal true + it("should compress the raw ops", function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(null, this.rawUpdates) + .should.equal(true); + }); - it "should save the new compressed ops into a pack", -> - @PackManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @lastCompressedUpdate, @compressedUpdates, @temporary) - .should.equal true + it("should save the new compressed ops into a pack", function() { + return this.PackManager.insertCompressedUpdates + .calledWith(this.project_id, this.doc_id, this.lastCompressedUpdate, this.compressedUpdates, this.temporary) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when the raw ops start where the existing history ends and the history is in a pack", -> - beforeEach -> - @lastCompressedUpdate = {pack: [{ v: 11, op: "compressed-op-11" }], v:11} - @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @lastCompressedUpdate, @lastCompressedUpdate.v) - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + describe("when the raw ops start where the existing history ends and the history is in a pack", function() { + beforeEach(function() { + this.lastCompressedUpdate = {pack: [{ v: 11, op: "compressed-op-11" }], v:11}; + this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; + this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, this.lastCompressedUpdate, this.lastCompressedUpdate.v); + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should look at the last compressed op", -> - @MongoManager.peekLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should look at the last compressed op", function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should compress the raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(null, @rawUpdates) - .should.equal true + it("should compress the raw ops", function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(null, this.rawUpdates) + .should.equal(true); + }); - it "should save the new compressed ops into a pack", -> - @PackManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, @lastCompressedUpdate, @compressedUpdates, @temporary) - .should.equal true + it("should save the new compressed ops into a pack", function() { + return this.PackManager.insertCompressedUpdates + .calledWith(this.project_id, this.doc_id, this.lastCompressedUpdate, this.compressedUpdates, this.temporary) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when some raw ops are passed that have already been compressed", -> - beforeEach -> - @rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] + describe("when some raw ops are passed that have already been compressed", function() { + beforeEach(function() { + this.rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should only compress the more recent raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(null, @rawUpdates.slice(-2)) - .should.equal true + return it("should only compress the more recent raw ops", function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(null, this.rawUpdates.slice(-2)) + .should.equal(true); + }); + }); - describe "when the raw ops do not follow from the last compressed op version", -> - beforeEach -> - @rawUpdates = [{ v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + describe("when the raw ops do not follow from the last compressed op version", function() { + beforeEach(function() { + this.rawUpdates = [{ v: 13, op: "mock-op-13" }]; + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should call the callback with an error", -> - @callback + it("should call the callback with an error", function() { + return this.callback .calledWith(sinon.match.has('message', "Tried to apply raw op at version 13 to last compressed update with version 11 from unknown time")) - .should.equal true + .should.equal(true); + }); - it "should not insert any update into mongo", -> - @PackManager.insertCompressedUpdates.called.should.equal false + return it("should not insert any update into mongo", function() { + return this.PackManager.insertCompressedUpdates.called.should.equal(false); + }); + }); - describe "when the raw ops are out of order", -> - beforeEach -> - @rawUpdates = [{ v: 13, op: "mock-op-13" }, { v: 12, op: "mock-op-12" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + return describe("when the raw ops are out of order", function() { + beforeEach(function() { + this.rawUpdates = [{ v: 13, op: "mock-op-13" }, { v: 12, op: "mock-op-12" }]; + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should call the callback with an error", -> - @callback + it("should call the callback with an error", function() { + return this.callback .calledWith(sinon.match.has('message')) - .should.equal true + .should.equal(true); + }); - it "should not insert any update into mongo", -> - @PackManager.insertCompressedUpdates.called.should.equal false + return it("should not insert any update into mongo", function() { + return this.PackManager.insertCompressedUpdates.called.should.equal(false); + }); + }); + }); - describe "when the raw ops need appending to existing history which is in S3", -> - beforeEach -> - @lastCompressedUpdate = null - @lastVersion = 11 - @compressedUpdates = [ { v: 13, op: "compressed-op-12" } ] + return describe("when the raw ops need appending to existing history which is in S3", function() { + beforeEach(function() { + this.lastCompressedUpdate = null; + this.lastVersion = 11; + this.compressedUpdates = [ { v: 13, op: "compressed-op-12" } ]; - @MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, @lastVersion) - @PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) - @UpdateCompressor.compressRawUpdates = sinon.stub().returns(@compressedUpdates) + this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, this.lastVersion); + this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5); + return this.UpdateCompressor.compressRawUpdates = sinon.stub().returns(this.compressedUpdates); + }); - describe "when the raw ops start where the existing history ends", -> - beforeEach -> - @rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }] - @UpdatesManager.compressAndSaveRawUpdates @project_id, @doc_id, @rawUpdates, @temporary, @callback + return describe("when the raw ops start where the existing history ends", function() { + beforeEach(function() { + this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; + return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); + }); - it "should try to look at the last compressed op", -> - @MongoManager.peekLastCompressedUpdate - .calledWith(@doc_id) - .should.equal true + it("should try to look at the last compressed op", function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true); + }); - it "should compress the last compressed op and the raw ops", -> - @UpdateCompressor.compressRawUpdates - .calledWith(@lastCompressedUpdate, @rawUpdates) - .should.equal true + it("should compress the last compressed op and the raw ops", function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(this.lastCompressedUpdate, this.rawUpdates) + .should.equal(true); + }); - it "should save the compressed ops", -> - @PackManager.insertCompressedUpdates - .calledWith(@project_id, @doc_id, null, @compressedUpdates, @temporary) - .should.equal true + it("should save the compressed ops", function() { + return this.PackManager.insertCompressedUpdates + .calledWith(this.project_id, this.doc_id, null, this.compressedUpdates, this.temporary) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); + }); - describe "processUncompressedUpdates", -> - beforeEach -> - @UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(4) - @RedisManager.deleteAppliedDocUpdates = sinon.stub().callsArg(3) - @MongoManager.backportProjectId = sinon.stub().callsArg(2) - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") + describe("processUncompressedUpdates", function() { + beforeEach(function() { + this.UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(4); + this.RedisManager.deleteAppliedDocUpdates = sinon.stub().callsArg(3); + this.MongoManager.backportProjectId = sinon.stub().callsArg(2); + return this.UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, (this.temporary = "temp mock")); + }); - describe "when there is fewer than one batch to send", -> - beforeEach -> - @updates = ["mock-update"] - @RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, @updates) - @RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, @updates) - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, @callback + describe("when there is fewer than one batch to send", function() { + beforeEach(function() { + this.updates = ["mock-update"]; + this.RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, this.updates); + this.RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, this.updates); + return this.UpdatesManager.processUncompressedUpdates(this.project_id, this.doc_id, this.temporary, this.callback); + }); - it "should get the oldest updates", -> - @RedisManager.getOldestDocUpdates - .calledWith(@doc_id, @UpdatesManager.REDIS_READ_BATCH_SIZE) - .should.equal true + it("should get the oldest updates", function() { + return this.RedisManager.getOldestDocUpdates + .calledWith(this.doc_id, this.UpdatesManager.REDIS_READ_BATCH_SIZE) + .should.equal(true); + }); - it "should compress and save the updates", -> - @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates, @temporary) - .should.equal true + it("should compress and save the updates", function() { + return this.UpdatesManager.compressAndSaveRawUpdates + .calledWith(this.project_id, this.doc_id, this.updates, this.temporary) + .should.equal(true); + }); - it "should delete the batch of uncompressed updates that was just processed", -> - @RedisManager.deleteAppliedDocUpdates - .calledWith(@project_id, @doc_id, @updates) - .should.equal true + it("should delete the batch of uncompressed updates that was just processed", function() { + return this.RedisManager.deleteAppliedDocUpdates + .calledWith(this.project_id, this.doc_id, this.updates) + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "when there are multiple batches to send", -> - beforeEach (done) -> - @UpdatesManager.REDIS_READ_BATCH_SIZE = 2 - @updates = ["mock-update-0", "mock-update-1", "mock-update-2", "mock-update-3", "mock-update-4"] - @redisArray = @updates.slice() - @RedisManager.getOldestDocUpdates = (doc_id, batchSize, callback = (error, updates) ->) => - updates = @redisArray.slice(0, batchSize) - @redisArray = @redisArray.slice(batchSize) - callback null, updates - sinon.spy @RedisManager, "getOldestDocUpdates" - @RedisManager.expandDocUpdates = (jsonUpdates, callback) => - callback null, jsonUpdates - sinon.spy @RedisManager, "expandDocUpdates" - @UpdatesManager.processUncompressedUpdates @project_id, @doc_id, @temporary, (args...) => - @callback(args...) - done() + return describe("when there are multiple batches to send", function() { + beforeEach(function(done) { + this.UpdatesManager.REDIS_READ_BATCH_SIZE = 2; + this.updates = ["mock-update-0", "mock-update-1", "mock-update-2", "mock-update-3", "mock-update-4"]; + this.redisArray = this.updates.slice(); + this.RedisManager.getOldestDocUpdates = (doc_id, batchSize, callback) => { + if (callback == null) { callback = function(error, updates) {}; } + const updates = this.redisArray.slice(0, batchSize); + this.redisArray = this.redisArray.slice(batchSize); + return callback(null, updates); + }; + sinon.spy(this.RedisManager, "getOldestDocUpdates"); + this.RedisManager.expandDocUpdates = (jsonUpdates, callback) => { + return callback(null, jsonUpdates); + }; + sinon.spy(this.RedisManager, "expandDocUpdates"); + return this.UpdatesManager.processUncompressedUpdates(this.project_id, this.doc_id, this.temporary, (...args) => { + this.callback(...Array.from(args || [])); + return done(); + }); + }); - it "should get the oldest updates in three batches ", -> - @RedisManager.getOldestDocUpdates.callCount.should.equal 3 + it("should get the oldest updates in three batches ", function() { + return this.RedisManager.getOldestDocUpdates.callCount.should.equal(3); + }); - it "should compress and save the updates in batches", -> - @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates.slice(0,2), @temporary) - .should.equal true - @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates.slice(2,4), @temporary) - .should.equal true - @UpdatesManager.compressAndSaveRawUpdates - .calledWith(@project_id, @doc_id, @updates.slice(4,5), @temporary) - .should.equal true + it("should compress and save the updates in batches", function() { + this.UpdatesManager.compressAndSaveRawUpdates + .calledWith(this.project_id, this.doc_id, this.updates.slice(0,2), this.temporary) + .should.equal(true); + this.UpdatesManager.compressAndSaveRawUpdates + .calledWith(this.project_id, this.doc_id, this.updates.slice(2,4), this.temporary) + .should.equal(true); + return this.UpdatesManager.compressAndSaveRawUpdates + .calledWith(this.project_id, this.doc_id, this.updates.slice(4,5), this.temporary) + .should.equal(true); + }); - it "should delete the batches of uncompressed updates", -> - @RedisManager.deleteAppliedDocUpdates.callCount.should.equal 3 + it("should delete the batches of uncompressed updates", function() { + return this.RedisManager.deleteAppliedDocUpdates.callCount.should.equal(3); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); + }); - describe "processCompressedUpdatesWithLock", -> - beforeEach -> - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") - @MongoManager.backportProjectId = sinon.stub().callsArg(2) - @UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3) - @LockManager.runWithLock = sinon.stub().callsArg(2) - @UpdatesManager.processUncompressedUpdatesWithLock @project_id, @doc_id, @callback + describe("processCompressedUpdatesWithLock", function() { + beforeEach(function() { + this.UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, (this.temporary = "temp mock")); + this.MongoManager.backportProjectId = sinon.stub().callsArg(2); + this.UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3); + this.LockManager.runWithLock = sinon.stub().callsArg(2); + return this.UpdatesManager.processUncompressedUpdatesWithLock(this.project_id, this.doc_id, this.callback); + }); - it "should check if the updates are temporary", -> - @UpdateTrimmer.shouldTrimUpdates - .calledWith(@project_id) - .should.equal true + it("should check if the updates are temporary", function() { + return this.UpdateTrimmer.shouldTrimUpdates + .calledWith(this.project_id) + .should.equal(true); + }); - it "should backport the project id", -> - @MongoManager.backportProjectId - .calledWith(@project_id, @doc_id) - .should.equal true + it("should backport the project id", function() { + return this.MongoManager.backportProjectId + .calledWith(this.project_id, this.doc_id) + .should.equal(true); + }); - it "should run processUncompressedUpdates with the lock", -> - @LockManager.runWithLock + it("should run processUncompressedUpdates with the lock", function() { + return this.LockManager.runWithLock .calledWith( - "HistoryLock:#{@doc_id}" + `HistoryLock:${this.doc_id}` ) - .should.equal true + .should.equal(true); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "getDocUpdates", -> - beforeEach -> - @updates = ["mock-updates"] - @options = { to: "mock-to", limit: "mock-limit" } - @PackManager.getOpsByVersionRange = sinon.stub().callsArgWith(4, null, @updates) - @UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2) - @UpdatesManager.getDocUpdates @project_id, @doc_id, @options, @callback + describe("getDocUpdates", function() { + beforeEach(function() { + this.updates = ["mock-updates"]; + this.options = { to: "mock-to", limit: "mock-limit" }; + this.PackManager.getOpsByVersionRange = sinon.stub().callsArgWith(4, null, this.updates); + this.UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2); + return this.UpdatesManager.getDocUpdates(this.project_id, this.doc_id, this.options, this.callback); + }); - it "should process outstanding updates", -> - @UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(@project_id, @doc_id) - .should.equal true + it("should process outstanding updates", function() { + return this.UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(this.project_id, this.doc_id) + .should.equal(true); + }); - it "should get the updates from the database", -> - @PackManager.getOpsByVersionRange - .calledWith(@project_id, @doc_id, @options.from, @options.to) - .should.equal true + it("should get the updates from the database", function() { + return this.PackManager.getOpsByVersionRange + .calledWith(this.project_id, this.doc_id, this.options.from, this.options.to) + .should.equal(true); + }); - it "should return the updates", -> - @callback - .calledWith(null, @updates) - .should.equal true + return it("should return the updates", function() { + return this.callback + .calledWith(null, this.updates) + .should.equal(true); + }); + }); - describe "getDocUpdatesWithUserInfo", -> - beforeEach -> - @updates = ["mock-updates"] - @options = { to: "mock-to", limit: "mock-limit" } - @updatesWithUserInfo = ["updates-with-user-info"] - @UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, @updates) - @UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - @UpdatesManager.getDocUpdatesWithUserInfo @project_id, @doc_id, @options, @callback + describe("getDocUpdatesWithUserInfo", function() { + beforeEach(function() { + this.updates = ["mock-updates"]; + this.options = { to: "mock-to", limit: "mock-limit" }; + this.updatesWithUserInfo = ["updates-with-user-info"]; + this.UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, this.updates); + this.UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, this.updatesWithUserInfo); + return this.UpdatesManager.getDocUpdatesWithUserInfo(this.project_id, this.doc_id, this.options, this.callback); + }); - it "should get the updates", -> - @UpdatesManager.getDocUpdates - .calledWith(@project_id, @doc_id, @options) - .should.equal true + it("should get the updates", function() { + return this.UpdatesManager.getDocUpdates + .calledWith(this.project_id, this.doc_id, this.options) + .should.equal(true); + }); - it "should file the updates with the user info", -> - @UpdatesManager.fillUserInfo - .calledWith(@updates) - .should.equal true + it("should file the updates with the user info", function() { + return this.UpdatesManager.fillUserInfo + .calledWith(this.updates) + .should.equal(true); + }); - it "should return the updates with the filled details", -> - @callback.calledWith(null, @updatesWithUserInfo).should.equal true + return it("should return the updates with the filled details", function() { + return this.callback.calledWith(null, this.updatesWithUserInfo).should.equal(true); + }); + }); - describe "processUncompressedUpdatesForProject", -> - beforeEach (done) -> - @doc_ids = ["mock-id-1", "mock-id-2"] - @UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, @temporary = "temp mock") - @MongoManager.backportProjectId = sinon.stub().callsArg(2) - @UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon.stub().callsArg(3) - @RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, @doc_ids) - @UpdatesManager.processUncompressedUpdatesForProject @project_id, () => - @callback() - done() + describe("processUncompressedUpdatesForProject", function() { + beforeEach(function(done) { + this.doc_ids = ["mock-id-1", "mock-id-2"]; + this.UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, (this.temporary = "temp mock")); + this.MongoManager.backportProjectId = sinon.stub().callsArg(2); + this.UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon.stub().callsArg(3); + this.RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, this.doc_ids); + return this.UpdatesManager.processUncompressedUpdatesForProject(this.project_id, () => { + this.callback(); + return done(); + }); + }); - it "should get all the docs with history ops", -> - @RedisManager.getDocIdsWithHistoryOps - .calledWith(@project_id) - .should.equal true + it("should get all the docs with history ops", function() { + return this.RedisManager.getDocIdsWithHistoryOps + .calledWith(this.project_id) + .should.equal(true); + }); - it "should process the doc ops for the each doc_id", -> - for doc_id in @doc_ids - @UpdatesManager._processUncompressedUpdatesForDocWithLock - .calledWith(@project_id, doc_id, @temporary) - .should.equal true + it("should process the doc ops for the each doc_id", function() { + return Array.from(this.doc_ids).map((doc_id) => + this.UpdatesManager._processUncompressedUpdatesForDocWithLock + .calledWith(this.project_id, doc_id, this.temporary) + .should.equal(true)); + }); - it "should call the callback", -> - @callback.called.should.equal true + return it("should call the callback", function() { + return this.callback.called.should.equal(true); + }); + }); - describe "getSummarizedProjectUpdates", -> - beforeEach -> - @updates = [{doc_id: 123, v:456, op: "mock-updates", meta: {user_id: 123, start_ts: 1233, end_ts:1234}}] - @options = { before: "mock-before", limit: "mock-limit" } - @summarizedUpdates = [ + describe("getSummarizedProjectUpdates", function() { + beforeEach(function() { + this.updates = [{doc_id: 123, v:456, op: "mock-updates", meta: {user_id: 123, start_ts: 1233, end_ts:1234}}]; + this.options = { before: "mock-before", limit: "mock-limit" }; + this.summarizedUpdates = [ {meta: {user_ids: [123], start_ts: 1233, end_ts:1234},docs:{"123":{fromV:456,toV:456}}} - ] - @updatesWithUserInfo = ["updates-with-user-info"] - @done_state = false - @iterator = - next: (cb) => - @done_state = true - cb(null, @updates) - done: () => - @done_state - @PackManager.makeProjectIterator = sinon.stub().callsArgWith(2, null, @iterator) - @UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1) - @UpdatesManager.fillSummarizedUserInfo = sinon.stub().callsArgWith(1, null, @updatesWithUserInfo) - @UpdatesManager.getSummarizedProjectUpdates @project_id, @options, @callback + ]; + this.updatesWithUserInfo = ["updates-with-user-info"]; + this.done_state = false; + this.iterator = { + next: cb => { + this.done_state = true; + return cb(null, this.updates); + }, + done: () => { + return this.done_state; + } + }; + this.PackManager.makeProjectIterator = sinon.stub().callsArgWith(2, null, this.iterator); + this.UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1); + this.UpdatesManager.fillSummarizedUserInfo = sinon.stub().callsArgWith(1, null, this.updatesWithUserInfo); + return this.UpdatesManager.getSummarizedProjectUpdates(this.project_id, this.options, this.callback); + }); - it "should process any outstanding updates", -> - @UpdatesManager.processUncompressedUpdatesForProject - .calledWith(@project_id) - .should.equal true + it("should process any outstanding updates", function() { + return this.UpdatesManager.processUncompressedUpdatesForProject + .calledWith(this.project_id) + .should.equal(true); + }); - it "should get the updates", -> - @PackManager.makeProjectIterator - .calledWith(@project_id, @options.before) - .should.equal true + it("should get the updates", function() { + return this.PackManager.makeProjectIterator + .calledWith(this.project_id, this.options.before) + .should.equal(true); + }); - it "should fill the updates with the user info", -> - @UpdatesManager.fillSummarizedUserInfo - .calledWith(@summarizedUpdates) - .should.equal true + it("should fill the updates with the user info", function() { + return this.UpdatesManager.fillSummarizedUserInfo + .calledWith(this.summarizedUpdates) + .should.equal(true); + }); - it "should return the updates with the filled details", -> - @callback.calledWith(null, @updatesWithUserInfo).should.equal true + return it("should return the updates with the filled details", function() { + return this.callback.calledWith(null, this.updatesWithUserInfo).should.equal(true); + }); + }); - # describe "_extendBatchOfSummarizedUpdates", -> - # beforeEach -> - # @before = Date.now() - # @min_count = 2 - # @existingSummarizedUpdates = ["summarized-updates-3"] - # @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + // describe "_extendBatchOfSummarizedUpdates", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 2 + // @existingSummarizedUpdates = ["summarized-updates-3"] + // @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - # describe "when there are updates to get", -> - # beforeEach -> - # @updates = [ - # {op: "mock-op-1", meta: end_ts: @before - 10}, - # {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} - # ] - # @existingSummarizedUpdates = ["summarized-updates-3"] - # @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - # @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - # @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - # @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback + // describe "when there are updates to get", -> + // beforeEach -> + // @updates = [ + // {op: "mock-op-1", meta: end_ts: @before - 10}, + // {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} + // ] + // @existingSummarizedUpdates = ["summarized-updates-3"] + // @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + // @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + // @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + // @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - # it "should get the updates", -> - # @UpdatesManager.getProjectUpdatesWithUserInfo - # .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) - # .should.equal true + // it "should get the updates", -> + // @UpdatesManager.getProjectUpdatesWithUserInfo + // .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) + // .should.equal true - # it "should summarize the updates", -> - # @UpdatesManager._summarizeUpdates - # .calledWith(@updates, @existingSummarizedUpdates) - # .should.equal true + // it "should summarize the updates", -> + // @UpdatesManager._summarizeUpdates + // .calledWith(@updates, @existingSummarizedUpdates) + // .should.equal true - # it "should call the callback with the summarized updates and the next before timestamp", -> - # @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true + // it "should call the callback with the summarized updates and the next before timestamp", -> + // @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true - # describe "when there are no more updates", -> - # beforeEach -> - # @updates = [] - # @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - # @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - # @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback + // describe "when there are no more updates", -> + // beforeEach -> + // @updates = [] + // @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + // @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + // @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - # it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> - # @callback.calledWith(null, @summarizedUpdates, null).should.equal true + // it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> + // @callback.calledWith(null, @summarizedUpdates, null).should.equal true - # describe "getSummarizedProjectUpdates", -> - # describe "when one batch of updates is enough to meet the limit", -> - # beforeEach -> - # @before = Date.now() - # @min_count = 2 - # @updates = ["summarized-updates-3", "summarized-updates-2"] - # @nextBeforeTimestamp = @before - 100 - # @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) - # @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + // describe "getSummarizedProjectUpdates", -> + // describe "when one batch of updates is enough to meet the limit", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 2 + // @updates = ["summarized-updates-3", "summarized-updates-2"] + // @nextBeforeTimestamp = @before - 100 + // @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) + // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - # it "should get the batch of summarized updates", -> - # @UpdatesManager._extendBatchOfSummarizedUpdates - # .calledWith(@project_id, [], @before, @min_count) - # .should.equal true + // it "should get the batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, [], @before, @min_count) + // .should.equal true - # it "should call the callback with the updates", -> - # @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true + // it "should call the callback with the updates", -> + // @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true - # describe "when multiple batches are needed to meet the limit", -> - # beforeEach -> - # @before = Date.now() - # @min_count = 4 - # @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - # @nextBeforeTimestamp = @before - 100 - # @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] - # @nextNextBeforeTimestamp = @before - 200 - # @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => - # if existingUpdates.length == 0 - # callback null, @firstBatch, @nextBeforeTimestamp - # else - # callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp - # sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" - # @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + // describe "when multiple batches are needed to meet the limit", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 4 + // @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + // @nextBeforeTimestamp = @before - 100 + // @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] + // @nextNextBeforeTimestamp = @before - 200 + // @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => + // if existingUpdates.length == 0 + // callback null, @firstBatch, @nextBeforeTimestamp + // else + // callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp + // sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" + // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - # it "should get the first batch of summarized updates", -> - # @UpdatesManager._extendBatchOfSummarizedUpdates - # .calledWith(@project_id, [], @before, @min_count) - # .should.equal true + // it "should get the first batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, [], @before, @min_count) + // .should.equal true - # it "should get the second batch of summarized updates", -> - # @UpdatesManager._extendBatchOfSummarizedUpdates - # .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) - # .should.equal true + // it "should get the second batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) + // .should.equal true - # it "should call the callback with all the updates", -> - # @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true + // it "should call the callback with all the updates", -> + // @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true - # describe "when the end of the database is hit", -> - # beforeEach -> - # @before = Date.now() - # @min_count = 4 - # @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - # @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) - # @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + // describe "when the end of the database is hit", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 4 + // @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + // @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) + // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - # it "should get the batch of summarized updates", -> - # @UpdatesManager._extendBatchOfSummarizedUpdates - # .calledWith(@project_id, [], @before, @min_count) - # .should.equal true + // it "should get the batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, [], @before, @min_count) + // .should.equal true - # it "should call the callback with the updates", -> - # @callback.calledWith(null, @updates, null).should.equal true + // it "should call the callback with the updates", -> + // @callback.calledWith(null, @updates, null).should.equal true - describe "fillUserInfo", -> - describe "with valid users", -> - beforeEach (done) -> - {ObjectId} = require "mongojs" - @user_id_1 = ObjectId().toString() - @user_id_2 = ObjectId().toString() - @updates = [{ - meta: - user_id: @user_id_1 + describe("fillUserInfo", function() { + describe("with valid users", function() { + beforeEach(function(done) { + const {ObjectId} = require("mongojs"); + this.user_id_1 = ObjectId().toString(); + this.user_id_2 = ObjectId().toString(); + this.updates = [{ + meta: { + user_id: this.user_id_1 + }, op: "mock-op-1" }, { - meta: - user_id: @user_id_1 + meta: { + user_id: this.user_id_1 + }, op: "mock-op-2" }, { - meta: - user_id: @user_id_2 + meta: { + user_id: this.user_id_2 + }, op: "mock-op-3" - }] - @user_info = {} - @user_info[@user_id_1] = email: "user1@sharelatex.com" - @user_info[@user_id_2] = email: "user2@sharelatex.com" + }]; + this.user_info = {}; + this.user_info[this.user_id_1] = {email: "user1@sharelatex.com"}; + this.user_info[this.user_id_2] = {email: "user2@sharelatex.com"}; - @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - callback null, @user_info[user_id] - sinon.spy @WebApiManager, "getUserInfo" + this.WebApiManager.getUserInfo = (user_id, callback) => { + if (callback == null) { callback = function(error, userInfo) {}; } + return callback(null, this.user_info[user_id]); + }; + sinon.spy(this.WebApiManager, "getUserInfo"); - @UpdatesManager.fillUserInfo @updates, (error, @results) => - done() + return this.UpdatesManager.fillUserInfo(this.updates, (error, results) => { + this.results = results; + return done(); + }); + }); - it "should only call getUserInfo once for each user_id", -> - @WebApiManager.getUserInfo.calledTwice.should.equal true - @WebApiManager.getUserInfo - .calledWith(@user_id_1) - .should.equal true - @WebApiManager.getUserInfo - .calledWith(@user_id_2) - .should.equal true + it("should only call getUserInfo once for each user_id", function() { + this.WebApiManager.getUserInfo.calledTwice.should.equal(true); + this.WebApiManager.getUserInfo + .calledWith(this.user_id_1) + .should.equal(true); + return this.WebApiManager.getUserInfo + .calledWith(this.user_id_2) + .should.equal(true); + }); - it "should return the updates with the user info filled", -> - expect(@results).to.deep.equal [{ - meta: - user: + return it("should return the updates with the user info filled", function() { + return expect(this.results).to.deep.equal([{ + meta: { + user: { email: "user1@sharelatex.com" + } + }, op: "mock-op-1" }, { - meta: - user: + meta: { + user: { email: "user1@sharelatex.com" + } + }, op: "mock-op-2" }, { - meta: - user: + meta: { + user: { email: "user2@sharelatex.com" + } + }, op: "mock-op-3" - }] + }]); + }); + }); - describe "with invalid user ids", -> - beforeEach (done) -> - @updates = [{ - meta: + return describe("with invalid user ids", function() { + beforeEach(function(done) { + this.updates = [{ + meta: { user_id: null + }, op: "mock-op-1" }, { - meta: + meta: { user_id: "anonymous-user" + }, op: "mock-op-2" - }] - @WebApiManager.getUserInfo = (user_id, callback = (error, userInfo) ->) => - callback null, @user_info[user_id] - sinon.spy @WebApiManager, "getUserInfo" + }]; + this.WebApiManager.getUserInfo = (user_id, callback) => { + if (callback == null) { callback = function(error, userInfo) {}; } + return callback(null, this.user_info[user_id]); + }; + sinon.spy(this.WebApiManager, "getUserInfo"); - @UpdatesManager.fillUserInfo @updates, (error, @results) => - done() + return this.UpdatesManager.fillUserInfo(this.updates, (error, results) => { + this.results = results; + return done(); + }); + }); - it "should not call getUserInfo", -> - @WebApiManager.getUserInfo.called.should.equal false + it("should not call getUserInfo", function() { + return this.WebApiManager.getUserInfo.called.should.equal(false); + }); - it "should return the updates without the user info filled", -> - expect(@results).to.deep.equal [{ - meta: {} + return it("should return the updates without the user info filled", function() { + return expect(this.results).to.deep.equal([{ + meta: {}, op: "mock-op-1" }, { - meta: {} + meta: {}, op: "mock-op-2" - }] + }]); + }); + }); +}); - describe "_summarizeUpdates", -> - beforeEach -> - @now = Date.now() - @user_1 = { id: "mock-user-1" } - @user_2 = { id: "mock-user-2" } + return describe("_summarizeUpdates", function() { + beforeEach(function() { + this.now = Date.now(); + this.user_1 = { id: "mock-user-1" }; + return this.user_2 = { id: "mock-user-2" };}); - it "should concat updates that are close in time", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user_id: @user_1.id - start_ts: @now + 20 - end_ts: @now + 30 + it("should concat updates that are close in time", function() { + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-1", + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, v: 5 }, { - doc_id: "doc-id-1" - meta: - user_id: @user_2.id - start_ts: @now - end_ts: @now + 10 + doc_id: "doc-id-1", + meta: { + user_id: this.user_2.id, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 - }] + }]); - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + fromV: 4, toV: 5 - meta: - user_ids: [@user_1.id, @user_2.id] - start_ts: @now - end_ts: @now + 30 - }] + } + }, + meta: { + user_ids: [this.user_1.id, this.user_2.id], + start_ts: this.now, + end_ts: this.now + 30 + } + }]); + }); - it "should leave updates that are far apart in time", -> - oneDay = 1000 * 60 * 60 * 24 - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user_id: @user_2.id - start_ts: @now + oneDay - end_ts: @now + oneDay + 10 + it("should leave updates that are far apart in time", function() { + const oneDay = 1000 * 60 * 60 * 24; + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-1", + meta: { + user_id: this.user_2.id, + start_ts: this.now + oneDay, + end_ts: this.now + oneDay + 10 + }, v: 5 }, { - doc_id: "doc-id-1" - meta: - user_id: @user_1.id - start_ts: @now - end_ts: @now + 10 + doc_id: "doc-id-1", + meta: { + user_id: this.user_1.id, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 5 + }]); + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + fromV: 5, toV: 5 - meta: - user_ids: [@user_2.id] - start_ts: @now + oneDay - end_ts: @now + oneDay + 10 + } + }, + meta: { + user_ids: [this.user_2.id], + start_ts: this.now + oneDay, + end_ts: this.now + oneDay + 10 + } }, { - docs: - "doc-id-1": - fromV: 4 + docs: { + "doc-id-1": { + fromV: 4, toV: 4 - meta: - user_ids: [@user_1.id] - start_ts: @now - end_ts: @now + 10 - }] + } + }, + meta: { + user_ids: [this.user_1.id], + start_ts: this.now, + end_ts: this.now + 10 + } + }]); + }); - it "should concat onto existing summarized updates", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-2" - meta: - user_id: @user_1.id - start_ts: @now + 20 - end_ts: @now + 30 + it("should concat onto existing summarized updates", function() { + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-2", + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, v: 5 }, { - doc_id: "doc-id-2" - meta: - user_id: @user_2.id - start_ts: @now - end_ts: @now + 10 + doc_id: "doc-id-2", + meta: { + user_id: this.user_2.id, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 }], [{ - docs: - "doc-id-1": - fromV: 6 - toV: 8 - meta: - user_ids: [@user_1.id] - start_ts: @now + 40 - end_ts: @now + 50 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": + docs: { + "doc-id-1": { + fromV: 6, toV: 8 + } + }, + meta: { + user_ids: [this.user_1.id], + start_ts: this.now + 40, + end_ts: this.now + 50 + } + }]); + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + toV: 8, fromV: 6 - "doc-id-2": - toV: 5 + }, + "doc-id-2": { + toV: 5, fromV: 4 - meta: - user_ids: [@user_1.id, @user_2.id] - start_ts: @now - end_ts: @now + 50 - }] + } + }, + meta: { + user_ids: [this.user_1.id, this.user_2.id], + start_ts: this.now, + end_ts: this.now + 50 + } + }]); + }); - it "should include null user values", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user_id: @user_1.id - start_ts: @now + 20 - end_ts: @now + 30 + it("should include null user values", function() { + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-1", + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, v: 5 }, { - doc_id: "doc-id-1" - meta: - user_id: null - start_ts: @now - end_ts: @now + 10 + doc_id: "doc-id-1", + meta: { + user_id: null, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 + }]); + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + fromV: 4, toV: 5 - meta: - user_ids: [@user_1.id, null] - start_ts: @now - end_ts: @now + 30 - }] + } + }, + meta: { + user_ids: [this.user_1.id, null], + start_ts: this.now, + end_ts: this.now + 30 + } + }]); + }); - it "should include null user values, when the null is earlier in the updates list", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user_id: null - start_ts: @now - end_ts: @now + 10 + it("should include null user values, when the null is earlier in the updates list", function() { + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-1", + meta: { + user_id: null, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 }, { - doc_id: "doc-id-1" - meta: - user_id: @user_1.id - start_ts: @now + 20 - end_ts: @now + 30 + doc_id: "doc-id-1", + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, v: 5 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 + }]); + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + fromV: 4, toV: 5 - meta: - user_ids: [null, @user_1.id] - start_ts: @now - end_ts: @now + 30 - }] + } + }, + meta: { + user_ids: [null, this.user_1.id], + start_ts: this.now, + end_ts: this.now + 30 + } + }]); + }); - it "should roll several null user values into one", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - meta: - user_id: @user_1.id - start_ts: @now + 20 - end_ts: @now + 30 + it("should roll several null user values into one", function() { + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-1", + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, v: 5 }, { - doc_id: "doc-id-1" - meta: - user_id: null - start_ts: @now - end_ts: @now + 10 + doc_id: "doc-id-1", + meta: { + user_id: null, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 }, { - doc_id: "doc-id-1" - meta: - user_id: null - start_ts: @now + 2 - end_ts: @now + 4 + doc_id: "doc-id-1", + meta: { + user_id: null, + start_ts: this.now + 2, + end_ts: this.now + 4 + }, v: 4 - }] - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 4 + }]); + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + fromV: 4, toV: 5 - meta: - user_ids: [@user_1.id, null] - start_ts: @now - end_ts: @now + 30 - }] + } + }, + meta: { + user_ids: [this.user_1.id, null], + start_ts: this.now, + end_ts: this.now + 30 + } + }]); + }); - it "should split updates before a big delete", -> - result = @UpdatesManager._summarizeUpdates [{ - doc_id: "doc-id-1" - op: [{ d: "this is a long long long long long delete", p: 34 }] - meta: - user_id: @user_1.id - start_ts: @now + 20 - end_ts: @now + 30 + return it("should split updates before a big delete", function() { + const result = this.UpdatesManager._summarizeUpdates([{ + doc_id: "doc-id-1", + op: [{ d: "this is a long long long long long delete", p: 34 }], + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, v: 5 }, { - doc_id: "doc-id-1" - meta: - user_id: @user_2.id - start_ts: @now - end_ts: @now + 10 + doc_id: "doc-id-1", + meta: { + user_id: this.user_2.id, + start_ts: this.now, + end_ts: this.now + 10 + }, v: 4 - }] + }]); - expect(result).to.deep.equal [{ - docs: - "doc-id-1": - fromV: 5 + return expect(result).to.deep.equal([{ + docs: { + "doc-id-1": { + fromV: 5, toV: 5 - meta: - user_ids: [@user_1.id] - start_ts: @now + 20 - end_ts: @now + 30 + } + }, + meta: { + user_ids: [this.user_1.id], + start_ts: this.now + 20, + end_ts: this.now + 30 + } }, { - docs: - "doc-id-1": - fromV: 4 + docs: { + "doc-id-1": { + fromV: 4, toV: 4 - meta: - user_ids: [@user_2.id] - start_ts: @now - end_ts: @now + 10 - }] + } + }, + meta: { + user_ids: [this.user_2.id], + start_ts: this.now, + end_ts: this.now + 10 + } + }]); + }); +}); +}); diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js index 92c0fcd54a..80a7dc0132 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js @@ -1,123 +1,164 @@ -sinon = require('sinon') -chai = require('chai') -should = chai.should() -expect = chai.expect -modulePath = "../../../../app/js/WebApiManager.js" -SandboxedModule = require('sandboxed-module') +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require('sinon'); +const chai = require('chai'); +const should = chai.should(); +const { expect } = chai; +const modulePath = "../../../../app/js/WebApiManager.js"; +const SandboxedModule = require('sandboxed-module'); -describe "WebApiManager", -> - beforeEach -> - @WebApiManager = SandboxedModule.require modulePath, requires: - "requestretry": @request = {} - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } - 'settings-sharelatex': @settings = - apis: - web: - url: "http://example.com" - user: "sharelatex" +describe("WebApiManager", function() { + beforeEach(function() { + this.WebApiManager = SandboxedModule.require(modulePath, { requires: { + "requestretry": (this.request = {}), + "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), + 'settings-sharelatex': (this.settings = { + apis: { + web: { + url: "http://example.com", + user: "sharelatex", pass: "password" - @callback = sinon.stub() - @user_id = "mock-user-id" - @project_id = "mock-project-id" - @user_info = - email: "leo@sharelatex.com" - id: @user_id - first_name: "Leo" - last_nane: "Lion" + } + } + }) + } + } + ); + this.callback = sinon.stub(); + this.user_id = "mock-user-id"; + this.project_id = "mock-project-id"; + this.user_info = { + email: "leo@sharelatex.com", + id: this.user_id, + first_name: "Leo", + last_nane: "Lion", extra_param: "blah" - @project = - features: "mock-features" + }; + return this.project = + {features: "mock-features"}; + }); - describe "getUserInfo", -> - describe "successfully", -> - beforeEach -> - @body = JSON.stringify @user_info - @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) - @WebApiManager.getUserInfo @user_id, @callback + describe("getUserInfo", function() { + describe("successfully", function() { + beforeEach(function() { + this.body = JSON.stringify(this.user_info); + this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body); + return this.WebApiManager.getUserInfo(this.user_id, this.callback); + }); - it 'should get the user from the web api', -> - @request.get + it('should get the user from the web api', function() { + return this.request.get .calledWithMatch({ - url: "#{@settings.apis.web.url}/user/#{@user_id}/personal_info" - auth: - user: @settings.apis.web.user - pass: @settings.apis.web.pass + url: `${this.settings.apis.web.url}/user/${this.user_id}/personal_info`, + auth: { + user: this.settings.apis.web.user, + pass: this.settings.apis.web.pass, sendImmediately: true + } }) - .should.equal true + .should.equal(true); + }); - it "should call the callback with only the email, id and names", -> - @callback.calledWith(null, { - id: @user_id - email: @user_info.email - first_name: @user_info.first_name - last_name: @user_info.last_name - }).should.equal true + return it("should call the callback with only the email, id and names", function() { + return this.callback.calledWith(null, { + id: this.user_id, + email: this.user_info.email, + first_name: this.user_info.first_name, + last_name: this.user_info.last_name + }).should.equal(true); + }); + }); - describe "when the web API returns an error", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) - @WebApiManager.getUserInfo @user_id, @callback + describe("when the web API returns an error", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); + return this.WebApiManager.getUserInfo(this.user_id, this.callback); + }); - it "should return an error to the callback", -> - @callback.calledWith(@error).should.equal true + return it("should return an error to the callback", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); - describe "when the web returns a failure error code", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42}, "") - @WebApiManager.getUserInfo @user_id, @callback + describe("when the web returns a failure error code", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42}, ""); + return this.WebApiManager.getUserInfo(this.user_id, this.callback); + }); - it "should return the callback with an error", -> - @callback + return it("should return the callback with an error", function() { + return this.callback .calledWith(sinon.match.has('message', "web returned a non-success status code: 500 (attempts: 42)")) - .should.equal true + .should.equal(true); + }); + }); - describe "when the user cannot be found", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 404}, "nothing") - @WebApiManager.getUserInfo @user_id, @callback + return describe("when the user cannot be found", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 404}, "nothing"); + return this.WebApiManager.getUserInfo(this.user_id, this.callback); + }); - it "should return a null value", -> - @callback + return it("should return a null value", function() { + return this.callback .calledWith(null, null) - .should.equal true + .should.equal(true); + }); + }); + }); - describe "getProjectDetails", -> - describe "successfully", -> - beforeEach -> - @body = JSON.stringify @project - @request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, @body) - @WebApiManager.getProjectDetails @project_id, @callback + return describe("getProjectDetails", function() { + describe("successfully", function() { + beforeEach(function() { + this.body = JSON.stringify(this.project); + this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body); + return this.WebApiManager.getProjectDetails(this.project_id, this.callback); + }); - it 'should get the project from the web api', -> - @request.get + it('should get the project from the web api', function() { + return this.request.get .calledWithMatch({ - url: "#{@settings.apis.web.url}/project/#{@project_id}/details" - auth: - user: @settings.apis.web.user - pass: @settings.apis.web.pass + url: `${this.settings.apis.web.url}/project/${this.project_id}/details`, + auth: { + user: this.settings.apis.web.user, + pass: this.settings.apis.web.pass, sendImmediately: true + } }) - .should.equal true + .should.equal(true); + }); - it "should call the callback with the project", -> - @callback.calledWith(null, @project).should.equal true + return it("should call the callback with the project", function() { + return this.callback.calledWith(null, this.project).should.equal(true); + }); + }); - describe "when the web API returns an error", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, @error = new Error("something went wrong"), null, null) - @WebApiManager.getProjectDetails @project_id, @callback + describe("when the web API returns an error", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); + return this.WebApiManager.getProjectDetails(this.project_id, this.callback); + }); - it "should return an error to the callback", -> - @callback.calledWith(@error).should.equal true + return it("should return an error to the callback", function() { + return this.callback.calledWith(this.error).should.equal(true); + }); + }); - describe "when the web returns a failure error code", -> - beforeEach -> - @request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42 }, "") - @WebApiManager.getProjectDetails @project_id, @callback + return describe("when the web returns a failure error code", function() { + beforeEach(function() { + this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42 }, ""); + return this.WebApiManager.getProjectDetails(this.project_id, this.callback); + }); - it "should return the callback with an error", -> - @callback + return it("should return the callback with an error", function() { + return this.callback .calledWith(sinon.match.has('message', "web returned a non-success status code: 500 (attempts: 42)")) - .should.equal true + .should.equal(true); + }); + }); + }); +}); From a35c69c0c8c575db0b2ee9ff93bd69027cea7cfa Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:35:01 +0100 Subject: [PATCH 472/549] decaffeinate: Run post-processing cleanups on DiffGeneratorTests.coffee and 13 other files --- .../DiffGenerator/DiffGeneratorTests.js | 71 +++++++++---------- .../coffee/DiffManager/DiffManagerTests.js | 7 ++ .../test/unit/coffee/DocArchive/MongoAWS.js | 6 ++ .../DocumentUpdaterManagerTests.js | 6 ++ .../HttpController/HttpControllerTests.js | 6 ++ .../coffee/LockManager/LockManagerTests.js | 15 +++- .../coffee/MongoManager/MongoManagerTests.js | 8 ++- .../coffee/PackManager/PackManagerTests.js | 20 ++++-- .../coffee/RedisManager/RedisManagerTests.js | 7 ++ .../RestoreManager/RestoreManagerTests.js | 6 ++ .../UpdateCompressor/UpdateCompressorTests.js | 30 ++++---- .../UpdateTrimmer/UpdateTrimmerTests.js | 8 ++- .../UpdatesManager/UpdatesManagerTests.js | 8 +++ .../WebApiManager/WebApiManagerTests.js | 6 ++ 14 files changed, 139 insertions(+), 65 deletions(-) diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js index 73379306fb..7876a22254 100644 --- a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js +++ b/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -25,52 +31,46 @@ describe("DiffGenerator", function() { };}); describe("rewindOp", function() { - describe("rewinding an insert", () => - it("should undo the insert", function() { + describe("rewinding an insert", function() { return it("should undo the insert", function() { const content = "hello world"; const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, i: "wo" }); return rewoundContent.should.equal("hello rld"); - }) + }); } ); - describe("rewinding a delete", () => - it("should undo the delete", function() { + describe("rewinding a delete", function() { return it("should undo the delete", function() { const content = "hello rld"; const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, d: "wo" }); return rewoundContent.should.equal("hello world"); - }) + }); } ); - describe("with an inconsistent update", () => - it("should throw an error", function() { + describe("with an inconsistent update", function() { return it("should throw an error", function() { const content = "hello world"; return expect( () => { return this.DiffGenerator.rewindOp(content, { p: 6, i: "foo" }); }).to.throw(this.DiffGenerator.ConsistencyError); - }) + }); } ); - return describe("with an update which is beyond the length of the content", () => - it("should undo the insert as if it were at the end of the content", function() { + return describe("with an update which is beyond the length of the content", function() { return it("should undo the insert as if it were at the end of the content", function() { const content = "foobar"; const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 4, i: "bar" }); return rewoundContent.should.equal("foo"); - }) + }); } ); }); - describe("rewindUpdate", () => - it("should rewind ops in reverse", function() { + describe("rewindUpdate", function() { return it("should rewind ops in reverse", function() { const content = "aaabbbccc"; const update = {op: [{ p: 3, i: "bbb" }, { p: 6, i: "ccc" }]}; const rewoundContent = this.DiffGenerator.rewindUpdate(content, update); return rewoundContent.should.equal("aaa"); - }) + }); } ); - describe("rewindUpdates", () => - it("should rewind updates in reverse", function() { + describe("rewindUpdates", function() { return it("should rewind updates in reverse", function() { const content = "aaabbbccc"; const updates = [ { op: [{ p: 3, i: "bbb" }] }, @@ -78,7 +78,7 @@ describe("DiffGenerator", function() { ]; const rewoundContent = this.DiffGenerator.rewindUpdates(content, updates); return rewoundContent.should.equal("aaa"); - }) + }); } ); describe("buildDiff", function() { @@ -122,8 +122,7 @@ describe("DiffGenerator", function() { }); describe("compressDiff", function() { - describe("with adjacent inserts with the same user_id", () => - it("should create one update with combined meta data and min/max timestamps", function() { + describe("with adjacent inserts with the same user_id", function() { return it("should create one update with combined meta data and min/max timestamps", function() { const diff = this.DiffGenerator.compressDiff([ { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }} @@ -131,22 +130,20 @@ describe("DiffGenerator", function() { return expect(diff).to.deep.equal([ { i: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }} ]); - }) + }); } ); - describe("with adjacent inserts with different user_ids", () => - it("should leave the inserts unchanged", function() { + describe("with adjacent inserts with different user_ids", function() { return it("should leave the inserts unchanged", function() { const input = [ { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }} ]; const output = this.DiffGenerator.compressDiff(input); return expect(output).to.deep.equal(input); - }) + }); } ); - describe("with adjacent deletes with the same user_id", () => - it("should create one update with combined meta data and min/max timestamps", function() { + describe("with adjacent deletes with the same user_id", function() { return it("should create one update with combined meta data and min/max timestamps", function() { const diff = this.DiffGenerator.compressDiff([ { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }} @@ -154,18 +151,17 @@ describe("DiffGenerator", function() { return expect(diff).to.deep.equal([ { d: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }} ]); - }) + }); } ); - return describe("with adjacent deletes with different user_ids", () => - it("should leave the deletes unchanged", function() { + return describe("with adjacent deletes with different user_ids", function() { return it("should leave the deletes unchanged", function() { const input = [ { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }} ]; const output = this.DiffGenerator.compressDiff(input); return expect(output).to.deep.equal(input); - }) + }); } ); }); @@ -331,8 +327,7 @@ describe("DiffGenerator", function() { }); }); - describe("deleting over existing deletes", () => - it("should delete across multiple (u)changed and (d)deleted text parts", function() { + describe("deleting over existing deletes", function() { return it("should delete across multiple (u)changed and (d)deleted text parts", function() { const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foo" }, { d: "baz", meta: this.meta }, { u: "bar" } ], { op: [{ p: 2, d: "ob" }], meta: this.meta } @@ -344,7 +339,7 @@ describe("DiffGenerator", function() { { d: "b", meta: this.meta }, { u: "ar" } ]); - }) + }); } ); describe("deleting when the text doesn't match", function() { @@ -376,8 +371,7 @@ describe("DiffGenerator", function() { }); }); - describe("when the last update in the existing diff is a delete", () => - it("should insert the new update before the delete", function() { + describe("when the last update in the existing diff is a delete", function() { return it("should insert the new update before the delete", function() { const diff = this.DiffGenerator.applyUpdateToDiff( [ { u: "foo" }, { d: "bar", meta: this.meta } ], { op: [{ p: 3, i: "baz" }], meta: this.meta } @@ -387,11 +381,10 @@ describe("DiffGenerator", function() { { i: "baz", meta: this.meta }, { d: "bar", meta: this.meta } ]); - }) + }); } ); - return describe("when the only update in the existing diff is a delete", () => - it("should insert the new update after the delete", function() { + return describe("when the only update in the existing diff is a delete", function() { return it("should insert the new update after the delete", function() { const diff = this.DiffGenerator.applyUpdateToDiff( [ { d: "bar", meta: this.meta } ], { op: [{ p: 0, i: "baz" }], meta: this.meta } @@ -400,7 +393,7 @@ describe("DiffGenerator", function() { { d: "bar", meta: this.meta }, { i: "baz", meta: this.meta } ]); - }) + }); } ); }); }); diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js index 18e048ee17..cc8f29c6f6 100644 --- a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js index 1da7f37e49..e3d82e6370 100644 --- a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js @@ -1,3 +1,9 @@ +/* eslint-disable + handle-callback-err, + no-return-assign, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index ca15a8867d..9b4ce3cc11 100644 --- a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js index 6acb0ae3eb..21a74a9661 100644 --- a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js index 7cd7461810..2ad9072133 100644 --- a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js @@ -1,3 +1,13 @@ +/* eslint-disable + camelcase, + handle-callback-err, + mocha/no-nested-tests, + no-return-assign, + no-undef, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -95,8 +105,7 @@ describe("LockManager", function() { }); }); - describe("deleteLock", () => - beforeEach(function() { + describe("deleteLock", function() { return beforeEach(function() { beforeEach(function() { this.rclient.del = sinon.stub().callsArg(1); return this.LockManager.deleteLock(this.key, this.callback); @@ -111,7 +120,7 @@ describe("LockManager", function() { return it("should call the callback", function() { return this.callback.called.should.equal(true); }); - }) + }); } ); describe("getLock", function() { diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js index 9f787a56a4..a72ec1dec8 100644 --- a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -28,7 +34,7 @@ describe("MongoManager", function() { return this.project_id = ObjectId().toString(); }); - afterEach(() => tk.reset()); + afterEach(function() { return tk.reset(); }); describe("getLastCompressedUpdate", function() { beforeEach(function() { diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js index 58bbc0312e..dda7a5e6bb 100644 --- a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js @@ -1,3 +1,10 @@ +/* eslint-disable + mocha/no-identical-title, + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -38,7 +45,7 @@ describe("PackManager", function() { return this.PackManager.MAX_COUNT = 512; }); - afterEach(() => tk.reset()); + afterEach(function() { return tk.reset(); }); describe("insertCompressedUpdates", function() { beforeEach(function() { @@ -195,8 +202,7 @@ describe("PackManager", function() { }); }); - describe("flushCompressedUpdates", () => - describe("when there is no previous update", function() { + describe("flushCompressedUpdates", function() { return describe("when there is no previous update", function() { beforeEach(function() { return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, true, this.callback); }); @@ -223,7 +229,7 @@ describe("PackManager", function() { return this.callback.called.should.equal(true); }); }); - }) + }); } ); describe("when there is a recent previous update in mongo that expires", function() { @@ -454,9 +460,9 @@ describe("PackManager", function() { function __range__(left, right, inclusive) { - let range = []; - let ascending = left < right; - let end = !inclusive ? right : ascending ? right + 1 : right - 1; + const range = []; + const ascending = left < right; + const end = !inclusive ? right : ascending ? right + 1 : right - 1; for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { range.push(i); } diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js index 502762810b..5637ed6135 100644 --- a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from diff --git a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js index 4396f92b9c..dfa2c07f05 100644 --- a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js +++ b/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js index 780ddd483c..1a12ec78d2 100644 --- a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js +++ b/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -494,8 +500,7 @@ describe("UpdateCompressor", function() { }); }); - describe("noop - insert", () => - it("should leave them untouched", function() { + describe("noop - insert", function() { return it("should leave them untouched", function() { return expect(this.UpdateCompressor.compressUpdates([{ op: this.UpdateCompressor.NOOP, meta: { ts: this.ts1, user_id: this.user_id @@ -518,11 +523,10 @@ describe("UpdateCompressor", function() { }, v: 43 }]); - }) + }); } ); - return describe("noop - delete", () => - it("should leave them untouched", function() { + return describe("noop - delete", function() { return it("should leave them untouched", function() { return expect(this.UpdateCompressor.compressUpdates([{ op: this.UpdateCompressor.NOOP, meta: { ts: this.ts1, user_id: this.user_id @@ -545,13 +549,11 @@ describe("UpdateCompressor", function() { }, v: 43 }]); - }) + }); } ); }); - return describe("compressRawUpdates", () => - describe("merging in-place with an array op", () => - it("should not change the existing last updates", function() { + return describe("compressRawUpdates", function() { return describe("merging in-place with an array op", function() { return it("should not change the existing last updates", function() { return expect(this.UpdateCompressor.compressRawUpdates({ op: [ {"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ], meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id @@ -574,15 +576,15 @@ describe("UpdateCompressor", function() { }, v: 43 }]); - }) - ) + }); } + ); } ); }); function __range__(left, right, inclusive) { - let range = []; - let ascending = left < right; - let end = !inclusive ? right : ascending ? right + 1 : right - 1; + const range = []; + const ascending = left < right; + const end = !inclusive ? right : ascending ? right + 1 : right - 1; for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { range.push(i); } diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js index 5976fd3335..83d1632ada 100644 --- a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js +++ b/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -27,7 +33,7 @@ describe("UpdateTrimmer", function() { return this.project_id = "mock-project-id"; }); - afterEach(() => tk.reset()); + afterEach(function() { return tk.reset(); }); return describe("shouldTrimUpdates", function() { beforeEach(function() { diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js index 53b9fefe87..2ac748c8f6 100644 --- a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js @@ -1,3 +1,11 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js index 80a7dc0132..984493afc4 100644 --- a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + no-return-assign, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns From 201bfbbf32b4786ee8f3580e19605aa3a23198f5 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:35:11 +0100 Subject: [PATCH 473/549] decaffeinate: rename test/unit/coffee to test/unit/js --- .../test/unit/{coffee => js}/DiffGenerator/DiffGeneratorTests.js | 0 .../test/unit/{coffee => js}/DiffManager/DiffManagerTests.js | 0 .../track-changes/test/unit/{coffee => js}/DocArchive/MongoAWS.js | 0 .../DocumentUpdaterManager/DocumentUpdaterManagerTests.js | 0 .../unit/{coffee => js}/HttpController/HttpControllerTests.js | 0 .../test/unit/{coffee => js}/LockManager/LockManagerTests.js | 0 .../test/unit/{coffee => js}/MongoManager/MongoManagerTests.js | 0 .../test/unit/{coffee => js}/PackManager/PackManagerTests.js | 0 .../test/unit/{coffee => js}/RedisManager/RedisManagerTests.js | 0 .../unit/{coffee => js}/RestoreManager/RestoreManagerTests.js | 0 .../unit/{coffee => js}/UpdateCompressor/UpdateCompressorTests.js | 0 .../test/unit/{coffee => js}/UpdateTrimmer/UpdateTrimmerTests.js | 0 .../unit/{coffee => js}/UpdatesManager/UpdatesManagerTests.js | 0 .../test/unit/{coffee => js}/WebApiManager/WebApiManagerTests.js | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/test/unit/{coffee => js}/DiffGenerator/DiffGeneratorTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/DiffManager/DiffManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/DocArchive/MongoAWS.js (100%) rename services/track-changes/test/unit/{coffee => js}/DocumentUpdaterManager/DocumentUpdaterManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/HttpController/HttpControllerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/LockManager/LockManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/MongoManager/MongoManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/PackManager/PackManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/RedisManager/RedisManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/RestoreManager/RestoreManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/UpdateCompressor/UpdateCompressorTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/UpdateTrimmer/UpdateTrimmerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/UpdatesManager/UpdatesManagerTests.js (100%) rename services/track-changes/test/unit/{coffee => js}/WebApiManager/WebApiManagerTests.js (100%) diff --git a/services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/DiffGenerator/DiffGeneratorTests.js rename to services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js diff --git a/services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.js rename to services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js diff --git a/services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js similarity index 100% rename from services/track-changes/test/unit/coffee/DocArchive/MongoAWS.js rename to services/track-changes/test/unit/js/DocArchive/MongoAWS.js diff --git a/services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/DocumentUpdaterManager/DocumentUpdaterManagerTests.js rename to services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js diff --git a/services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/HttpController/HttpControllerTests.js rename to services/track-changes/test/unit/js/HttpController/HttpControllerTests.js diff --git a/services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/LockManager/LockManagerTests.js rename to services/track-changes/test/unit/js/LockManager/LockManagerTests.js diff --git a/services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.js rename to services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js diff --git a/services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/PackManager/PackManagerTests.js rename to services/track-changes/test/unit/js/PackManager/PackManagerTests.js diff --git a/services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/RedisManager/RedisManagerTests.js rename to services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js diff --git a/services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/RestoreManager/RestoreManagerTests.js rename to services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js diff --git a/services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/UpdateCompressor/UpdateCompressorTests.js rename to services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js diff --git a/services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/UpdateTrimmer/UpdateTrimmerTests.js rename to services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js diff --git a/services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/UpdatesManager/UpdatesManagerTests.js rename to services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js diff --git a/services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js similarity index 100% rename from services/track-changes/test/unit/coffee/WebApiManager/WebApiManagerTests.js rename to services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js From 77417305746a7787d7eb27b4c61f9614b4d6a02a Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:35:16 +0100 Subject: [PATCH 474/549] prettier: convert test/unit decaffeinated files to Prettier format --- .../js/DiffGenerator/DiffGeneratorTests.js | 770 +++--- .../unit/js/DiffManager/DiffManagerTests.js | 680 +++-- .../test/unit/js/DocArchive/MongoAWS.js | 170 +- .../DocumentUpdaterManagerTests.js | 286 +- .../js/HttpController/HttpControllerTests.js | 328 +-- .../unit/js/LockManager/LockManagerTests.js | 494 ++-- .../unit/js/MongoManager/MongoManagerTests.js | 374 +-- .../unit/js/PackManager/PackManagerTests.js | 1148 ++++---- .../unit/js/RedisManager/RedisManagerTests.js | 240 +- .../js/RestoreManager/RestoreManagerTests.js | 97 +- .../UpdateCompressor/UpdateCompressorTests.js | 1370 ++++++---- .../js/UpdateTrimmer/UpdateTrimmerTests.js | 281 +- .../js/UpdatesManager/UpdatesManagerTests.js | 2326 ++++++++++------- .../js/WebApiManager/WebApiManagerTests.js | 328 ++- 14 files changed, 5100 insertions(+), 3792 deletions(-) diff --git a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js index 7876a22254..b84775ddea 100644 --- a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js +++ b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js @@ -10,393 +10,453 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/DiffGenerator.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/DiffGenerator.js' +const SandboxedModule = require('sandboxed-module') -describe("DiffGenerator", function() { - beforeEach(function() { - this.DiffGenerator = SandboxedModule.require(modulePath, { requires: { - "logger-sharelatex": { warn: sinon.stub() } - } - }); - this.ts = Date.now(); - this.user_id = "mock-user-id"; - this.user_id_2 = "mock-user-id-2"; - return this.meta = { - start_ts: this.ts, end_ts: this.ts, user_id: this.user_id - };}); +describe('DiffGenerator', function() { + beforeEach(function() { + this.DiffGenerator = SandboxedModule.require(modulePath, { + requires: { + 'logger-sharelatex': { warn: sinon.stub() } + } + }) + this.ts = Date.now() + this.user_id = 'mock-user-id' + this.user_id_2 = 'mock-user-id-2' + return (this.meta = { + start_ts: this.ts, + end_ts: this.ts, + user_id: this.user_id + }) + }) - describe("rewindOp", function() { - describe("rewinding an insert", function() { return it("should undo the insert", function() { - const content = "hello world"; - const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, i: "wo" }); - return rewoundContent.should.equal("hello rld"); - }); } - ); + describe('rewindOp', function() { + describe('rewinding an insert', function() { + return it('should undo the insert', function() { + const content = 'hello world' + const rewoundContent = this.DiffGenerator.rewindOp(content, { + p: 6, + i: 'wo' + }) + return rewoundContent.should.equal('hello rld') + }) + }) - describe("rewinding a delete", function() { return it("should undo the delete", function() { - const content = "hello rld"; - const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, d: "wo" }); - return rewoundContent.should.equal("hello world"); - }); } - ); + describe('rewinding a delete', function() { + return it('should undo the delete', function() { + const content = 'hello rld' + const rewoundContent = this.DiffGenerator.rewindOp(content, { + p: 6, + d: 'wo' + }) + return rewoundContent.should.equal('hello world') + }) + }) - describe("with an inconsistent update", function() { return it("should throw an error", function() { - const content = "hello world"; - return expect( () => { - return this.DiffGenerator.rewindOp(content, { p: 6, i: "foo" }); - }).to.throw(this.DiffGenerator.ConsistencyError); - }); } - ); - - return describe("with an update which is beyond the length of the content", function() { return it("should undo the insert as if it were at the end of the content", function() { - const content = "foobar"; - const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 4, i: "bar" }); - return rewoundContent.should.equal("foo"); - }); } - ); - }); + describe('with an inconsistent update', function() { + return it('should throw an error', function() { + const content = 'hello world' + return expect(() => { + return this.DiffGenerator.rewindOp(content, { p: 6, i: 'foo' }) + }).to.throw(this.DiffGenerator.ConsistencyError) + }) + }) - describe("rewindUpdate", function() { return it("should rewind ops in reverse", function() { - const content = "aaabbbccc"; - const update = - {op: [{ p: 3, i: "bbb" }, { p: 6, i: "ccc" }]}; - const rewoundContent = this.DiffGenerator.rewindUpdate(content, update); - return rewoundContent.should.equal("aaa"); - }); } - ); + return describe('with an update which is beyond the length of the content', function() { + return it('should undo the insert as if it were at the end of the content', function() { + const content = 'foobar' + const rewoundContent = this.DiffGenerator.rewindOp(content, { + p: 4, + i: 'bar' + }) + return rewoundContent.should.equal('foo') + }) + }) + }) - describe("rewindUpdates", function() { return it("should rewind updates in reverse", function() { - const content = "aaabbbccc"; - const updates = [ - { op: [{ p: 3, i: "bbb" }] }, - { op: [{ p: 6, i: "ccc" }] } - ]; - const rewoundContent = this.DiffGenerator.rewindUpdates(content, updates); - return rewoundContent.should.equal("aaa"); - }); } - ); + describe('rewindUpdate', function() { + return it('should rewind ops in reverse', function() { + const content = 'aaabbbccc' + const update = { + op: [ + { p: 3, i: 'bbb' }, + { p: 6, i: 'ccc' } + ] + } + const rewoundContent = this.DiffGenerator.rewindUpdate(content, update) + return rewoundContent.should.equal('aaa') + }) + }) - describe("buildDiff", function() { - beforeEach(function() { - this.diff = [ {u: "mock-diff"} ]; - this.content = "Hello world"; - this.updates = [ - { i: "mock-update-1" }, - { i: "mock-update-2" }, - { i: "mock-update-3" } - ]; - this.DiffGenerator.applyUpdateToDiff = sinon.stub().returns(this.diff); - this.DiffGenerator.compressDiff = sinon.stub().returns(this.diff); - return this.result = this.DiffGenerator.buildDiff(this.content, this.updates); - }); + describe('rewindUpdates', function() { + return it('should rewind updates in reverse', function() { + const content = 'aaabbbccc' + const updates = [ + { op: [{ p: 3, i: 'bbb' }] }, + { op: [{ p: 6, i: 'ccc' }] } + ] + const rewoundContent = this.DiffGenerator.rewindUpdates(content, updates) + return rewoundContent.should.equal('aaa') + }) + }) - it("should return the diff", function() { - return this.result.should.deep.equal(this.diff); - }); + describe('buildDiff', function() { + beforeEach(function() { + this.diff = [{ u: 'mock-diff' }] + this.content = 'Hello world' + this.updates = [ + { i: 'mock-update-1' }, + { i: 'mock-update-2' }, + { i: 'mock-update-3' } + ] + this.DiffGenerator.applyUpdateToDiff = sinon.stub().returns(this.diff) + this.DiffGenerator.compressDiff = sinon.stub().returns(this.diff) + return (this.result = this.DiffGenerator.buildDiff( + this.content, + this.updates + )) + }) - it("should build the content into an initial diff", function() { - return this.DiffGenerator.applyUpdateToDiff - .calledWith([{ - u: this.content - }], this.updates[0]) - .should.equal(true); - }); + it('should return the diff', function() { + return this.result.should.deep.equal(this.diff) + }) - it("should apply each update", function() { - return Array.from(this.updates).map((update) => - this.DiffGenerator.applyUpdateToDiff - .calledWith(sinon.match.any, update) - .should.equal(true)); - }); + it('should build the content into an initial diff', function() { + return this.DiffGenerator.applyUpdateToDiff + .calledWith( + [ + { + u: this.content + } + ], + this.updates[0] + ) + .should.equal(true) + }) - return it("should compress the diff", function() { - return this.DiffGenerator.compressDiff - .calledWith(this.diff) - .should.equal(true); - }); - }); + it('should apply each update', function() { + return Array.from(this.updates).map(update => + this.DiffGenerator.applyUpdateToDiff + .calledWith(sinon.match.any, update) + .should.equal(true) + ) + }) - describe("compressDiff", function() { - describe("with adjacent inserts with the same user_id", function() { return it("should create one update with combined meta data and min/max timestamps", function() { - const diff = this.DiffGenerator.compressDiff([ - { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, - { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }} - ]); - return expect(diff).to.deep.equal([ - { i: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }} - ]); - }); } - ); + return it('should compress the diff', function() { + return this.DiffGenerator.compressDiff + .calledWith(this.diff) + .should.equal(true) + }) + }) - describe("with adjacent inserts with different user_ids", function() { return it("should leave the inserts unchanged", function() { - const input = [ - { i: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, - { i: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }} - ]; - const output = this.DiffGenerator.compressDiff(input); - return expect(output).to.deep.equal(input); - }); } - ); + describe('compressDiff', function() { + describe('with adjacent inserts with the same user_id', function() { + return it('should create one update with combined meta data and min/max timestamps', function() { + const diff = this.DiffGenerator.compressDiff([ + { + i: 'foo', + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + }, + { + i: 'bar', + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } } + } + ]) + return expect(diff).to.deep.equal([ + { + i: 'foobar', + meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } } + } + ]) + }) + }) - describe("with adjacent deletes with the same user_id", function() { return it("should create one update with combined meta data and min/max timestamps", function() { - const diff = this.DiffGenerator.compressDiff([ - { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, - { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }} - ]); - return expect(diff).to.deep.equal([ - { d: "foobar", meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }} - ]); - }); } - ); + describe('with adjacent inserts with different user_ids', function() { + return it('should leave the inserts unchanged', function() { + const input = [ + { + i: 'foo', + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + }, + { + i: 'bar', + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } } + } + ] + const output = this.DiffGenerator.compressDiff(input) + return expect(output).to.deep.equal(input) + }) + }) - return describe("with adjacent deletes with different user_ids", function() { return it("should leave the deletes unchanged", function() { - const input = [ - { d: "foo", meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }}, - { d: "bar", meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }} - ]; - const output = this.DiffGenerator.compressDiff(input); - return expect(output).to.deep.equal(input); - }); } - ); - }); + describe('with adjacent deletes with the same user_id', function() { + return it('should create one update with combined meta data and min/max timestamps', function() { + const diff = this.DiffGenerator.compressDiff([ + { + d: 'foo', + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + }, + { + d: 'bar', + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } } + } + ]) + return expect(diff).to.deep.equal([ + { + d: 'foobar', + meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } } + } + ]) + }) + }) - return describe("applyUpdateToDiff", function() { - describe("an insert", function() { - it("should insert into the middle of (u)nchanged text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobar" } ], - { op: [{ p: 3, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "foo" }, - { i: "baz", meta: this.meta }, - { u: "bar" } - ]); - }); + return describe('with adjacent deletes with different user_ids', function() { + return it('should leave the deletes unchanged', function() { + const input = [ + { + d: 'foo', + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + }, + { + d: 'bar', + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } } + } + ] + const output = this.DiffGenerator.compressDiff(input) + return expect(output).to.deep.equal(input) + }) + }) + }) - it("should insert into the start of (u)changed text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobar" } ], - { op: [{ p: 0, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { i: "baz", meta: this.meta }, - { u: "foobar" } - ]); - }); + return describe('applyUpdateToDiff', function() { + describe('an insert', function() { + it('should insert into the middle of (u)nchanged text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { + op: [{ p: 3, i: 'baz' }], + meta: this.meta + }) + return expect(diff).to.deep.equal([ + { u: 'foo' }, + { i: 'baz', meta: this.meta }, + { u: 'bar' } + ]) + }) - it("should insert into the end of (u)changed text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobar" } ], - { op: [{ p: 6, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "foobar" }, - { i: "baz", meta: this.meta } - ]); - }); + it('should insert into the start of (u)changed text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { + op: [{ p: 0, i: 'baz' }], + meta: this.meta + }) + return expect(diff).to.deep.equal([ + { i: 'baz', meta: this.meta }, + { u: 'foobar' } + ]) + }) - it("should insert into the middle of (i)inserted text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { i: "foobar", meta: this.meta } ], - { op: [{ p: 3, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { i: "foo", meta: this.meta }, - { i: "baz", meta: this.meta }, - { i: "bar", meta: this.meta } - ]); - }); + it('should insert into the end of (u)changed text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { + op: [{ p: 6, i: 'baz' }], + meta: this.meta + }) + return expect(diff).to.deep.equal([ + { u: 'foobar' }, + { i: 'baz', meta: this.meta } + ]) + }) - return it("should not count deletes in the running length total", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ - { d: "deleted", meta: this.meta }, - { u: "foobar" } - ], - { op: [{ p: 3, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { d: "deleted", meta: this.meta }, - { u: "foo" }, - { i: "baz", meta: this.meta }, - { u: "bar" } - ]); - }); - }); + it('should insert into the middle of (i)inserted text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ i: 'foobar', meta: this.meta }], + { op: [{ p: 3, i: 'baz' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { i: 'foo', meta: this.meta }, + { i: 'baz', meta: this.meta }, + { i: 'bar', meta: this.meta } + ]) + }) - return describe("a delete", function() { - describe("deleting unchanged text", function() { - it("should delete from the middle of (u)nchanged text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobazbar" } ], - { op: [{ p: 3, d: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "foo" }, - { d: "baz", meta: this.meta }, - { u: "bar" } - ]); - }); + return it('should not count deletes in the running length total', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ d: 'deleted', meta: this.meta }, { u: 'foobar' }], + { op: [{ p: 3, i: 'baz' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { d: 'deleted', meta: this.meta }, + { u: 'foo' }, + { i: 'baz', meta: this.meta }, + { u: 'bar' } + ]) + }) + }) - it("should delete from the start of (u)nchanged text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobazbar" } ], - { op: [{ p: 0, d: "foo" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { d: "foo", meta: this.meta }, - { u: "bazbar" } - ]); - }); + return describe('a delete', function() { + describe('deleting unchanged text', function() { + it('should delete from the middle of (u)nchanged text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foobazbar' }], + { op: [{ p: 3, d: 'baz' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { u: 'foo' }, + { d: 'baz', meta: this.meta }, + { u: 'bar' } + ]) + }) - it("should delete from the end of (u)nchanged text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobazbar" } ], - { op: [{ p: 6, d: "bar" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "foobaz" }, - { d: "bar", meta: this.meta } - ]); - }); + it('should delete from the start of (u)nchanged text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foobazbar' }], + { op: [{ p: 0, d: 'foo' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { d: 'foo', meta: this.meta }, + { u: 'bazbar' } + ]) + }) - return it("should delete across multiple (u)changed text parts", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { u: "baz" }, { u: "bar" } ], - { op: [{ p: 2, d: "obazb" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "fo" }, - { d: "o", meta: this.meta }, - { d: "baz", meta: this.meta }, - { d: "b", meta: this.meta }, - { u: "ar" } - ]); - }); - }); + it('should delete from the end of (u)nchanged text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foobazbar' }], + { op: [{ p: 6, d: 'bar' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { u: 'foobaz' }, + { d: 'bar', meta: this.meta } + ]) + }) - describe("deleting inserts", function() { - it("should delete from the middle of (i)nserted text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { i: "foobazbar", meta: this.meta } ], - { op: [{ p: 3, d: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { i: "foo", meta: this.meta }, - { i: "bar", meta: this.meta } - ]); - }); + return it('should delete across multiple (u)changed text parts', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foo' }, { u: 'baz' }, { u: 'bar' }], + { op: [{ p: 2, d: 'obazb' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { u: 'fo' }, + { d: 'o', meta: this.meta }, + { d: 'baz', meta: this.meta }, + { d: 'b', meta: this.meta }, + { u: 'ar' } + ]) + }) + }) - it("should delete from the start of (u)nchanged text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { i: "foobazbar", meta: this.meta } ], - { op: [{ p: 0, d: "foo" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { i: "bazbar", meta: this.meta } - ]); - }); + describe('deleting inserts', function() { + it('should delete from the middle of (i)nserted text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ i: 'foobazbar', meta: this.meta }], + { op: [{ p: 3, d: 'baz' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { i: 'foo', meta: this.meta }, + { i: 'bar', meta: this.meta } + ]) + }) - it("should delete from the end of (u)nchanged text", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { i: "foobazbar", meta: this.meta } ], - { op: [{ p: 6, d: "bar" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { i: "foobaz", meta: this.meta } - ]); - }); + it('should delete from the start of (u)nchanged text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ i: 'foobazbar', meta: this.meta }], + { op: [{ p: 0, d: 'foo' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([{ i: 'bazbar', meta: this.meta }]) + }) - return it("should delete across multiple (u)changed and (i)nserted text parts", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { i: "baz", meta: this.meta }, { u: "bar" } ], - { op: [{ p: 2, d: "obazb" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "fo" }, - { d: "o", meta: this.meta }, - { d: "b", meta: this.meta }, - { u: "ar" } - ]); - }); - }); + it('should delete from the end of (u)nchanged text', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ i: 'foobazbar', meta: this.meta }], + { op: [{ p: 6, d: 'bar' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([{ i: 'foobaz', meta: this.meta }]) + }) - describe("deleting over existing deletes", function() { return it("should delete across multiple (u)changed and (d)deleted text parts", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { d: "baz", meta: this.meta }, { u: "bar" } ], - { op: [{ p: 2, d: "ob" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "fo" }, - { d: "o", meta: this.meta }, - { d: "baz", meta: this.meta }, - { d: "b", meta: this.meta }, - { u: "ar" } - ]); - }); } - ); + return it('should delete across multiple (u)changed and (i)nserted text parts', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foo' }, { i: 'baz', meta: this.meta }, { u: 'bar' }], + { op: [{ p: 2, d: 'obazb' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { u: 'fo' }, + { d: 'o', meta: this.meta }, + { d: 'b', meta: this.meta }, + { u: 'ar' } + ]) + }) + }) - describe("deleting when the text doesn't match", function() { - it("should throw an error when deleting from the middle of (u)nchanged text", function() { - return expect( - () => this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobazbar" } ], - { op: [{ p: 3, d: "xxx" }], meta: this.meta } - ) - ).to.throw(this.DiffGenerator.ConsistencyError); - }); + describe('deleting over existing deletes', function() { + return it('should delete across multiple (u)changed and (d)deleted text parts', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foo' }, { d: 'baz', meta: this.meta }, { u: 'bar' }], + { op: [{ p: 2, d: 'ob' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { u: 'fo' }, + { d: 'o', meta: this.meta }, + { d: 'baz', meta: this.meta }, + { d: 'b', meta: this.meta }, + { u: 'ar' } + ]) + }) + }) - it("should throw an error when deleting from the start of (u)nchanged text", function() { - return expect( - () => this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobazbar" } ], - { op: [{ p: 0, d: "xxx" }], meta: this.meta } - ) - ).to.throw(this.DiffGenerator.ConsistencyError); - }); + describe("deleting when the text doesn't match", function() { + it('should throw an error when deleting from the middle of (u)nchanged text', function() { + return expect(() => + this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { + op: [{ p: 3, d: 'xxx' }], + meta: this.meta + }) + ).to.throw(this.DiffGenerator.ConsistencyError) + }) - return it("should throw an error when deleting from the end of (u)nchanged text", function() { - return expect( - () => this.DiffGenerator.applyUpdateToDiff( - [ { u: "foobazbar" } ], - { op: [{ p: 6, d: "xxx" }] , meta: this.meta } - ) - ).to.throw(this.DiffGenerator.ConsistencyError); - }); - }); + it('should throw an error when deleting from the start of (u)nchanged text', function() { + return expect(() => + this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { + op: [{ p: 0, d: 'xxx' }], + meta: this.meta + }) + ).to.throw(this.DiffGenerator.ConsistencyError) + }) - describe("when the last update in the existing diff is a delete", function() { return it("should insert the new update before the delete", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { u: "foo" }, { d: "bar", meta: this.meta } ], - { op: [{ p: 3, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { u: "foo" }, - { i: "baz", meta: this.meta }, - { d: "bar", meta: this.meta } - ]); - }); } - ); - - return describe("when the only update in the existing diff is a delete", function() { return it("should insert the new update after the delete", function() { - const diff = this.DiffGenerator.applyUpdateToDiff( - [ { d: "bar", meta: this.meta } ], - { op: [{ p: 0, i: "baz" }], meta: this.meta } - ); - return expect(diff).to.deep.equal([ - { d: "bar", meta: this.meta }, - { i: "baz", meta: this.meta } - ]); - }); } - ); - }); - }); -}); + return it('should throw an error when deleting from the end of (u)nchanged text', function() { + return expect(() => + this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { + op: [{ p: 6, d: 'xxx' }], + meta: this.meta + }) + ).to.throw(this.DiffGenerator.ConsistencyError) + }) + }) + describe('when the last update in the existing diff is a delete', function() { + return it('should insert the new update before the delete', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ u: 'foo' }, { d: 'bar', meta: this.meta }], + { op: [{ p: 3, i: 'baz' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { u: 'foo' }, + { i: 'baz', meta: this.meta }, + { d: 'bar', meta: this.meta } + ]) + }) + }) + return describe('when the only update in the existing diff is a delete', function() { + return it('should insert the new update after the delete', function() { + const diff = this.DiffGenerator.applyUpdateToDiff( + [{ d: 'bar', meta: this.meta }], + { op: [{ p: 0, i: 'baz' }], meta: this.meta } + ) + return expect(diff).to.deep.equal([ + { d: 'bar', meta: this.meta }, + { i: 'baz', meta: this.meta } + ]) + }) + }) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js index cc8f29c6f6..f6768fa197 100644 --- a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js @@ -10,301 +10,439 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/DiffManager.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/DiffManager.js' +const SandboxedModule = require('sandboxed-module') -describe("DiffManager", function() { - beforeEach(function() { - this.DiffManager = SandboxedModule.require(modulePath, { requires: { - "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }), - "./UpdatesManager": (this.UpdatesManager = {}), - "./DocumentUpdaterManager": (this.DocumentUpdaterManager = {}), - "./DiffGenerator": (this.DiffGenerator = {}) - } - }); - this.callback = sinon.stub(); - this.from = new Date(); - this.to = new Date(Date.now() + 10000); - this.project_id = "mock-project-id"; - return this.doc_id = "mock-doc-id"; - }); +describe('DiffManager', function() { + beforeEach(function() { + this.DiffManager = SandboxedModule.require(modulePath, { + requires: { + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + error: sinon.stub(), + warn: sinon.stub() + }), + './UpdatesManager': (this.UpdatesManager = {}), + './DocumentUpdaterManager': (this.DocumentUpdaterManager = {}), + './DiffGenerator': (this.DiffGenerator = {}) + } + }) + this.callback = sinon.stub() + this.from = new Date() + this.to = new Date(Date.now() + 10000) + this.project_id = 'mock-project-id' + return (this.doc_id = 'mock-doc-id') + }) - describe("getLatestDocAndUpdates", function() { - beforeEach(function() { - this.content = "hello world"; - this.version = 42; - this.updates = [ "mock-update-1", "mock-update-2" ]; + describe('getLatestDocAndUpdates', function() { + beforeEach(function() { + this.content = 'hello world' + this.version = 42 + this.updates = ['mock-update-1', 'mock-update-2'] - this.DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, this.content, this.version); - return this.UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, this.updates); - }); + this.DocumentUpdaterManager.getDocument = sinon + .stub() + .callsArgWith(2, null, this.content, this.version) + return (this.UpdatesManager.getDocUpdatesWithUserInfo = sinon + .stub() + .callsArgWith(3, null, this.updates)) + }) - describe("with a fromVersion", function() { - beforeEach(function() { - return this.DiffManager.getLatestDocAndUpdates(this.project_id, this.doc_id, this.from, this.callback); - }); + describe('with a fromVersion', function() { + beforeEach(function() { + return this.DiffManager.getLatestDocAndUpdates( + this.project_id, + this.doc_id, + this.from, + this.callback + ) + }) - it("should get the latest version of the doc", function() { - return this.DocumentUpdaterManager.getDocument - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); + it('should get the latest version of the doc', function() { + return this.DocumentUpdaterManager.getDocument + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) - it("should get the latest updates", function() { - return this.UpdatesManager.getDocUpdatesWithUserInfo - .calledWith(this.project_id, this.doc_id, {from: this.from}) - .should.equal(true); - }); + it('should get the latest updates', function() { + return this.UpdatesManager.getDocUpdatesWithUserInfo + .calledWith(this.project_id, this.doc_id, { from: this.from }) + .should.equal(true) + }) - return it("should call the callback with the content, version and updates", function() { - return this.callback.calledWith(null, this.content, this.version, this.updates).should.equal(true); - }); - }); + return it('should call the callback with the content, version and updates', function() { + return this.callback + .calledWith(null, this.content, this.version, this.updates) + .should.equal(true) + }) + }) - return describe("with no fromVersion", function() { - beforeEach(function() { - return this.DiffManager.getLatestDocAndUpdates(this.project_id, this.doc_id, null, this.callback); - }); + return describe('with no fromVersion', function() { + beforeEach(function() { + return this.DiffManager.getLatestDocAndUpdates( + this.project_id, + this.doc_id, + null, + this.callback + ) + }) - it("should get the latest version of the doc", function() { - return this.DocumentUpdaterManager.getDocument - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); + it('should get the latest version of the doc', function() { + return this.DocumentUpdaterManager.getDocument + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) - it("should not get the latest updates", function() { - return this.UpdatesManager.getDocUpdatesWithUserInfo - .called.should.equal(false); - }); + it('should not get the latest updates', function() { + return this.UpdatesManager.getDocUpdatesWithUserInfo.called.should.equal( + false + ) + }) - return it("should call the callback with the content, version and blank updates", function() { - return this.callback.calledWith(null, this.content, this.version, []).should.equal(true); - }); - }); - }); - + return it('should call the callback with the content, version and blank updates', function() { + return this.callback + .calledWith(null, this.content, this.version, []) + .should.equal(true) + }) + }) + }) - describe("getDiff", function() { - beforeEach(function() { - this.content = "hello world"; - // Op versions are the version they were applied to, so doc is always one version - // ahead.s - this.version = 43; - this.updates = [ - { op: "mock-4", v: 42, meta: { start_ts: new Date(this.to.getTime() + 20)} }, - { op: "mock-3", v: 41, meta: { start_ts: new Date(this.to.getTime() + 10)} }, - { op: "mock-2", v: 40, meta: { start_ts: new Date(this.to.getTime() - 10)} }, - { op: "mock-1", v: 39, meta: { start_ts: new Date(this.to.getTime() - 20)} } - ]; - this.fromVersion = 39; - this.toVersion = 40; - this.diffed_updates = this.updates.slice(2); - this.rewound_content = "rewound-content"; - return this.diff = [ {u: "mock-diff"} ];}); - - describe("with matching versions", function() { - beforeEach(function() { - this.DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, this.rewound_content, this.updates); - this.DiffGenerator.buildDiff = sinon.stub().returns(this.diff); - return this.DiffManager.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, this.callback); - }); + describe('getDiff', function() { + beforeEach(function() { + this.content = 'hello world' + // Op versions are the version they were applied to, so doc is always one version + // ahead.s + this.version = 43 + this.updates = [ + { + op: 'mock-4', + v: 42, + meta: { start_ts: new Date(this.to.getTime() + 20) } + }, + { + op: 'mock-3', + v: 41, + meta: { start_ts: new Date(this.to.getTime() + 10) } + }, + { + op: 'mock-2', + v: 40, + meta: { start_ts: new Date(this.to.getTime() - 10) } + }, + { + op: 'mock-1', + v: 39, + meta: { start_ts: new Date(this.to.getTime() - 20) } + } + ] + this.fromVersion = 39 + this.toVersion = 40 + this.diffed_updates = this.updates.slice(2) + this.rewound_content = 'rewound-content' + return (this.diff = [{ u: 'mock-diff' }]) + }) - it("should get the latest doc and version with all recent updates", function() { - return this.DiffManager.getDocumentBeforeVersion - .calledWith(this.project_id, this.doc_id, this.fromVersion) - .should.equal(true); - }); + describe('with matching versions', function() { + beforeEach(function() { + this.DiffManager.getDocumentBeforeVersion = sinon + .stub() + .callsArgWith(3, null, this.rewound_content, this.updates) + this.DiffGenerator.buildDiff = sinon.stub().returns(this.diff) + return this.DiffManager.getDiff( + this.project_id, + this.doc_id, + this.fromVersion, + this.toVersion, + this.callback + ) + }) - it("should generate the diff", function() { - return this.DiffGenerator.buildDiff - .calledWith(this.rewound_content, this.diffed_updates.slice().reverse()) - .should.equal(true); - }); + it('should get the latest doc and version with all recent updates', function() { + return this.DiffManager.getDocumentBeforeVersion + .calledWith(this.project_id, this.doc_id, this.fromVersion) + .should.equal(true) + }) - return it("should call the callback with the diff", function() { - return this.callback.calledWith(null, this.diff).should.equal(true); - }); - }); + it('should generate the diff', function() { + return this.DiffGenerator.buildDiff + .calledWith( + this.rewound_content, + this.diffed_updates.slice().reverse() + ) + .should.equal(true) + }) - return describe("when the updates are inconsistent", function() { - beforeEach(function() { - this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); - this.DiffGenerator.buildDiff = sinon.stub().throws(this.error = new Error("inconsistent!")); - return this.DiffManager.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, this.callback); - }); + return it('should call the callback with the diff', function() { + return this.callback.calledWith(null, this.diff).should.equal(true) + }) + }) - return it("should call the callback with an error", function() { - return this.callback - .calledWith(this.error) - .should.equal(true); - }); - }); - }); + return describe('when the updates are inconsistent', function() { + beforeEach(function() { + this.DiffManager.getLatestDocAndUpdates = sinon + .stub() + .callsArgWith(3, null, this.content, this.version, this.updates) + this.DiffGenerator.buildDiff = sinon + .stub() + .throws((this.error = new Error('inconsistent!'))) + return this.DiffManager.getDiff( + this.project_id, + this.doc_id, + this.fromVersion, + this.toVersion, + this.callback + ) + }) - describe("getDocumentBeforeVersion", function() { - beforeEach(function() { - this.DiffManager._tryGetDocumentBeforeVersion = sinon.stub(); - this.document = "mock-documents"; - return this.rewound_updates = "mock-rewound-updates"; - }); + return it('should call the callback with an error', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) + }) - describe("succesfully", function() { - beforeEach(function() { - this.DiffManager._tryGetDocumentBeforeVersion.yields(null, this.document, this.rewound_updates); - return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); - }); - - it("should call _tryGetDocumentBeforeVersion", function() { - return this.DiffManager._tryGetDocumentBeforeVersion - .calledWith(this.project_id, this.doc_id, this.version) - .should.equal(true); - }); - - return it("should call the callback with the response", function() { - return this.callback.calledWith(null, this.document, this.rewound_updates).should.equal(true); - }); - }); - - describe("with a retry needed", function() { - beforeEach(function() { - let retried = false; - this.DiffManager._tryGetDocumentBeforeVersion = (project_id, doc_id, version, callback) => { - if (!retried) { - retried = true; - const error = new Error(); - error.retry = true; - return callback(error); - } else { - return callback(null, this.document, this.rewound_updates); - } - }; - sinon.spy(this.DiffManager, "_tryGetDocumentBeforeVersion"); - return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); - }); - - it("should call _tryGetDocumentBeforeVersion twice", function() { - return this.DiffManager._tryGetDocumentBeforeVersion - .calledTwice - .should.equal(true); - }); - - return it("should call the callback with the response", function() { - return this.callback.calledWith(null, this.document, this.rewound_updates).should.equal(true); - }); - }); - - describe("with a non-retriable error", function() { - beforeEach(function() { - this.error = new Error("oops"); - this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error); - return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); - }); - - it("should call _tryGetDocumentBeforeVersion once", function() { - return this.DiffManager._tryGetDocumentBeforeVersion - .calledOnce - .should.equal(true); - }); - - return it("should call the callback with the error", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); - - return describe("when retry limit is matched", function() { - beforeEach(function() { - this.error = new Error("oops"); - this.error.retry = true; - this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error); - return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.version, this.callback); - }); - - it("should call _tryGetDocumentBeforeVersion three times (max retries)", function() { - return this.DiffManager._tryGetDocumentBeforeVersion - .calledThrice - .should.equal(true); - }); - - return it("should call the callback with the error", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); - }); + describe('getDocumentBeforeVersion', function() { + beforeEach(function() { + this.DiffManager._tryGetDocumentBeforeVersion = sinon.stub() + this.document = 'mock-documents' + return (this.rewound_updates = 'mock-rewound-updates') + }) - return describe("_tryGetDocumentBeforeVersion", function() { - beforeEach(function() { - this.content = "hello world"; - // Op versions are the version they were applied to, so doc is always one version - // ahead.s - this.version = 43; - this.updates = [ - { op: "mock-4", v: 42, meta: { start_ts: new Date(this.to.getTime() + 20)} }, - { op: "mock-3", v: 41, meta: { start_ts: new Date(this.to.getTime() + 10)} }, - { op: "mock-2", v: 40, meta: { start_ts: new Date(this.to.getTime() - 10)} }, - { op: "mock-1", v: 39, meta: { start_ts: new Date(this.to.getTime() - 20)} } - ]; - this.fromVersion = 39; - this.rewound_content = "rewound-content"; - return this.diff = [ {u: "mock-diff"} ];}); - - describe("with matching versions", function() { - beforeEach(function() { - this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); - this.DiffGenerator.rewindUpdates = sinon.spy((content, updates) => { - // the rewindUpdates method reverses the 'updates' array - updates.reverse(); - return this.rewound_content; - }); - this.rewindUpdatesWithArgs = this.DiffGenerator.rewindUpdates.withArgs(this.content, this.updates.slice().reverse()); - return this.DiffManager._tryGetDocumentBeforeVersion(this.project_id, this.doc_id, this.fromVersion, this.callback); - }); + describe('succesfully', function() { + beforeEach(function() { + this.DiffManager._tryGetDocumentBeforeVersion.yields( + null, + this.document, + this.rewound_updates + ) + return this.DiffManager.getDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.version, + this.callback + ) + }) - it("should get the latest doc and version with all recent updates", function() { - return this.DiffManager.getLatestDocAndUpdates - .calledWith(this.project_id, this.doc_id, this.fromVersion) - .should.equal(true); - }); + it('should call _tryGetDocumentBeforeVersion', function() { + return this.DiffManager._tryGetDocumentBeforeVersion + .calledWith(this.project_id, this.doc_id, this.version) + .should.equal(true) + }) - it("should rewind the diff", function() { - return sinon.assert.calledOnce(this.rewindUpdatesWithArgs); - }); + return it('should call the callback with the response', function() { + return this.callback + .calledWith(null, this.document, this.rewound_updates) + .should.equal(true) + }) + }) - return it("should call the callback with the rewound document and updates", function() { - return this.callback.calledWith(null, this.rewound_content, this.updates).should.equal(true); - }); - }); + describe('with a retry needed', function() { + beforeEach(function() { + let retried = false + this.DiffManager._tryGetDocumentBeforeVersion = ( + project_id, + doc_id, + version, + callback + ) => { + if (!retried) { + retried = true + const error = new Error() + error.retry = true + return callback(error) + } else { + return callback(null, this.document, this.rewound_updates) + } + } + sinon.spy(this.DiffManager, '_tryGetDocumentBeforeVersion') + return this.DiffManager.getDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.version, + this.callback + ) + }) - describe("with mismatching versions", function() { - beforeEach(function() { - this.version = 50; - this.updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ]; - this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); - return this.DiffManager._tryGetDocumentBeforeVersion(this.project_id, this.doc_id, this.fromVersion, this.callback); - }); + it('should call _tryGetDocumentBeforeVersion twice', function() { + return this.DiffManager._tryGetDocumentBeforeVersion.calledTwice.should.equal( + true + ) + }) - return it("should call the callback with an error with retry = true set", function() { - this.callback.calledOnce.should.equal(true); - const error = this.callback.args[0][0]; - return expect(error.retry).to.equal(true); - }); - }); + return it('should call the callback with the response', function() { + return this.callback + .calledWith(null, this.document, this.rewound_updates) + .should.equal(true) + }) + }) - return describe("when the updates are inconsistent", function() { - beforeEach(function() { - this.DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, this.content, this.version, this.updates); - this.DiffGenerator.rewindUpdates = sinon.stub().throws(this.error = new Error("inconsistent!")); - return this.DiffManager.getDocumentBeforeVersion(this.project_id, this.doc_id, this.fromVersion, this.callback); - }); + describe('with a non-retriable error', function() { + beforeEach(function() { + this.error = new Error('oops') + this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error) + return this.DiffManager.getDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.version, + this.callback + ) + }) - return it("should call the callback with an error", function() { - return this.callback - .calledWith(this.error) - .should.equal(true); - }); - }); - }); -}); + it('should call _tryGetDocumentBeforeVersion once', function() { + return this.DiffManager._tryGetDocumentBeforeVersion.calledOnce.should.equal( + true + ) + }) + + return it('should call the callback with the error', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) + + return describe('when retry limit is matched', function() { + beforeEach(function() { + this.error = new Error('oops') + this.error.retry = true + this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error) + return this.DiffManager.getDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.version, + this.callback + ) + }) + + it('should call _tryGetDocumentBeforeVersion three times (max retries)', function() { + return this.DiffManager._tryGetDocumentBeforeVersion.calledThrice.should.equal( + true + ) + }) + + return it('should call the callback with the error', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) + }) + + return describe('_tryGetDocumentBeforeVersion', function() { + beforeEach(function() { + this.content = 'hello world' + // Op versions are the version they were applied to, so doc is always one version + // ahead.s + this.version = 43 + this.updates = [ + { + op: 'mock-4', + v: 42, + meta: { start_ts: new Date(this.to.getTime() + 20) } + }, + { + op: 'mock-3', + v: 41, + meta: { start_ts: new Date(this.to.getTime() + 10) } + }, + { + op: 'mock-2', + v: 40, + meta: { start_ts: new Date(this.to.getTime() - 10) } + }, + { + op: 'mock-1', + v: 39, + meta: { start_ts: new Date(this.to.getTime() - 20) } + } + ] + this.fromVersion = 39 + this.rewound_content = 'rewound-content' + return (this.diff = [{ u: 'mock-diff' }]) + }) + + describe('with matching versions', function() { + beforeEach(function() { + this.DiffManager.getLatestDocAndUpdates = sinon + .stub() + .callsArgWith(3, null, this.content, this.version, this.updates) + this.DiffGenerator.rewindUpdates = sinon.spy((content, updates) => { + // the rewindUpdates method reverses the 'updates' array + updates.reverse() + return this.rewound_content + }) + this.rewindUpdatesWithArgs = this.DiffGenerator.rewindUpdates.withArgs( + this.content, + this.updates.slice().reverse() + ) + return this.DiffManager._tryGetDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.fromVersion, + this.callback + ) + }) + + it('should get the latest doc and version with all recent updates', function() { + return this.DiffManager.getLatestDocAndUpdates + .calledWith(this.project_id, this.doc_id, this.fromVersion) + .should.equal(true) + }) + + it('should rewind the diff', function() { + return sinon.assert.calledOnce(this.rewindUpdatesWithArgs) + }) + + return it('should call the callback with the rewound document and updates', function() { + return this.callback + .calledWith(null, this.rewound_content, this.updates) + .should.equal(true) + }) + }) + + describe('with mismatching versions', function() { + beforeEach(function() { + this.version = 50 + this.updates = [ + { op: 'mock-1', v: 40 }, + { op: 'mock-1', v: 39 } + ] + this.DiffManager.getLatestDocAndUpdates = sinon + .stub() + .callsArgWith(3, null, this.content, this.version, this.updates) + return this.DiffManager._tryGetDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.fromVersion, + this.callback + ) + }) + + return it('should call the callback with an error with retry = true set', function() { + this.callback.calledOnce.should.equal(true) + const error = this.callback.args[0][0] + return expect(error.retry).to.equal(true) + }) + }) + + return describe('when the updates are inconsistent', function() { + beforeEach(function() { + this.DiffManager.getLatestDocAndUpdates = sinon + .stub() + .callsArgWith(3, null, this.content, this.version, this.updates) + this.DiffGenerator.rewindUpdates = sinon + .stub() + .throws((this.error = new Error('inconsistent!'))) + return this.DiffManager.getDocumentBeforeVersion( + this.project_id, + this.doc_id, + this.fromVersion, + this.callback + ) + }) + + return it('should call the callback with an error', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index e3d82e6370..27e18b1d6b 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -9,88 +9,104 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const chai = require('chai'); -chai.should(); -const sinon = require("sinon"); -const modulePath = "../../../../app/js/MongoAWS.js"; -const SandboxedModule = require('sandboxed-module'); -const {ObjectId} = require("mongojs"); -const MemoryStream = require('memorystream'); -const zlib = require("zlib"); +const chai = require('chai') +chai.should() +const sinon = require('sinon') +const modulePath = '../../../../app/js/MongoAWS.js' +const SandboxedModule = require('sandboxed-module') +const { ObjectId } = require('mongojs') +const MemoryStream = require('memorystream') +const zlib = require('zlib') -describe("MongoAWS", function() { - beforeEach(function() { - this.MongoAWS = SandboxedModule.require(modulePath, { requires: { - "settings-sharelatex": (this.settings = { - trackchanges: { - s3: { - secret: "s3-secret", - key: "s3-key" - }, - stores: { - doc_history: "s3-bucket" - } - } - }), - "child_process": (this.child_process = {}), - "mongo-uri": (this.mongouri = {}), - "logger-sharelatex": (this.logger = {log: sinon.stub(), error: sinon.stub(), err() {}}), - "aws-sdk": (this.awssdk = {}), - "fs": (this.fs = {}), - "s3-streams": (this.S3S = {}), - "./mongojs" : { db: (this.db = {}), ObjectId }, - "JSONStream": (this.JSONStream = {}), - "readline-stream": (this.readline = sinon.stub()), - 'metrics-sharelatex': {inc(){}} - } - }); +describe('MongoAWS', function() { + beforeEach(function() { + this.MongoAWS = SandboxedModule.require(modulePath, { + requires: { + 'settings-sharelatex': (this.settings = { + trackchanges: { + s3: { + secret: 's3-secret', + key: 's3-key' + }, + stores: { + doc_history: 's3-bucket' + } + } + }), + child_process: (this.child_process = {}), + 'mongo-uri': (this.mongouri = {}), + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + error: sinon.stub(), + err() {} + }), + 'aws-sdk': (this.awssdk = {}), + fs: (this.fs = {}), + 's3-streams': (this.S3S = {}), + './mongojs': { db: (this.db = {}), ObjectId }, + JSONStream: (this.JSONStream = {}), + 'readline-stream': (this.readline = sinon.stub()), + 'metrics-sharelatex': { inc() {} } + } + }) - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.pack_id = ObjectId(); - this.update = { v:123 }; - return this.callback = sinon.stub(); - }); + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.pack_id = ObjectId() + this.update = { v: 123 } + return (this.callback = sinon.stub()) + }) - describe("archivePack", function() { + describe('archivePack', function() { + beforeEach(function(done) { + this.awssdk.config = { update: sinon.stub() } + this.awssdk.S3 = sinon.stub() + this.S3S.WriteStream = () => MemoryStream.createWriteStream() + this.db.docHistory = {} + this.db.docHistory.findOne = sinon + .stub() + .callsArgWith(1, null, { pack: 'hello' }) - beforeEach(function(done) { - this.awssdk.config = { update: sinon.stub() }; - this.awssdk.S3 = sinon.stub(); - this.S3S.WriteStream = () => MemoryStream.createWriteStream(); - this.db.docHistory = {}; - this.db.docHistory.findOne = sinon.stub().callsArgWith(1, null, {"pack":"hello"}); + return this.MongoAWS.archivePack( + this.project_id, + this.doc_id, + this.pack_id, + (err, result) => { + this.callback() + return done() + } + ) + }) - return this.MongoAWS.archivePack(this.project_id, this.doc_id, this.pack_id, (err, result) => { - this.callback(); - return done(); - }); - }); + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); + return describe('unArchivePack', function() { + beforeEach(function(done) { + return zlib.gzip('{"pack":"123"}', (err, zbuf) => { + this.awssdk.config = { update: sinon.stub() } + this.awssdk.S3 = sinon.stub() + this.S3S.ReadStream = () => + MemoryStream.createReadStream(zbuf, { readable: true }) + this.db.docHistory = {} + this.db.docHistory.insert = sinon.stub().callsArgWith(1, null, 'pack') - return describe("unArchivePack", function() { + return this.MongoAWS.unArchivePack( + this.project_id, + this.doc_id, + this.pack_id, + (err, result) => { + this.callback() + return done() + } + ) + }) + }) - beforeEach(function(done) { - return zlib.gzip('{"pack":"123"}', (err, zbuf) => { - this.awssdk.config = { update: sinon.stub() }; - this.awssdk.S3 = sinon.stub(); - this.S3S.ReadStream = () => MemoryStream.createReadStream(zbuf, {readable:true}); - this.db.docHistory = {}; - this.db.docHistory.insert = sinon.stub().callsArgWith(1, null, "pack"); - - return this.MongoAWS.unArchivePack(this.project_id, this.doc_id, this.pack_id, (err, result) => { - this.callback(); - return done(); - }); - }); - }); - - return it("should call db.docHistory.insert", function() { - return this.db.docHistory.insert.called.should.equal(true); - }); - }); -}); + return it('should call db.docHistory.insert', function() { + return this.db.docHistory.insert.called.should.equal(true) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index 9b4ce3cc11..a1dcaa0ee5 100644 --- a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -9,127 +9,197 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/DocumentUpdaterManager.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/DocumentUpdaterManager.js' +const SandboxedModule = require('sandboxed-module') -describe("DocumentUpdaterManager", function() { - beforeEach(function() { - this.DocumentUpdaterManager = SandboxedModule.require(modulePath, { requires: { - "request": (this.request = {}), - "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), - 'settings-sharelatex': (this.settings = - {apis : {documentupdater: {url : "http://example.com"}}}) - } - } - ); - this.callback = sinon.stub(); - this.lines = ["one", "two", "three"]; - return this.version = 42; - }); +describe('DocumentUpdaterManager', function() { + beforeEach(function() { + this.DocumentUpdaterManager = SandboxedModule.require(modulePath, { + requires: { + request: (this.request = {}), + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + error: sinon.stub() + }), + 'settings-sharelatex': (this.settings = { + apis: { documentupdater: { url: 'http://example.com' } } + }) + } + }) + this.callback = sinon.stub() + this.lines = ['one', 'two', 'three'] + return (this.version = 42) + }) - describe("getDocument", function() { - describe("successfully", function() { - beforeEach(function() { - this.body = JSON.stringify({ - lines: this.lines, - version: this.version, - ops: []}); - this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body); - return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.callback); - }); + describe('getDocument', function() { + describe('successfully', function() { + beforeEach(function() { + this.body = JSON.stringify({ + lines: this.lines, + version: this.version, + ops: [] + }) + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200 }, this.body) + return this.DocumentUpdaterManager.getDocument( + this.project_id, + this.doc_id, + this.callback + ) + }) - it('should get the document from the document updater', function() { - const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`; - return this.request.get.calledWith(url).should.equal(true); - }); + it('should get the document from the document updater', function() { + const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}` + return this.request.get.calledWith(url).should.equal(true) + }) - return it("should call the callback with the content and version", function() { - return this.callback.calledWith(null, this.lines.join("\n"), this.version).should.equal(true); - }); - }); + return it('should call the callback with the content and version', function() { + return this.callback + .calledWith(null, this.lines.join('\n'), this.version) + .should.equal(true) + }) + }) - describe("when the document updater API returns an error", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); - return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.callback); - }); + describe('when the document updater API returns an error', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith( + 1, + (this.error = new Error('something went wrong')), + null, + null + ) + return this.DocumentUpdaterManager.getDocument( + this.project_id, + this.doc_id, + this.callback + ) + }) - return it("should return an error to the callback", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); + return it('should return an error to the callback', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) - return describe("when the document updater returns a failure error code", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, ""); - return this.DocumentUpdaterManager.getDocument(this.project_id, this.doc_id, this.callback); - }); + return describe('when the document updater returns a failure error code', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 500 }, '') + return this.DocumentUpdaterManager.getDocument( + this.project_id, + this.doc_id, + this.callback + ) + }) - return it("should return the callback with an error", function() { - return this.callback - .calledWith(sinon.match.has('message', "doc updater returned a non-success status code: 500")) - .should.equal(true); - }); - }); - }); + return it('should return the callback with an error', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + 'doc updater returned a non-success status code: 500' + ) + ) + .should.equal(true) + }) + }) + }) - return describe("setDocument", function() { - beforeEach(function() { - this.content = "mock content"; - return this.user_id = "user-id-123"; - }); + return describe('setDocument', function() { + beforeEach(function() { + this.content = 'mock content' + return (this.user_id = 'user-id-123') + }) - describe("successfully", function() { - beforeEach(function() { - this.request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200}); - return this.DocumentUpdaterManager.setDocument(this.project_id, this.doc_id, this.content, this.user_id, this.callback); - }); + describe('successfully', function() { + beforeEach(function() { + this.request.post = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200 }) + return this.DocumentUpdaterManager.setDocument( + this.project_id, + this.doc_id, + this.content, + this.user_id, + this.callback + ) + }) - it('should set the document in the document updater', function() { - const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}`; - return this.request.post - .calledWith({ - url, - json: { - lines: this.content.split("\n"), - source: "restore", - user_id: this.user_id, - undoing: true - } - }).should.equal(true); - }); + it('should set the document in the document updater', function() { + const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}` + return this.request.post + .calledWith({ + url, + json: { + lines: this.content.split('\n'), + source: 'restore', + user_id: this.user_id, + undoing: true + } + }) + .should.equal(true) + }) - return it("should call the callback", function() { - return this.callback.calledWith(null).should.equal(true); - }); - }); + return it('should call the callback', function() { + return this.callback.calledWith(null).should.equal(true) + }) + }) - describe("when the document updater API returns an error", function() { - beforeEach(function() { - this.request.post = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); - return this.DocumentUpdaterManager.setDocument(this.project_id, this.doc_id, this.content, this.user_id, this.callback); - }); + describe('when the document updater API returns an error', function() { + beforeEach(function() { + this.request.post = sinon + .stub() + .callsArgWith( + 1, + (this.error = new Error('something went wrong')), + null, + null + ) + return this.DocumentUpdaterManager.setDocument( + this.project_id, + this.doc_id, + this.content, + this.user_id, + this.callback + ) + }) - return it("should return an error to the callback", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); + return it('should return an error to the callback', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) - return describe("when the document updater returns a failure error code", function() { - beforeEach(function() { - this.request.post = sinon.stub().callsArgWith(1, null, { statusCode: 500 }, ""); - return this.DocumentUpdaterManager.setDocument(this.project_id, this.doc_id, this.content, this.user_id, this.callback); - }); + return describe('when the document updater returns a failure error code', function() { + beforeEach(function() { + this.request.post = sinon + .stub() + .callsArgWith(1, null, { statusCode: 500 }, '') + return this.DocumentUpdaterManager.setDocument( + this.project_id, + this.doc_id, + this.content, + this.user_id, + this.callback + ) + }) - return it("should return the callback with an error", function() { - return this.callback - .calledWith(sinon.match.has('message', "doc updater returned a non-success status code: 500")) - .should.equal(true); - }); - }); - }); -}); + return it('should return the callback with an error', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + 'doc updater returned a non-success status code: 500' + ) + ) + .should.equal(true) + }) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js index 21a74a9661..68bab28da3 100644 --- a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js @@ -9,175 +9,193 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/HttpController.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/HttpController.js' +const SandboxedModule = require('sandboxed-module') -describe("HttpController", function() { - beforeEach(function() { - this.HttpController = SandboxedModule.require(modulePath, { requires: { - "logger-sharelatex": { log: sinon.stub() }, - "./UpdatesManager": (this.UpdatesManager = {}), - "./DiffManager": (this.DiffManager = {}), - "./RestoreManager": (this.RestoreManager = {}), - "./PackManager": (this.PackManager = {}), - "./DocArchiveManager": (this.DocArchiveManager = {}), - "./HealthChecker": (this.HealthChecker = {}) - } - }); - this.doc_id = "doc-id-123"; - this.project_id = "project-id-123"; - this.next = sinon.stub(); - this.user_id = "mock-user-123"; - return this.now = Date.now(); - }); +describe('HttpController', function() { + beforeEach(function() { + this.HttpController = SandboxedModule.require(modulePath, { + requires: { + 'logger-sharelatex': { log: sinon.stub() }, + './UpdatesManager': (this.UpdatesManager = {}), + './DiffManager': (this.DiffManager = {}), + './RestoreManager': (this.RestoreManager = {}), + './PackManager': (this.PackManager = {}), + './DocArchiveManager': (this.DocArchiveManager = {}), + './HealthChecker': (this.HealthChecker = {}) + } + }) + this.doc_id = 'doc-id-123' + this.project_id = 'project-id-123' + this.next = sinon.stub() + this.user_id = 'mock-user-123' + return (this.now = Date.now()) + }) - describe("flushDoc", function() { - beforeEach(function() { - this.req = { - params: { - doc_id: this.doc_id, - project_id: this.project_id - } - }; - this.res = - {send: sinon.stub()}; - this.UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2); - return this.HttpController.flushDoc(this.req, this.res, this.next); - }); + describe('flushDoc', function() { + beforeEach(function() { + this.req = { + params: { + doc_id: this.doc_id, + project_id: this.project_id + } + } + this.res = { send: sinon.stub() } + this.UpdatesManager.processUncompressedUpdatesWithLock = sinon + .stub() + .callsArg(2) + return this.HttpController.flushDoc(this.req, this.res, this.next) + }) - it("should process the updates", function() { - return this.UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); + it('should process the updates', function() { + return this.UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) - return it("should return a success code", function() { - return this.res.send.calledWith(204).should.equal(true); - }); - }); + return it('should return a success code', function() { + return this.res.send.calledWith(204).should.equal(true) + }) + }) - describe("flushProject", function() { - beforeEach(function() { - this.req = { - params: { - project_id: this.project_id - } - }; - this.res = - {send: sinon.stub()}; - this.UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1); - return this.HttpController.flushProject(this.req, this.res, this.next); - }); + describe('flushProject', function() { + beforeEach(function() { + this.req = { + params: { + project_id: this.project_id + } + } + this.res = { send: sinon.stub() } + this.UpdatesManager.processUncompressedUpdatesForProject = sinon + .stub() + .callsArg(1) + return this.HttpController.flushProject(this.req, this.res, this.next) + }) - it("should process the updates", function() { - return this.UpdatesManager.processUncompressedUpdatesForProject - .calledWith(this.project_id) - .should.equal(true); - }); + it('should process the updates', function() { + return this.UpdatesManager.processUncompressedUpdatesForProject + .calledWith(this.project_id) + .should.equal(true) + }) - return it("should return a success code", function() { - return this.res.send.calledWith(204).should.equal(true); - }); - }); + return it('should return a success code', function() { + return this.res.send.calledWith(204).should.equal(true) + }) + }) + describe('getDiff', function() { + beforeEach(function() { + this.from = 42 + this.to = 45 + this.req = { + params: { + doc_id: this.doc_id, + project_id: this.project_id + }, + query: { + from: this.from.toString(), + to: this.to.toString() + } + } + this.res = { json: sinon.stub() } + this.diff = [{ u: 'mock-diff' }] + this.DiffManager.getDiff = sinon.stub().callsArgWith(4, null, this.diff) + return this.HttpController.getDiff(this.req, this.res, this.next) + }) - describe("getDiff", function() { - beforeEach(function() { - this.from = 42; - this.to = 45; - this.req = { - params: { - doc_id: this.doc_id, - project_id: this.project_id - }, - query: { - from: this.from.toString(), - to: this.to.toString() - } - }; - this.res = - {json: sinon.stub()}; - this.diff = [ {u: "mock-diff"} ]; - this.DiffManager.getDiff = sinon.stub().callsArgWith(4, null, this.diff); - return this.HttpController.getDiff(this.req, this.res, this.next); - }); + it('should get the diff', function() { + return this.DiffManager.getDiff + .calledWith( + this.project_id, + this.doc_id, + parseInt(this.from, 10), + parseInt(this.to, 10) + ) + .should.equal(true) + }) - it("should get the diff", function() { - return this.DiffManager.getDiff - .calledWith(this.project_id, this.doc_id, parseInt(this.from, 10), parseInt(this.to, 10)) - .should.equal(true); - }); + return it('should return the diff', function() { + return this.res.json.calledWith({ diff: this.diff }).should.equal(true) + }) + }) - return it("should return the diff", function() { - return this.res.json.calledWith({diff: this.diff}).should.equal(true); - }); - }); + describe('getUpdates', function() { + beforeEach(function() { + this.before = Date.now() + this.nextBeforeTimestamp = this.before - 100 + this.min_count = 10 + this.req = { + params: { + project_id: this.project_id + }, + query: { + before: this.before.toString(), + min_count: this.min_count.toString() + } + } + this.res = { json: sinon.stub() } + this.updates = ['mock-summarized-updates'] + this.UpdatesManager.getSummarizedProjectUpdates = sinon + .stub() + .callsArgWith(2, null, this.updates, this.nextBeforeTimestamp) + return this.HttpController.getUpdates(this.req, this.res, this.next) + }) - describe("getUpdates", function() { - beforeEach(function() { - this.before = Date.now(); - this.nextBeforeTimestamp = this.before - 100; - this.min_count = 10; - this.req = { - params: { - project_id: this.project_id - }, - query: { - before: this.before.toString(), - min_count: this.min_count.toString() - } - }; - this.res = - {json: sinon.stub()}; - this.updates = ["mock-summarized-updates"]; - this.UpdatesManager.getSummarizedProjectUpdates = sinon.stub().callsArgWith(2, null, this.updates, this.nextBeforeTimestamp); - return this.HttpController.getUpdates(this.req, this.res, this.next); - }); + it('should get the updates', function() { + return this.UpdatesManager.getSummarizedProjectUpdates + .calledWith(this.project_id, { + before: this.before, + min_count: this.min_count + }) + .should.equal(true) + }) - it("should get the updates", function() { - return this.UpdatesManager.getSummarizedProjectUpdates - .calledWith(this.project_id, {before: this.before, min_count: this.min_count}) - .should.equal(true); - }); + return it('should return the formatted updates', function() { + return this.res.json + .calledWith({ + updates: this.updates, + nextBeforeTimestamp: this.nextBeforeTimestamp + }) + .should.equal(true) + }) + }) - return it("should return the formatted updates", function() { - return this.res.json.calledWith({updates: this.updates, nextBeforeTimestamp: this.nextBeforeTimestamp}).should.equal(true); - }); - }); + return describe('RestoreManager', function() { + beforeEach(function() { + this.version = '42' + this.req = { + params: { + doc_id: this.doc_id, + project_id: this.project_id, + version: this.version + }, + headers: { + 'x-user-id': this.user_id + } + } + this.res = { send: sinon.stub() } - return describe("RestoreManager", function() { - beforeEach(function() { - this.version = "42"; - this.req = { - params: { - doc_id: this.doc_id, - project_id: this.project_id, - version: this.version - }, - headers: { - "x-user-id": this.user_id - } - }; - this.res = - {send: sinon.stub()}; + this.RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(4) + return this.HttpController.restore(this.req, this.res, this.next) + }) - this.RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(4); - return this.HttpController.restore(this.req, this.res, this.next); - }); - - it("should restore the document", function() { - return this.RestoreManager.restoreToBeforeVersion - .calledWith(this.project_id, this.doc_id, parseInt(this.version, 10), this.user_id) - .should.equal(true); - }); - - return it("should return a success code", function() { - return this.res.send.calledWith(204).should.equal(true); - }); - }); -}); + it('should restore the document', function() { + return this.RestoreManager.restoreToBeforeVersion + .calledWith( + this.project_id, + this.doc_id, + parseInt(this.version, 10), + this.user_id + ) + .should.equal(true) + }) + return it('should return a success code', function() { + return this.res.send.calledWith(204).should.equal(true) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js index 2ad9072133..9c5a8fdebc 100644 --- a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js @@ -15,270 +15,306 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/LockManager.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/LockManager.js' +const SandboxedModule = require('sandboxed-module') -describe("LockManager", function() { - beforeEach(function() { - this.Settings = { - redis: { - lock:{} - } - }; - this.LockManager = SandboxedModule.require(modulePath, { requires: { - "redis-sharelatex": { - createClient: () => { return this.rclient = - {auth: sinon.stub()}; } - }, - "settings-sharelatex": this.Settings, - "logger-sharelatex": {error() {}} - } - }); +describe('LockManager', function() { + beforeEach(function() { + this.Settings = { + redis: { + lock: {} + } + } + this.LockManager = SandboxedModule.require(modulePath, { + requires: { + 'redis-sharelatex': { + createClient: () => { + return (this.rclient = { auth: sinon.stub() }) + } + }, + 'settings-sharelatex': this.Settings, + 'logger-sharelatex': { error() {} } + } + }) - this.key = "lock-key"; - return this.callback = sinon.stub(); - }); + this.key = 'lock-key' + return (this.callback = sinon.stub()) + }) - describe("checkLock", function() { - describe("when the lock is taken", function() { - beforeEach(function() { - this.rclient.exists = sinon.stub().callsArgWith(1, null, "1"); - return this.LockManager.checkLock(this.key, this.callback); - }); + describe('checkLock', function() { + describe('when the lock is taken', function() { + beforeEach(function() { + this.rclient.exists = sinon.stub().callsArgWith(1, null, '1') + return this.LockManager.checkLock(this.key, this.callback) + }) - it("should check the lock in redis", function() { - return this.rclient.exists - .calledWith(this.key) - .should.equal(true); - }); + it('should check the lock in redis', function() { + return this.rclient.exists.calledWith(this.key).should.equal(true) + }) - return it("should return the callback with false", function() { - return this.callback.calledWith(null, false).should.equal(true); - }); - }); + return it('should return the callback with false', function() { + return this.callback.calledWith(null, false).should.equal(true) + }) + }) - return describe("when the lock is free", function() { - beforeEach(function() { - this.rclient.exists = sinon.stub().callsArgWith(1, null, "0"); - return this.LockManager.checkLock(this.key, this.callback); - }); + return describe('when the lock is free', function() { + beforeEach(function() { + this.rclient.exists = sinon.stub().callsArgWith(1, null, '0') + return this.LockManager.checkLock(this.key, this.callback) + }) - return it("should return the callback with true", function() { - return this.callback.calledWith(null, true).should.equal(true); - }); - }); - }); + return it('should return the callback with true', function() { + return this.callback.calledWith(null, true).should.equal(true) + }) + }) + }) + describe('tryLock', function() { + describe('when the lock is taken', function() { + beforeEach(function() { + this.rclient.set = sinon.stub().callsArgWith(5, null, null) + this.LockManager.randomLock = sinon + .stub() + .returns('locked-random-value') + return this.LockManager.tryLock(this.key, this.callback) + }) - describe("tryLock", function() { - describe("when the lock is taken", function() { - beforeEach(function() { - this.rclient.set = sinon.stub().callsArgWith(5, null, null); - this.LockManager.randomLock = sinon.stub().returns("locked-random-value"); - return this.LockManager.tryLock(this.key, this.callback); - }); + it('should check the lock in redis', function() { + return this.rclient.set + .calledWith( + this.key, + 'locked-random-value', + 'EX', + this.LockManager.LOCK_TTL, + 'NX' + ) + .should.equal(true) + }) - it("should check the lock in redis", function() { - return this.rclient.set - .calledWith(this.key, "locked-random-value", "EX", this.LockManager.LOCK_TTL, "NX") - .should.equal(true); - }); + return it('should return the callback with false', function() { + return this.callback.calledWith(null, false).should.equal(true) + }) + }) - return it("should return the callback with false", function() { - return this.callback.calledWith(null, false).should.equal(true); - }); - }); + return describe('when the lock is free', function() { + beforeEach(function() { + this.rclient.set = sinon.stub().callsArgWith(5, null, 'OK') + return this.LockManager.tryLock(this.key, this.callback) + }) - return describe("when the lock is free", function() { - beforeEach(function() { - this.rclient.set = sinon.stub().callsArgWith(5, null, "OK"); - return this.LockManager.tryLock(this.key, this.callback); - }); + return it('should return the callback with true', function() { + return this.callback.calledWith(null, true).should.equal(true) + }) + }) + }) - return it("should return the callback with true", function() { - return this.callback.calledWith(null, true).should.equal(true); - }); - }); - }); + describe('deleteLock', function() { + return beforeEach(function() { + beforeEach(function() { + this.rclient.del = sinon.stub().callsArg(1) + return this.LockManager.deleteLock(this.key, this.callback) + }) - describe("deleteLock", function() { return beforeEach(function() { - beforeEach(function() { - this.rclient.del = sinon.stub().callsArg(1); - return this.LockManager.deleteLock(this.key, this.callback); - }); + it('should delete the lock in redis', function() { + return this.rclient.del.calledWith(key).should.equal(true) + }) - it("should delete the lock in redis", function() { - return this.rclient.del - .calledWith(key) - .should.equal(true); - }); + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); } - ); + describe('getLock', function() { + describe('when the lock is not taken', function() { + beforeEach(function(done) { + this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, true) + return this.LockManager.getLock(this.key, (...args) => { + this.callback(...Array.from(args || [])) + return done() + }) + }) - describe("getLock", function() { - describe("when the lock is not taken", function() { - beforeEach(function(done) { - this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, true); - return this.LockManager.getLock(this.key, (...args) => { - this.callback(...Array.from(args || [])); - return done(); - }); - }); + it('should try to get the lock', function() { + return this.LockManager.tryLock.calledWith(this.key).should.equal(true) + }) - it("should try to get the lock", function() { - return this.LockManager.tryLock - .calledWith(this.key) - .should.equal(true); - }); + it('should only need to try once', function() { + return this.LockManager.tryLock.callCount.should.equal(1) + }) - it("should only need to try once", function() { - return this.LockManager.tryLock.callCount.should.equal(1); - }); + return it('should return the callback', function() { + return this.callback.calledWith(null).should.equal(true) + }) + }) - return it("should return the callback", function() { - return this.callback.calledWith(null).should.equal(true); - }); - }); + describe('when the lock is initially set', function() { + beforeEach(function(done) { + const startTime = Date.now() + this.LockManager.LOCK_TEST_INTERVAL = 5 + this.LockManager.tryLock = function(doc_id, callback) { + if (callback == null) { + callback = function(error, isFree) {} + } + if (Date.now() - startTime < 100) { + return callback(null, false) + } else { + return callback(null, true) + } + } + sinon.spy(this.LockManager, 'tryLock') - describe("when the lock is initially set", function() { - beforeEach(function(done) { - const startTime = Date.now(); - this.LockManager.LOCK_TEST_INTERVAL = 5; - this.LockManager.tryLock = function(doc_id, callback) { - if (callback == null) { callback = function(error, isFree) {}; } - if ((Date.now() - startTime) < 100) { - return callback(null, false); - } else { - return callback(null, true); - } - }; - sinon.spy(this.LockManager, "tryLock"); + return this.LockManager.getLock(this.key, (...args) => { + this.callback(...Array.from(args || [])) + return done() + }) + }) - return this.LockManager.getLock(this.key, (...args) => { - this.callback(...Array.from(args || [])); - return done(); - }); - }); + it('should call tryLock multiple times until free', function() { + return (this.LockManager.tryLock.callCount > 1).should.equal(true) + }) - it("should call tryLock multiple times until free", function() { - return (this.LockManager.tryLock.callCount > 1).should.equal(true); - }); + return it('should return the callback', function() { + return this.callback.calledWith(null).should.equal(true) + }) + }) - return it("should return the callback", function() { - return this.callback.calledWith(null).should.equal(true); - }); - }); + return describe('when the lock times out', function() { + beforeEach(function(done) { + const time = Date.now() + this.LockManager.MAX_LOCK_WAIT_TIME = 5 + this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, false) + return this.LockManager.getLock(this.key, (...args) => { + this.callback(...Array.from(args || [])) + return done() + }) + }) - return describe("when the lock times out", function() { - beforeEach(function(done) { - const time = Date.now(); - this.LockManager.MAX_LOCK_WAIT_TIME = 5; - this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, false); - return this.LockManager.getLock(this.key, (...args) => { - this.callback(...Array.from(args || [])); - return done(); - }); - }); + return it('should return the callback with an error', function() { + return this.callback + .calledWith(sinon.match.instanceOf(Error)) + .should.equal(true) + }) + }) + }) - return it("should return the callback with an error", function() { - return this.callback.calledWith(sinon.match.instanceOf(Error)).should.equal(true); - }); - }); - }); + return describe('runWithLock', function() { + describe('with successful run', function() { + beforeEach(function() { + this.runner = function(releaseLock) { + if (releaseLock == null) { + releaseLock = function(error) {} + } + return releaseLock() + } + sinon.spy(this, 'runner') + this.LockManager.getLock = sinon.stub().callsArg(1) + this.LockManager.releaseLock = sinon.stub().callsArg(2) + return this.LockManager.runWithLock( + this.key, + this.runner, + this.callback + ) + }) - return describe("runWithLock", function() { - describe("with successful run", function() { - beforeEach(function() { - this.runner = function(releaseLock) { - if (releaseLock == null) { releaseLock = function(error) {}; } - return releaseLock(); - }; - sinon.spy(this, "runner"); - this.LockManager.getLock = sinon.stub().callsArg(1); - this.LockManager.releaseLock = sinon.stub().callsArg(2); - return this.LockManager.runWithLock(this.key, this.runner, this.callback); - }); + it('should get the lock', function() { + return this.LockManager.getLock.calledWith(this.key).should.equal(true) + }) - it("should get the lock", function() { - return this.LockManager.getLock - .calledWith(this.key) - .should.equal(true); - }); + it('should run the passed function', function() { + return this.runner.called.should.equal(true) + }) - it("should run the passed function", function() { - return this.runner.called.should.equal(true); - }); + it('should release the lock', function() { + return this.LockManager.releaseLock + .calledWith(this.key) + .should.equal(true) + }) - it("should release the lock", function() { - return this.LockManager.releaseLock - .calledWith(this.key) - .should.equal(true); - }); + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); + describe('when the runner function returns an error', function() { + beforeEach(function() { + this.error = new Error('oops') + this.runner = releaseLock => { + if (releaseLock == null) { + releaseLock = function(error) {} + } + return releaseLock(this.error) + } + sinon.spy(this, 'runner') + this.LockManager.getLock = sinon.stub().callsArg(1) + this.LockManager.releaseLock = sinon.stub().callsArg(2) + return this.LockManager.runWithLock( + this.key, + this.runner, + this.callback + ) + }) - describe("when the runner function returns an error", function() { - beforeEach(function() { - this.error = new Error("oops"); - this.runner = releaseLock => { - if (releaseLock == null) { releaseLock = function(error) {}; } - return releaseLock(this.error); - }; - sinon.spy(this, "runner"); - this.LockManager.getLock = sinon.stub().callsArg(1); - this.LockManager.releaseLock = sinon.stub().callsArg(2); - return this.LockManager.runWithLock(this.key, this.runner, this.callback); - }); + it('should release the lock', function() { + return this.LockManager.releaseLock + .calledWith(this.key) + .should.equal(true) + }) - it("should release the lock", function() { - return this.LockManager.releaseLock - .calledWith(this.key) - .should.equal(true); - }); + return it('should call the callback with the error', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) - return it("should call the callback with the error", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); + return describe('releaseLock', function() { + describe('when the lock is current', function() { + beforeEach(function() { + this.rclient.eval = sinon.stub().yields(null, 1) + return this.LockManager.releaseLock( + this.key, + this.lockValue, + this.callback + ) + }) - return describe("releaseLock", function() { - describe("when the lock is current", function() { - beforeEach(function() { - this.rclient.eval = sinon.stub().yields(null, 1); - return this.LockManager.releaseLock(this.key, this.lockValue, this.callback); - }); + it('should clear the data from redis', function() { + return this.rclient.eval + .calledWith( + this.LockManager.unlockScript, + 1, + this.key, + this.lockValue + ) + .should.equal(true) + }) - it('should clear the data from redis', function() { - return this.rclient.eval.calledWith(this.LockManager.unlockScript, 1, this.key, this.lockValue).should.equal(true); - }); + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) - return it('should call the callback', function() { - return this.callback.called.should.equal(true); - }); - }); - - return describe("when the lock has expired", function() { - beforeEach(function() { - this.rclient.eval = sinon.stub().yields(null, 0); - return this.LockManager.releaseLock(this.key, this.lockValue, this.callback); - }); - - return it('should return an error if the lock has expired', function() { - return this.callback.calledWith(sinon.match.has('message', "tried to release timed out lock")).should.equal(true); - }); - }); - }); - }); -}); + return describe('when the lock has expired', function() { + beforeEach(function() { + this.rclient.eval = sinon.stub().yields(null, 0) + return this.LockManager.releaseLock( + this.key, + this.lockValue, + this.callback + ) + }) + return it('should return an error if the lock has expired', function() { + return this.callback + .calledWith( + sinon.match.has('message', 'tried to release timed out lock') + ) + .should.equal(true) + }) + }) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index a72ec1dec8..8adc8dd202 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -9,195 +9,237 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/MongoManager.js"; -const packModulePath = "../../../../app/js/PackManager.js"; -const SandboxedModule = require('sandboxed-module'); -const {ObjectId} = require("mongojs"); -const tk = require("timekeeper"); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/MongoManager.js' +const packModulePath = '../../../../app/js/PackManager.js' +const SandboxedModule = require('sandboxed-module') +const { ObjectId } = require('mongojs') +const tk = require('timekeeper') -describe("MongoManager", function() { - beforeEach(function() { - tk.freeze(new Date()); - this.MongoManager = SandboxedModule.require(modulePath, { requires: { - "./mongojs" : { db: (this.db = {}), ObjectId }, - "./PackManager" : (this.PackManager = {}), - 'metrics-sharelatex': {timeAsyncMethod(){}}, - 'logger-sharelatex': {log(){}} - } - }); - this.callback = sinon.stub(); - this.doc_id = ObjectId().toString(); - return this.project_id = ObjectId().toString(); - }); +describe('MongoManager', function() { + beforeEach(function() { + tk.freeze(new Date()) + this.MongoManager = SandboxedModule.require(modulePath, { + requires: { + './mongojs': { db: (this.db = {}), ObjectId }, + './PackManager': (this.PackManager = {}), + 'metrics-sharelatex': { timeAsyncMethod() {} }, + 'logger-sharelatex': { log() {} } + } + }) + this.callback = sinon.stub() + this.doc_id = ObjectId().toString() + return (this.project_id = ObjectId().toString()) + }) - afterEach(function() { return tk.reset(); }); + afterEach(function() { + return tk.reset() + }) - describe("getLastCompressedUpdate", function() { - beforeEach(function() { - this.update = "mock-update"; - this.db.docHistory = {}; - this.db.docHistory.find = sinon.stub().returns(this.db.docHistory); - this.db.docHistory.findOne = sinon.stub().returns(this.db.docHistory); - this.db.docHistory.sort = sinon.stub().returns(this.db.docHistory); - this.db.docHistory.limit = sinon.stub().returns(this.db.docHistory); - this.db.docHistory.toArray = sinon.stub().callsArgWith(0, null, [this.update]); + describe('getLastCompressedUpdate', function() { + beforeEach(function() { + this.update = 'mock-update' + this.db.docHistory = {} + this.db.docHistory.find = sinon.stub().returns(this.db.docHistory) + this.db.docHistory.findOne = sinon.stub().returns(this.db.docHistory) + this.db.docHistory.sort = sinon.stub().returns(this.db.docHistory) + this.db.docHistory.limit = sinon.stub().returns(this.db.docHistory) + this.db.docHistory.toArray = sinon + .stub() + .callsArgWith(0, null, [this.update]) - return this.MongoManager.getLastCompressedUpdate(this.doc_id, this.callback); - }); + return this.MongoManager.getLastCompressedUpdate( + this.doc_id, + this.callback + ) + }) - it("should find the updates for the doc", function() { - return this.db.docHistory.find - .calledWith({doc_id: ObjectId(this.doc_id)}) - .should.equal(true); - }); + it('should find the updates for the doc', function() { + return this.db.docHistory.find + .calledWith({ doc_id: ObjectId(this.doc_id) }) + .should.equal(true) + }) - it("should limit to one result", function() { - return this.db.docHistory.limit - .calledWith(1) - .should.equal(true); - }); + it('should limit to one result', function() { + return this.db.docHistory.limit.calledWith(1).should.equal(true) + }) - it("should sort in descending version order", function() { - return this.db.docHistory.sort - .calledWith({v: -1}) - .should.equal(true); - }); + it('should sort in descending version order', function() { + return this.db.docHistory.sort.calledWith({ v: -1 }).should.equal(true) + }) - return it("should call the call back with the update", function() { - return this.callback.calledWith(null, this.update).should.equal(true); - }); - }); + return it('should call the call back with the update', function() { + return this.callback.calledWith(null, this.update).should.equal(true) + }) + }) + describe('peekLastCompressedUpdate', function() { + describe('when there is no last update', function() { + beforeEach(function() { + this.PackManager.getLastPackFromIndex = sinon + .stub() + .callsArgWith(1, null, null) + this.MongoManager.getLastCompressedUpdate = sinon + .stub() + .callsArgWith(1, null, null) + return this.MongoManager.peekLastCompressedUpdate( + this.doc_id, + this.callback + ) + }) - describe("peekLastCompressedUpdate", function() { - describe("when there is no last update", function() { - beforeEach(function() { - this.PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, null); - this.MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null); - return this.MongoManager.peekLastCompressedUpdate(this.doc_id, this.callback); - }); + it('should get the last update', function() { + return this.MongoManager.getLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) - it("should get the last update", function() { - return this.MongoManager.getLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); + return it('should call the callback with no update', function() { + return this.callback.calledWith(null, null).should.equal(true) + }) + }) - return it("should call the callback with no update", function() { - return this.callback.calledWith(null, null).should.equal(true); - }); - }); + describe('when there is an update', function() { + beforeEach(function() { + this.update = { _id: Object() } + this.MongoManager.getLastCompressedUpdate = sinon + .stub() + .callsArgWith(1, null, this.update) + return this.MongoManager.peekLastCompressedUpdate( + this.doc_id, + this.callback + ) + }) - describe("when there is an update", function() { - beforeEach(function() { - this.update = { _id: Object() }; - this.MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, this.update); - return this.MongoManager.peekLastCompressedUpdate(this.doc_id, this.callback); - }); + it('should get the last update', function() { + return this.MongoManager.getLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) - it("should get the last update", function() { - return this.MongoManager.getLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); + return it('should call the callback with the update', function() { + return this.callback.calledWith(null, this.update).should.equal(true) + }) + }) - return it("should call the callback with the update", function() { - return this.callback.calledWith(null, this.update).should.equal(true); - }); - }); + return describe('when there is a last update in S3', function() { + beforeEach(function() { + this.update = { _id: Object(), v: 12345, v_end: 12345, inS3: true } + this.PackManager.getLastPackFromIndex = sinon + .stub() + .callsArgWith(1, null, this.update) + this.MongoManager.getLastCompressedUpdate = sinon + .stub() + .callsArgWith(1, null) + return this.MongoManager.peekLastCompressedUpdate( + this.doc_id, + this.callback + ) + }) - return describe("when there is a last update in S3", function() { - beforeEach(function() { - this.update = { _id: Object(), v: 12345, v_end: 12345, inS3:true}; - this.PackManager.getLastPackFromIndex = sinon.stub().callsArgWith(1, null, this.update); - this.MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null); - return this.MongoManager.peekLastCompressedUpdate(this.doc_id, this.callback); - }); + it('should get the last update', function() { + return this.MongoManager.getLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) - it("should get the last update", function() { - return this.MongoManager.getLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); + return it('should call the callback with a null update and the correct version', function() { + return this.callback + .calledWith(null, null, this.update.v_end) + .should.equal(true) + }) + }) + }) - return it("should call the callback with a null update and the correct version", function() { - return this.callback.calledWith(null, null, this.update.v_end).should.equal(true); - }); - }); - }); + describe('backportProjectId', function() { + beforeEach(function() { + this.db.docHistory = { update: sinon.stub().callsArg(3) } + return this.MongoManager.backportProjectId( + this.project_id, + this.doc_id, + this.callback + ) + }) + it("should insert the project_id into all entries for the doc_id which don't have it set", function() { + return this.db.docHistory.update + .calledWith( + { + doc_id: ObjectId(this.doc_id), + project_id: { $exists: false } + }, + { + $set: { project_id: ObjectId(this.project_id) } + }, + { + multi: true + } + ) + .should.equal(true) + }) - describe("backportProjectId", function() { - beforeEach(function() { - this.db.docHistory = - {update: sinon.stub().callsArg(3)}; - return this.MongoManager.backportProjectId(this.project_id, this.doc_id, this.callback); - }); + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) - it("should insert the project_id into all entries for the doc_id which don't have it set", function() { - return this.db.docHistory.update - .calledWith({ - doc_id: ObjectId(this.doc_id), - project_id: { $exists: false } - }, { - $set: { project_id: ObjectId(this.project_id) } - }, { - multi: true - }) - .should.equal(true); - }); + describe('getProjectMetaData', function() { + beforeEach(function() { + this.metadata = { mock: 'metadata' } + this.db.projectHistoryMetaData = { + find: sinon.stub().callsArgWith(1, null, [this.metadata]) + } + return this.MongoManager.getProjectMetaData( + this.project_id, + this.callback + ) + }) - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); + it('should look up the meta data in the db', function() { + return this.db.projectHistoryMetaData.find + .calledWith({ project_id: ObjectId(this.project_id) }) + .should.equal(true) + }) - describe("getProjectMetaData", function() { - beforeEach(function() { - this.metadata = { "mock": "metadata" }; - this.db.projectHistoryMetaData = - {find: sinon.stub().callsArgWith(1, null, [this.metadata])}; - return this.MongoManager.getProjectMetaData(this.project_id, this.callback); - }); + return it('should return the metadata', function() { + return this.callback.calledWith(null, this.metadata).should.equal(true) + }) + }) - it("should look up the meta data in the db", function() { - return this.db.projectHistoryMetaData.find - .calledWith({ project_id: ObjectId(this.project_id) }) - .should.equal(true); - }); + return describe('setProjectMetaData', function() { + beforeEach(function() { + this.metadata = { mock: 'metadata' } + this.db.projectHistoryMetaData = { + update: sinon.stub().callsArgWith(3, null, [this.metadata]) + } + return this.MongoManager.setProjectMetaData( + this.project_id, + this.metadata, + this.callback + ) + }) - return it("should return the metadata", function() { - return this.callback.calledWith(null, this.metadata).should.equal(true); - }); - }); - - return describe("setProjectMetaData", function() { - beforeEach(function() { - this.metadata = { "mock": "metadata" }; - this.db.projectHistoryMetaData = - {update: sinon.stub().callsArgWith(3, null, [this.metadata])}; - return this.MongoManager.setProjectMetaData(this.project_id, this.metadata, this.callback); - }); - - it("should upsert the metadata into the DB", function() { - return this.db.projectHistoryMetaData.update - .calledWith({ - project_id: ObjectId(this.project_id) - }, { - $set: this.metadata - }, { - upsert: true - }) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); -}); + it('should upsert the metadata into the DB', function() { + return this.db.projectHistoryMetaData.update + .calledWith( + { + project_id: ObjectId(this.project_id) + }, + { + $set: this.metadata + }, + { + upsert: true + } + ) + .should.equal(true) + }) + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index dda7a5e6bb..843a010f8f 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -10,461 +10,701 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const { assert } = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/PackManager.js"; -const SandboxedModule = require('sandboxed-module'); -const {ObjectId} = require("mongojs"); -const bson = require("bson"); -const BSON = new bson.BSONPure(); -const _ = require("underscore"); - -const tk = require("timekeeper"); - -describe("PackManager", function() { - beforeEach(function() { - tk.freeze(new Date()); - this.PackManager = SandboxedModule.require(modulePath, { requires: { - "./mongojs" : { db: (this.db = {}), ObjectId, BSON }, - "./LockManager" : {}, - "./MongoAWS": {}, - "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }, - 'metrics-sharelatex': {inc(){}}, - "./ProjectIterator": require("../../../../app/js/ProjectIterator.js"), // Cache for speed - "settings-sharelatex": { - redis: {lock: {key_schema: {}}} - } - } - }); - this.callback = sinon.stub(); - this.doc_id = ObjectId().toString(); - this.project_id = ObjectId().toString(); - return this.PackManager.MAX_COUNT = 512; - }); - - afterEach(function() { return tk.reset(); }); - - describe("insertCompressedUpdates", function() { - beforeEach(function() { - this.lastUpdate = { - _id: "12345", - pack: [ - { op: "op-1", meta: "meta-1", v: 1}, - { op: "op-2", meta: "meta-2", v: 2} - ], - n : 2, - sz : 100 - }; - this.newUpdates = [ - { op: "op-3", meta: "meta-3", v: 3}, - { op: "op-4", meta: "meta-4", v: 4} - ]; - return this.db.docHistory = { - save: sinon.stub().callsArg(1), - insert: sinon.stub().callsArg(1), - findAndModify: sinon.stub().callsArg(1) - }; - }); - - describe("with no last update", function() { - beforeEach(function() { - this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4); - return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, true, this.callback); - }); - - describe("for a small update", function() { - it("should insert the update into a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates, true).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - return describe("for many small updates", function() { - beforeEach(function() { - this.newUpdates = (__range__(0, 2048, true).map((i) => ({ op: `op-${i}`, meta: `meta-${i}`, v: i}))); - return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, false, this.callback); - }); - - it("should append the initial updates to the existing pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(0, 512), false).should.equal(true); - }); - - it("should insert the first set remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(512, 1024), false).should.equal(true); - }); - - it("should insert the second set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1024, 1536), false).should.equal(true); - }); - - it("should insert the third set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1536, 2048), false).should.equal(true); - }); - - it("should insert the final set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(2048, 2049), false).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - - - - describe("with an existing pack as the last update", function() { - beforeEach(function() { - this.PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5); - this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4); - return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); - }); - - describe("for a small update", function() { - it("should append the update to the existing pack", function() { - return this.PackManager.appendUpdatesToExistingPack.calledWith(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false).should.equal(true); - }); - it("should not insert any new packs", function() { - return this.PackManager.insertUpdatesIntoNewPack.called.should.equal(false); - }); - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("for many small updates", function() { - beforeEach(function() { - this.newUpdates = (__range__(0, 2048, true).map((i) => ({ op: `op-${i}`, meta: `meta-${i}`, v: i}))); - return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); - }); - - it("should append the initial updates to the existing pack", function() { - return this.PackManager.appendUpdatesToExistingPack.calledWith(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates.slice(0, 510), false).should.equal(true); - }); - - it("should insert the first set remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(510, 1022), false).should.equal(true); - }); - - it("should insert the second set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1022, 1534), false).should.equal(true); - }); - - it("should insert the third set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1534, 2046), false).should.equal(true); - }); - - it("should insert the final set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(2046, 2049), false).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - return describe("for many big updates", function() { - beforeEach(function() { - const longString = (__range__(0, (0.75*this.PackManager.MAX_SIZE), true).map((j) => "a")).join(""); - this.newUpdates = ([0, 1, 2, 3, 4].map((i) => ({ op: `op-${i}-${longString}`, meta: `meta-${i}`, v: i}))); - return this.PackManager.insertCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); - }); - - it("should append the initial updates to the existing pack", function() { - return this.PackManager.appendUpdatesToExistingPack.calledWith(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates.slice(0, 1), false).should.equal(true); - }); - - it("should insert the first set remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(1, 2), false).should.equal(true); - }); - - it("should insert the second set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(2, 3), false).should.equal(true); - }); - - it("should insert the third set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(3, 4), false).should.equal(true); - }); - - it("should insert the final set of remaining updates as a new pack", function() { - return this.PackManager.insertUpdatesIntoNewPack.calledWith(this.project_id, this.doc_id, this.newUpdates.slice(4, 5), false).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - - describe("flushCompressedUpdates", function() { return describe("when there is no previous update", function() { - beforeEach(function() { - return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, null, this.newUpdates, true, this.callback); - }); - - return describe("for a small update that will expire", function() { - it("should insert the update into mongo", function() { - return this.db.docHistory.save.calledWithMatch({ - pack: this.newUpdates, - project_id: ObjectId(this.project_id), - doc_id: ObjectId(this.doc_id), - n: this.newUpdates.length, - v: this.newUpdates[0].v, - v_end: this.newUpdates[this.newUpdates.length-1].v - }).should.equal(true); - }); - - it("should set an expiry time in the future", function() { - return this.db.docHistory.save.calledWithMatch({ - expiresAt: new Date(Date.now() + (7 * 24 * 3600 * 1000)) - }).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); } - ); - - describe("when there is a recent previous update in mongo that expires", function() { - beforeEach(function() { - this.lastUpdate = { - _id: "12345", - pack: [ - { op: "op-1", meta: "meta-1", v: 1}, - { op: "op-2", meta: "meta-2", v: 2} - ], - n : 2, - sz : 100, - meta: {start_ts: Date.now() - (6 * 3600 * 1000)}, - expiresAt: new Date(Date.now()) - }; - - return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, true, this.callback); - }); - - return describe("for a small update that will expire", function() { - it("should append the update in mongo", function() { - return this.db.docHistory.findAndModify.calledWithMatch({ - query: {_id: this.lastUpdate._id}, - update: { $push: {"pack" : {$each: this.newUpdates}}, $set: {v_end: this.newUpdates[this.newUpdates.length-1].v}} - }).should.equal(true); - }); - - it("should set an expiry time in the future", function() { - return this.db.docHistory.findAndModify.calledWithMatch({ - update: {$set: {expiresAt: new Date(Date.now() + (7 * 24 * 3600 * 1000))}} - }).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - - - describe("when there is a recent previous update in mongo that expires", function() { - beforeEach(function() { - this.PackManager.updateIndex = sinon.stub().callsArg(2); - - this.lastUpdate = { - _id: "12345", - pack: [ - { op: "op-1", meta: "meta-1", v: 1}, - { op: "op-2", meta: "meta-2", v: 2} - ], - n : 2, - sz : 100, - meta: {start_ts: Date.now() - (6 * 3600 * 1000)}, - expiresAt: new Date(Date.now()) - }; - - return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, false, this.callback); - }); - - return describe("for a small update that will not expire", function() { - it("should insert the update into mongo", function() { - return this.db.docHistory.save.calledWithMatch({ - pack: this.newUpdates, - project_id: ObjectId(this.project_id), - doc_id: ObjectId(this.doc_id), - n: this.newUpdates.length, - v: this.newUpdates[0].v, - v_end: this.newUpdates[this.newUpdates.length-1].v - }).should.equal(true); - }); - - it("should not set any expiry time", function() { - return this.db.docHistory.save.neverCalledWithMatch(sinon.match.has("expiresAt")).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - - return describe("when there is an old previous update in mongo", function() { - beforeEach(function() { - this.lastUpdate = { - _id: "12345", - pack: [ - { op: "op-1", meta: "meta-1", v: 1}, - { op: "op-2", meta: "meta-2", v: 2} - ], - n : 2, - sz : 100, - meta: {start_ts: Date.now() - (30 * 24 * 3600 * 1000)}, - expiresAt: new Date(Date.now() - (30 * 24 * 3600 * 1000)) - }; - - return this.PackManager.flushCompressedUpdates(this.project_id, this.doc_id, this.lastUpdate, this.newUpdates, true, this.callback); - }); - - return describe("for a small update that will expire", function() { - it("should insert the update into mongo", function() { - return this.db.docHistory.save.calledWithMatch({ - pack: this.newUpdates, - project_id: ObjectId(this.project_id), - doc_id: ObjectId(this.doc_id), - n: this.newUpdates.length, - v: this.newUpdates[0].v, - v_end: this.newUpdates[this.newUpdates.length-1].v - }).should.equal(true); - }); - - it("should set an expiry time in the future", function() { - return this.db.docHistory.save.calledWithMatch({ - expiresAt: new Date(Date.now() + (7 * 24 * 3600 * 1000)) - }).should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - }); - - - describe("getOpsByVersionRange", function() {}); - - describe("loadPacksByVersionRange", function() {}); - - describe("fetchPacksIfNeeded", function() {}); - - describe("makeProjectIterator", function() {}); - - describe("getPackById", function() {}); - - describe("increaseTTL", function() {}); - - describe("getIndex", function() {}); - - describe("getPackFromIndex", function() {}); -// getLastPackFromIndex: -// getIndexWithKeys -// initialiseIndex -// updateIndex -// findCompletedPacks -// findUnindexedPacks -// insertPacksIntoIndexWithLock -// _insertPacksIntoIndex -// archivePack -// checkArchivedPack -// processOldPack -// updateIndexIfNeeded -// findUnarchivedPacks - - return describe("checkArchiveNotInProgress", function() { - - describe("when an archive is in progress", function() { - beforeEach(function() { - this.db.docHistoryIndex = - {findOne: sinon.stub().callsArgWith(2, null, {inS3:false})}; - return this.PackManager.checkArchiveNotInProgress(this.project_id, this.doc_id, this.pack_id, this.callback); - }); - it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - return it("should return an error", function() { - return this.callback.calledWith(sinon.match.has('message')).should.equal(true); - }); - }); - - describe("when an archive is completed", function() { - beforeEach(function() { - this.db.docHistoryIndex = - {findOne: sinon.stub().callsArgWith(2, null, {inS3:true})}; - return this.PackManager.checkArchiveNotInProgress(this.project_id, this.doc_id, this.pack_id, this.callback); - }); - it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - return it("should return an error", function() { - return this.callback.calledWith(sinon.match.has('message')).should.equal(true); - }); - }); - - return describe("when the archive has not started or completed", function() { - beforeEach(function() { - this.db.docHistoryIndex = - {findOne: sinon.stub().callsArgWith(2, null, {})}; - return this.PackManager.checkArchiveNotInProgress(this.project_id, this.doc_id, this.pack_id, this.callback); - }); - it("should call the callback with no error", function() { - return this.callback.called.should.equal(true); - }); - return it("should return with no error", function() { - return (typeof this.callback.lastCall.args[0]).should.equal('undefined'); - }); - }); - }); -}); - - // describe "setTTLOnArchivedPack", -> - // beforeEach -> - // @pack_id = "somepackid" - // @onedayinms = 86400000 - // @db.docHistory = - // findAndModify : sinon.stub().callsArgWith(1) - - // it "should set expires to 1 day", (done)-> - // #@PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) - // @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => - // args = @db.docHistory.findAndModify.args[0][0] - // args.query._id.should.equal @pack_id - // args.update['$set'].expiresAt.should.equal @onedayinms - // done() - - - // describe "_getOneDayInFutureWithRandomDelay", -> - // beforeEach -> - // @onedayinms = 86400000 - // @thirtyMins = 1000 * 60 * 30 - - // it "should give 1 day + 30 mins random time", (done)-> - // loops = 10000 - // while --loops > 0 - // randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) - // randomDelay.should.be.above(0) - // randomDelay.should.be.below(@thirtyMins + 1) - // done() - +const sinon = require('sinon') +const chai = require('chai') +const { assert } = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/PackManager.js' +const SandboxedModule = require('sandboxed-module') +const { ObjectId } = require('mongojs') +const bson = require('bson') +const BSON = new bson.BSONPure() +const _ = require('underscore') + +const tk = require('timekeeper') + +describe('PackManager', function() { + beforeEach(function() { + tk.freeze(new Date()) + this.PackManager = SandboxedModule.require(modulePath, { + requires: { + './mongojs': { db: (this.db = {}), ObjectId, BSON }, + './LockManager': {}, + './MongoAWS': {}, + 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, + 'metrics-sharelatex': { inc() {} }, + './ProjectIterator': require('../../../../app/js/ProjectIterator.js'), // Cache for speed + 'settings-sharelatex': { + redis: { lock: { key_schema: {} } } + } + } + }) + this.callback = sinon.stub() + this.doc_id = ObjectId().toString() + this.project_id = ObjectId().toString() + return (this.PackManager.MAX_COUNT = 512) + }) + + afterEach(function() { + return tk.reset() + }) + + describe('insertCompressedUpdates', function() { + beforeEach(function() { + this.lastUpdate = { + _id: '12345', + pack: [ + { op: 'op-1', meta: 'meta-1', v: 1 }, + { op: 'op-2', meta: 'meta-2', v: 2 } + ], + n: 2, + sz: 100 + } + this.newUpdates = [ + { op: 'op-3', meta: 'meta-3', v: 3 }, + { op: 'op-4', meta: 'meta-4', v: 4 } + ] + return (this.db.docHistory = { + save: sinon.stub().callsArg(1), + insert: sinon.stub().callsArg(1), + findAndModify: sinon.stub().callsArg(1) + }) + }) + + describe('with no last update', function() { + beforeEach(function() { + this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) + return this.PackManager.insertCompressedUpdates( + this.project_id, + this.doc_id, + null, + this.newUpdates, + true, + this.callback + ) + }) + + describe('for a small update', function() { + it('should insert the update into a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith(this.project_id, this.doc_id, this.newUpdates, true) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + return describe('for many small updates', function() { + beforeEach(function() { + this.newUpdates = __range__(0, 2048, true).map(i => ({ + op: `op-${i}`, + meta: `meta-${i}`, + v: i + })) + return this.PackManager.insertCompressedUpdates( + this.project_id, + this.doc_id, + null, + this.newUpdates, + false, + this.callback + ) + }) + + it('should append the initial updates to the existing pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(0, 512), + false + ) + .should.equal(true) + }) + + it('should insert the first set remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(512, 1024), + false + ) + .should.equal(true) + }) + + it('should insert the second set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(1024, 1536), + false + ) + .should.equal(true) + }) + + it('should insert the third set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(1536, 2048), + false + ) + .should.equal(true) + }) + + it('should insert the final set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(2048, 2049), + false + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + + describe('with an existing pack as the last update', function() { + beforeEach(function() { + this.PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5) + this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) + return this.PackManager.insertCompressedUpdates( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + false, + this.callback + ) + }) + + describe('for a small update', function() { + it('should append the update to the existing pack', function() { + return this.PackManager.appendUpdatesToExistingPack + .calledWith( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + false + ) + .should.equal(true) + }) + it('should not insert any new packs', function() { + return this.PackManager.insertUpdatesIntoNewPack.called.should.equal( + false + ) + }) + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('for many small updates', function() { + beforeEach(function() { + this.newUpdates = __range__(0, 2048, true).map(i => ({ + op: `op-${i}`, + meta: `meta-${i}`, + v: i + })) + return this.PackManager.insertCompressedUpdates( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + false, + this.callback + ) + }) + + it('should append the initial updates to the existing pack', function() { + return this.PackManager.appendUpdatesToExistingPack + .calledWith( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates.slice(0, 510), + false + ) + .should.equal(true) + }) + + it('should insert the first set remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(510, 1022), + false + ) + .should.equal(true) + }) + + it('should insert the second set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(1022, 1534), + false + ) + .should.equal(true) + }) + + it('should insert the third set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(1534, 2046), + false + ) + .should.equal(true) + }) + + it('should insert the final set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(2046, 2049), + false + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + return describe('for many big updates', function() { + beforeEach(function() { + const longString = __range__( + 0, + 0.75 * this.PackManager.MAX_SIZE, + true + ) + .map(j => 'a') + .join('') + this.newUpdates = [0, 1, 2, 3, 4].map(i => ({ + op: `op-${i}-${longString}`, + meta: `meta-${i}`, + v: i + })) + return this.PackManager.insertCompressedUpdates( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + false, + this.callback + ) + }) + + it('should append the initial updates to the existing pack', function() { + return this.PackManager.appendUpdatesToExistingPack + .calledWith( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates.slice(0, 1), + false + ) + .should.equal(true) + }) + + it('should insert the first set remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(1, 2), + false + ) + .should.equal(true) + }) + + it('should insert the second set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(2, 3), + false + ) + .should.equal(true) + }) + + it('should insert the third set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(3, 4), + false + ) + .should.equal(true) + }) + + it('should insert the final set of remaining updates as a new pack', function() { + return this.PackManager.insertUpdatesIntoNewPack + .calledWith( + this.project_id, + this.doc_id, + this.newUpdates.slice(4, 5), + false + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + + describe('flushCompressedUpdates', function() { + return describe('when there is no previous update', function() { + beforeEach(function() { + return this.PackManager.flushCompressedUpdates( + this.project_id, + this.doc_id, + null, + this.newUpdates, + true, + this.callback + ) + }) + + return describe('for a small update that will expire', function() { + it('should insert the update into mongo', function() { + return this.db.docHistory.save + .calledWithMatch({ + pack: this.newUpdates, + project_id: ObjectId(this.project_id), + doc_id: ObjectId(this.doc_id), + n: this.newUpdates.length, + v: this.newUpdates[0].v, + v_end: this.newUpdates[this.newUpdates.length - 1].v + }) + .should.equal(true) + }) + + it('should set an expiry time in the future', function() { + return this.db.docHistory.save + .calledWithMatch({ + expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) + }) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + }) + + describe('when there is a recent previous update in mongo that expires', function() { + beforeEach(function() { + this.lastUpdate = { + _id: '12345', + pack: [ + { op: 'op-1', meta: 'meta-1', v: 1 }, + { op: 'op-2', meta: 'meta-2', v: 2 } + ], + n: 2, + sz: 100, + meta: { start_ts: Date.now() - 6 * 3600 * 1000 }, + expiresAt: new Date(Date.now()) + } + + return this.PackManager.flushCompressedUpdates( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + true, + this.callback + ) + }) + + return describe('for a small update that will expire', function() { + it('should append the update in mongo', function() { + return this.db.docHistory.findAndModify + .calledWithMatch({ + query: { _id: this.lastUpdate._id }, + update: { + $push: { pack: { $each: this.newUpdates } }, + $set: { v_end: this.newUpdates[this.newUpdates.length - 1].v } + } + }) + .should.equal(true) + }) + + it('should set an expiry time in the future', function() { + return this.db.docHistory.findAndModify + .calledWithMatch({ + update: { + $set: { expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) } + } + }) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + + describe('when there is a recent previous update in mongo that expires', function() { + beforeEach(function() { + this.PackManager.updateIndex = sinon.stub().callsArg(2) + + this.lastUpdate = { + _id: '12345', + pack: [ + { op: 'op-1', meta: 'meta-1', v: 1 }, + { op: 'op-2', meta: 'meta-2', v: 2 } + ], + n: 2, + sz: 100, + meta: { start_ts: Date.now() - 6 * 3600 * 1000 }, + expiresAt: new Date(Date.now()) + } + + return this.PackManager.flushCompressedUpdates( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + false, + this.callback + ) + }) + + return describe('for a small update that will not expire', function() { + it('should insert the update into mongo', function() { + return this.db.docHistory.save + .calledWithMatch({ + pack: this.newUpdates, + project_id: ObjectId(this.project_id), + doc_id: ObjectId(this.doc_id), + n: this.newUpdates.length, + v: this.newUpdates[0].v, + v_end: this.newUpdates[this.newUpdates.length - 1].v + }) + .should.equal(true) + }) + + it('should not set any expiry time', function() { + return this.db.docHistory.save + .neverCalledWithMatch(sinon.match.has('expiresAt')) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + + return describe('when there is an old previous update in mongo', function() { + beforeEach(function() { + this.lastUpdate = { + _id: '12345', + pack: [ + { op: 'op-1', meta: 'meta-1', v: 1 }, + { op: 'op-2', meta: 'meta-2', v: 2 } + ], + n: 2, + sz: 100, + meta: { start_ts: Date.now() - 30 * 24 * 3600 * 1000 }, + expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000) + } + + return this.PackManager.flushCompressedUpdates( + this.project_id, + this.doc_id, + this.lastUpdate, + this.newUpdates, + true, + this.callback + ) + }) + + return describe('for a small update that will expire', function() { + it('should insert the update into mongo', function() { + return this.db.docHistory.save + .calledWithMatch({ + pack: this.newUpdates, + project_id: ObjectId(this.project_id), + doc_id: ObjectId(this.doc_id), + n: this.newUpdates.length, + v: this.newUpdates[0].v, + v_end: this.newUpdates[this.newUpdates.length - 1].v + }) + .should.equal(true) + }) + + it('should set an expiry time in the future', function() { + return this.db.docHistory.save + .calledWithMatch({ + expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) + }) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + }) + + describe('getOpsByVersionRange', function() {}) + + describe('loadPacksByVersionRange', function() {}) + + describe('fetchPacksIfNeeded', function() {}) + + describe('makeProjectIterator', function() {}) + + describe('getPackById', function() {}) + + describe('increaseTTL', function() {}) + + describe('getIndex', function() {}) + + describe('getPackFromIndex', function() {}) + // getLastPackFromIndex: + // getIndexWithKeys + // initialiseIndex + // updateIndex + // findCompletedPacks + // findUnindexedPacks + // insertPacksIntoIndexWithLock + // _insertPacksIntoIndex + // archivePack + // checkArchivedPack + // processOldPack + // updateIndexIfNeeded + // findUnarchivedPacks + + return describe('checkArchiveNotInProgress', function() { + describe('when an archive is in progress', function() { + beforeEach(function() { + this.db.docHistoryIndex = { + findOne: sinon.stub().callsArgWith(2, null, { inS3: false }) + } + return this.PackManager.checkArchiveNotInProgress( + this.project_id, + this.doc_id, + this.pack_id, + this.callback + ) + }) + it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + return it('should return an error', function() { + return this.callback + .calledWith(sinon.match.has('message')) + .should.equal(true) + }) + }) + + describe('when an archive is completed', function() { + beforeEach(function() { + this.db.docHistoryIndex = { + findOne: sinon.stub().callsArgWith(2, null, { inS3: true }) + } + return this.PackManager.checkArchiveNotInProgress( + this.project_id, + this.doc_id, + this.pack_id, + this.callback + ) + }) + it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + return it('should return an error', function() { + return this.callback + .calledWith(sinon.match.has('message')) + .should.equal(true) + }) + }) + + return describe('when the archive has not started or completed', function() { + beforeEach(function() { + this.db.docHistoryIndex = { + findOne: sinon.stub().callsArgWith(2, null, {}) + } + return this.PackManager.checkArchiveNotInProgress( + this.project_id, + this.doc_id, + this.pack_id, + this.callback + ) + }) + it('should call the callback with no error', function() { + return this.callback.called.should.equal(true) + }) + return it('should return with no error', function() { + return (typeof this.callback.lastCall.args[0]).should.equal('undefined') + }) + }) + }) +}) + +// describe "setTTLOnArchivedPack", -> +// beforeEach -> +// @pack_id = "somepackid" +// @onedayinms = 86400000 +// @db.docHistory = +// findAndModify : sinon.stub().callsArgWith(1) + +// it "should set expires to 1 day", (done)-> +// #@PackManager._getOneDayInFutureWithRandomDelay = sinon.stub().returns(@onedayinms) +// @PackManager.setTTLOnArchivedPack @project_id, @doc_id, @pack_id, => +// args = @db.docHistory.findAndModify.args[0][0] +// args.query._id.should.equal @pack_id +// args.update['$set'].expiresAt.should.equal @onedayinms +// done() + +// describe "_getOneDayInFutureWithRandomDelay", -> +// beforeEach -> +// @onedayinms = 86400000 +// @thirtyMins = 1000 * 60 * 30 + +// it "should give 1 day + 30 mins random time", (done)-> +// loops = 10000 +// while --loops > 0 +// randomDelay = @PackManager._getOneDayInFutureWithRandomDelay() - new Date(Date.now() + @onedayinms) +// randomDelay.should.be.above(0) +// randomDelay.should.be.below(@thirtyMins + 1) +// done() function __range__(left, right, inclusive) { - const range = []; - const ascending = left < right; - const end = !inclusive ? right : ascending ? right + 1 : right - 1; + const range = [] + const ascending = left < right + const end = !inclusive ? right : ascending ? right + 1 : right - 1 for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { - range.push(i); + range.push(i) } - return range; -} \ No newline at end of file + return range +} diff --git a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js index 5637ed6135..d05bf50814 100644 --- a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js @@ -11,118 +11,156 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/RedisManager.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/RedisManager.js' +const SandboxedModule = require('sandboxed-module') -describe("RedisManager", function() { - beforeEach(function() { - this.RedisManager = SandboxedModule.require(modulePath, { requires: { - "redis-sharelatex" : { - createClient: () => { return this.rclient = { - auth: sinon.stub(), - multi: () => this.rclient - }; } - }, - "settings-sharelatex": { - redis: { - history: { - key_schema: { - uncompressedHistoryOps({doc_id}) { return `UncompressedHistoryOps:${doc_id}`; }, - docsWithHistoryOps({project_id}) { return `DocsWithHistoryOps:${project_id}`; } - } - } - } - } - } - } - ); - this.doc_id = "doc-id-123"; - this.project_id = "project-id-123"; - this.batchSize = 100; - return this.callback = sinon.stub(); - }); +describe('RedisManager', function() { + beforeEach(function() { + this.RedisManager = SandboxedModule.require(modulePath, { + requires: { + 'redis-sharelatex': { + createClient: () => { + return (this.rclient = { + auth: sinon.stub(), + multi: () => this.rclient + }) + } + }, + 'settings-sharelatex': { + redis: { + history: { + key_schema: { + uncompressedHistoryOps({ doc_id }) { + return `UncompressedHistoryOps:${doc_id}` + }, + docsWithHistoryOps({ project_id }) { + return `DocsWithHistoryOps:${project_id}` + } + } + } + } + } + } + }) + this.doc_id = 'doc-id-123' + this.project_id = 'project-id-123' + this.batchSize = 100 + return (this.callback = sinon.stub()) + }) - describe("getOldestDocUpdates", function() { - beforeEach(function() { - this.rawUpdates = [ {v: 42, op: "mock-op-42"}, { v: 45, op: "mock-op-45" }]; - this.jsonUpdates = (Array.from(this.rawUpdates).map((update) => JSON.stringify(update))); - this.rclient.lrange = sinon.stub().callsArgWith(3, null, this.jsonUpdates); - return this.RedisManager.getOldestDocUpdates(this.doc_id, this.batchSize, this.callback); - }); + describe('getOldestDocUpdates', function() { + beforeEach(function() { + this.rawUpdates = [ + { v: 42, op: 'mock-op-42' }, + { v: 45, op: 'mock-op-45' } + ] + this.jsonUpdates = Array.from(this.rawUpdates).map(update => + JSON.stringify(update) + ) + this.rclient.lrange = sinon.stub().callsArgWith(3, null, this.jsonUpdates) + return this.RedisManager.getOldestDocUpdates( + this.doc_id, + this.batchSize, + this.callback + ) + }) - it("should read the updates from redis", function() { - return this.rclient.lrange - .calledWith(`UncompressedHistoryOps:${this.doc_id}`, 0, this.batchSize - 1) - .should.equal(true); - }); + it('should read the updates from redis', function() { + return this.rclient.lrange + .calledWith( + `UncompressedHistoryOps:${this.doc_id}`, + 0, + this.batchSize - 1 + ) + .should.equal(true) + }) - it("should call the callback with the unparsed ops", function() { - return this.callback.calledWith(null, this.jsonUpdates).should.equal(true); - }); + it('should call the callback with the unparsed ops', function() { + return this.callback.calledWith(null, this.jsonUpdates).should.equal(true) + }) + describe('expandDocUpdates', function() { + beforeEach(function() { + return this.RedisManager.expandDocUpdates( + this.jsonUpdates, + this.callback + ) + }) - describe("expandDocUpdates", function() { - beforeEach(function() { - return this.RedisManager.expandDocUpdates(this.jsonUpdates, this.callback); - }); + return it('should call the callback with the parsed ops', function() { + return this.callback + .calledWith(null, this.rawUpdates) + .should.equal(true) + }) + }) - return it("should call the callback with the parsed ops", function() { - return this.callback.calledWith(null, this.rawUpdates).should.equal(true); - }); - }); + return describe('deleteAppliedDocUpdates', function() { + beforeEach(function() { + this.rclient.lrem = sinon.stub() + this.rclient.srem = sinon.stub() + this.rclient.exec = sinon.stub().callsArgWith(0) + return this.RedisManager.deleteAppliedDocUpdates( + this.project_id, + this.doc_id, + this.jsonUpdates, + this.callback + ) + }) + it('should delete the first update from redis', function() { + return this.rclient.lrem + .calledWith( + `UncompressedHistoryOps:${this.doc_id}`, + 1, + this.jsonUpdates[0] + ) + .should.equal(true) + }) - return describe("deleteAppliedDocUpdates", function() { - beforeEach(function() { - this.rclient.lrem = sinon.stub(); - this.rclient.srem = sinon.stub(); - this.rclient.exec = sinon.stub().callsArgWith(0); - return this.RedisManager.deleteAppliedDocUpdates(this.project_id, this.doc_id, this.jsonUpdates, this.callback); - }); + it('should delete the second update from redis', function() { + return this.rclient.lrem + .calledWith( + `UncompressedHistoryOps:${this.doc_id}`, + 1, + this.jsonUpdates[1] + ) + .should.equal(true) + }) - it("should delete the first update from redis", function() { - return this.rclient.lrem - .calledWith(`UncompressedHistoryOps:${this.doc_id}`, 1, this.jsonUpdates[0]) - .should.equal(true); - }); + it('should delete the doc from the set of docs with history ops', function() { + return this.rclient.srem + .calledWith(`DocsWithHistoryOps:${this.project_id}`, this.doc_id) + .should.equal(true) + }) - it("should delete the second update from redis", function() { - return this.rclient.lrem - .calledWith(`UncompressedHistoryOps:${this.doc_id}`, 1, this.jsonUpdates[1]) - .should.equal(true); - }); + return it('should call the callback ', function() { + return this.callback.called.should.equal(true) + }) + }) + }) - it("should delete the doc from the set of docs with history ops", function() { - return this.rclient.srem - .calledWith(`DocsWithHistoryOps:${this.project_id}`, this.doc_id) - .should.equal(true); - }); + return describe('getDocIdsWithHistoryOps', function() { + beforeEach(function() { + this.doc_ids = ['mock-id-1', 'mock-id-2'] + this.rclient.smembers = sinon.stub().callsArgWith(1, null, this.doc_ids) + return this.RedisManager.getDocIdsWithHistoryOps( + this.project_id, + this.callback + ) + }) - return it("should call the callback ", function() { - return this.callback.called.should.equal(true); - }); - }); - }); + it('should read the doc_ids from redis', function() { + return this.rclient.smembers + .calledWith(`DocsWithHistoryOps:${this.project_id}`) + .should.equal(true) + }) - return describe("getDocIdsWithHistoryOps", function() { - beforeEach(function() { - this.doc_ids = ["mock-id-1", "mock-id-2"]; - this.rclient.smembers = sinon.stub().callsArgWith(1, null, this.doc_ids); - return this.RedisManager.getDocIdsWithHistoryOps(this.project_id, this.callback); - }); - - it("should read the doc_ids from redis", function() { - return this.rclient.smembers - .calledWith(`DocsWithHistoryOps:${this.project_id}`) - .should.equal(true); - }); - - return it("should call the callback with the doc_ids", function() { - return this.callback.calledWith(null, this.doc_ids).should.equal(true); - }); - }); -}); + return it('should call the callback with the doc_ids', function() { + return this.callback.calledWith(null, this.doc_ids).should.equal(true) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js index dfa2c07f05..86d2f92f0c 100644 --- a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js +++ b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js @@ -9,51 +9,62 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/RestoreManager.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/RestoreManager.js' +const SandboxedModule = require('sandboxed-module') -describe("RestoreManager", function() { - beforeEach(function() { - this.RestoreManager = SandboxedModule.require(modulePath, { requires: { - "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), - "./DocumentUpdaterManager": (this.DocumentUpdaterManager = {}), - "./DiffManager": (this.DiffManager = {}) - } - }); - this.callback = sinon.stub(); - this.project_id = "mock-project-id"; - this.doc_id = "mock-doc-id"; - this.user_id = "mock-user-id"; - return this.version = 42; - }); +describe('RestoreManager', function() { + beforeEach(function() { + this.RestoreManager = SandboxedModule.require(modulePath, { + requires: { + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + error: sinon.stub() + }), + './DocumentUpdaterManager': (this.DocumentUpdaterManager = {}), + './DiffManager': (this.DiffManager = {}) + } + }) + this.callback = sinon.stub() + this.project_id = 'mock-project-id' + this.doc_id = 'mock-doc-id' + this.user_id = 'mock-user-id' + return (this.version = 42) + }) - return describe("restoreToBeforeVersion", function() { - beforeEach(function() { - this.content = "mock content"; - this.DocumentUpdaterManager.setDocument = sinon.stub().callsArg(4); - this.DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, this.content); - return this.RestoreManager.restoreToBeforeVersion(this.project_id, this.doc_id, this.version, this.user_id, this.callback); - }); + return describe('restoreToBeforeVersion', function() { + beforeEach(function() { + this.content = 'mock content' + this.DocumentUpdaterManager.setDocument = sinon.stub().callsArg(4) + this.DiffManager.getDocumentBeforeVersion = sinon + .stub() + .callsArgWith(3, null, this.content) + return this.RestoreManager.restoreToBeforeVersion( + this.project_id, + this.doc_id, + this.version, + this.user_id, + this.callback + ) + }) - it("should get the content before the requested version", function() { - return this.DiffManager.getDocumentBeforeVersion - .calledWith(this.project_id, this.doc_id, this.version) - .should.equal(true); - }); + it('should get the content before the requested version', function() { + return this.DiffManager.getDocumentBeforeVersion + .calledWith(this.project_id, this.doc_id, this.version) + .should.equal(true) + }) - it("should set the document in the document updater", function() { - return this.DocumentUpdaterManager.setDocument - .calledWith(this.project_id, this.doc_id, this.content, this.user_id) - .should.equal(true); - }); + it('should set the document in the document updater', function() { + return this.DocumentUpdaterManager.setDocument + .calledWith(this.project_id, this.doc_id, this.content, this.user_id) + .should.equal(true) + }) - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); -}); - \ No newline at end of file + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js index 1a12ec78d2..9a88db6357 100644 --- a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js +++ b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js @@ -9,584 +9,842 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/UpdateCompressor.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/UpdateCompressor.js' +const SandboxedModule = require('sandboxed-module') -const bigstring = (__range__(0, 2*1024*1024, true).map((i) => "a")).join(""); -const mediumstring = (__range__(0, 1024*1024, true).map((j) => "a")).join(""); +const bigstring = __range__(0, 2 * 1024 * 1024, true) + .map(i => 'a') + .join('') +const mediumstring = __range__(0, 1024 * 1024, true) + .map(j => 'a') + .join('') -describe("UpdateCompressor", function() { - beforeEach(function() { - this.UpdateCompressor = SandboxedModule.require(modulePath, { requires: { - "../lib/diff_match_patch": require("../../../../app/lib/diff_match_patch") - } - } - ); - this.user_id = "user-id-1"; - this.other_user_id = "user-id-2"; - this.ts1 = Date.now(); - return this.ts2 = Date.now() + 1000; - }); +describe('UpdateCompressor', function() { + beforeEach(function() { + this.UpdateCompressor = SandboxedModule.require(modulePath, { + requires: { + '../lib/diff_match_patch': require('../../../../app/lib/diff_match_patch') + } + }) + this.user_id = 'user-id-1' + this.other_user_id = 'user-id-2' + this.ts1 = Date.now() + return (this.ts2 = Date.now() + 1000) + }) - describe("convertToSingleOpUpdates", function() { - it("should split grouped updates into individual updates", function() { - return expect(this.UpdateCompressor.convertToSingleOpUpdates([{ - op: [ (this.op1 = { p: 0, i: "Foo" }), (this.op2 = { p: 6, i: "bar"}) ], - meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: [ (this.op3 = { p: 10, i: "baz" }) ], - meta: { ts: this.ts2, user_id: this.other_user_id }, - v: 43 - }])) - .to.deep.equal([{ - op: this.op1, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: this.op2, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: this.op3, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.other_user_id }, - v: 43 - }]); - }); + describe('convertToSingleOpUpdates', function() { + it('should split grouped updates into individual updates', function() { + return expect( + this.UpdateCompressor.convertToSingleOpUpdates([ + { + op: [ + (this.op1 = { p: 0, i: 'Foo' }), + (this.op2 = { p: 6, i: 'bar' }) + ], + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: [(this.op3 = { p: 10, i: 'baz' })], + meta: { ts: this.ts2, user_id: this.other_user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: this.op1, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: this.op2, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: this.op3, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.other_user_id + }, + v: 43 + } + ]) + }) - it("should return no-op updates when the op list is empty", function() { - return expect(this.UpdateCompressor.convertToSingleOpUpdates([{ - op: [], - meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 - }])) - .to.deep.equal([{ - op: this.UpdateCompressor.NOOP, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }]); - }); + it('should return no-op updates when the op list is empty', function() { + return expect( + this.UpdateCompressor.convertToSingleOpUpdates([ + { + op: [], + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + } + ]) + ).to.deep.equal([ + { + op: this.UpdateCompressor.NOOP, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + } + ]) + }) - return it("should ignore comment ops", function() { - return expect(this.UpdateCompressor.convertToSingleOpUpdates([{ - op: [ (this.op1 = { p: 0, i: "Foo" }), (this.op2 = { p: 9, c: "baz"}), (this.op3 = { p: 6, i: "bar"}) ], - meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 - }])) - .to.deep.equal([{ - op: this.op1, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: this.op3, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }]); - }); -}); + return it('should ignore comment ops', function() { + return expect( + this.UpdateCompressor.convertToSingleOpUpdates([ + { + op: [ + (this.op1 = { p: 0, i: 'Foo' }), + (this.op2 = { p: 9, c: 'baz' }), + (this.op3 = { p: 6, i: 'bar' }) + ], + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + } + ]) + ).to.deep.equal([ + { + op: this.op1, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: this.op3, + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + } + ]) + }) + }) - describe("concatUpdatesWithSameVersion", function() { - it("should concat updates with the same version", function() { - return expect(this.UpdateCompressor.concatUpdatesWithSameVersion([{ - op: (this.op1 = { p: 0, i: "Foo" }), - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: (this.op2 = { p: 6, i: "bar" }), - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: (this.op3 = { p: 10, i: "baz" }), - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.other_user_id }, - v: 43 - }])) - .to.deep.equal([{ - op: [ this.op1, this.op2 ], - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }, { - op: [ this.op3 ], - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.other_user_id }, - v: 43 - }]); - }); + describe('concatUpdatesWithSameVersion', function() { + it('should concat updates with the same version', function() { + return expect( + this.UpdateCompressor.concatUpdatesWithSameVersion([ + { + op: (this.op1 = { p: 0, i: 'Foo' }), + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: (this.op2 = { p: 6, i: 'bar' }), + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: (this.op3 = { p: 10, i: 'baz' }), + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.other_user_id + }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: [this.op1, this.op2], + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: [this.op3], + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.other_user_id + }, + v: 43 + } + ]) + }) - return it("should turn a noop into an empty op", function() { - return expect(this.UpdateCompressor.concatUpdatesWithSameVersion([{ - op: this.UpdateCompressor.NOOP, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }])) - .to.deep.equal([{ - op: [], - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - }]); - }); -}); + return it('should turn a noop into an empty op', function() { + return expect( + this.UpdateCompressor.concatUpdatesWithSameVersion([ + { + op: this.UpdateCompressor.NOOP, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + } + ]) + ).to.deep.equal([ + { + op: [], + meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, + v: 42 + } + ]) + }) + }) - describe("compress", function() { - describe("insert - insert", function() { - it("should append one insert to the other", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, i: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "foobar" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + describe('compress', function() { + describe('insert - insert', function() { + it('should append one insert to the other', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 6, i: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'foobar' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - it("should insert one insert inside the other", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 5, i: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "fobaro" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + it('should insert one insert inside the other', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 5, i: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'fobaro' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - it("should not append separated inserts", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 9, i: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "foo" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 9, i: "bar" }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + it('should not append separated inserts', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 9, i: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'foo' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 9, i: 'bar' }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - it("should not append inserts that are too big (second op)", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, i: bigstring }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "foo" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, i: bigstring }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + it('should not append inserts that are too big (second op)', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 6, i: bigstring }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'foo' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 6, i: bigstring }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - it("should not append inserts that are too big (first op)", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: bigstring }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3 + bigstring.length, i: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: bigstring }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3 + bigstring.length, i: "bar" }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + it('should not append inserts that are too big (first op)', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: bigstring }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 3 + bigstring.length, i: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: bigstring }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 3 + bigstring.length, i: 'bar' }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - return it("should not append inserts that are too big (first and second op)", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: mediumstring }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3 + mediumstring.length, i: mediumstring }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: mediumstring }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3 + mediumstring.length, i: mediumstring }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - }); + return it('should not append inserts that are too big (first and second op)', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: mediumstring }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 3 + mediumstring.length, i: mediumstring }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: mediumstring }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 3 + mediumstring.length, i: mediumstring }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) + describe('delete - delete', function() { + it('should append one delete to the other', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, d: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 3, d: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, d: 'foobar' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - describe("delete - delete", function() { - it("should append one delete to the other", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, d: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3, d: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, d: "foobar" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - - it("should insert one delete inside the other", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, d: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 1, d: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 1, d: "bafoor" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - - return it("should not append separated deletes", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, d: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 9, d: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, d: "foo" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 9, d: "bar" }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - }); + it('should insert one delete inside the other', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, d: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 1, d: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 1, d: 'bafoor' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - describe("insert - delete", function() { - it("should undo a previous insert", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 5, d: "o" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "fo" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - - it("should remove part of an insert from the middle", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "fobaro" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 5, d: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "foo" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + return it('should not append separated deletes', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, d: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 9, d: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, d: 'foo' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 9, d: 'bar' }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) - it("should cancel out two opposite updates", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3, d: "foo" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - } - ]); - }); - - it("should not combine separated updates", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foo" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 9, d: "bar" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "foo" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 9, d: "bar" }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); + describe('insert - delete', function() { + it('should undo a previous insert', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 5, d: 'o' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'fo' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - return it("should not combine updates with overlap beyond the end", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, i: "foobar" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, d: "bardle" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "foobar" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, d: "bardle" }, - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - }); - - describe("delete - insert", function() { - it("should do a diff of the content", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, d: "one two three four five six seven eight" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3, i: "one 2 three four five six seven eight" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 7, d: "two" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }, { - op: { p: 7, i: "2" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - - return it("should return a no-op if the delete and insert are the same", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: { p: 3, d: "one two three four five six seven eight" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 3, i: "one two three four five six seven eight" }, - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: { p: 3, i: "" }, - meta: { start_ts: this.ts1, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); - }); + it('should remove part of an insert from the middle', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'fobaro' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 5, d: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'foo' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - describe("noop - insert", function() { return it("should leave them untouched", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: this.UpdateCompressor.NOOP, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, i: "bar" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: this.UpdateCompressor.NOOP, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, i: "bar" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 43 - }]); - }); } - ); + it('should cancel out two opposite updates', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 3, d: 'foo' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: '' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - return describe("noop - delete", function() { return it("should leave them untouched", function() { - return expect(this.UpdateCompressor.compressUpdates([{ - op: this.UpdateCompressor.NOOP, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, d: "bar" }, - meta: { ts: this.ts1, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: this.UpdateCompressor.NOOP, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, { - op: { p: 6, d: "bar" }, - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 43 - }]); - }); } - ); -}); + it('should not combine separated updates', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foo' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 9, d: 'bar' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'foo' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 9, d: 'bar' }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) - return describe("compressRawUpdates", function() { return describe("merging in-place with an array op", function() { return it("should not change the existing last updates", function() { - return expect(this.UpdateCompressor.compressRawUpdates({ - op: [ {"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ], - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - }, [{ - op: [{ p: 1006, i: "WORLD" }], - meta: { ts: this.ts2, user_id: this.user_id - }, - v: 43 - }])) - .to.deep.equal([{ - op: [{"p":1000,"d":"hello"}, {"p":1000,"i":"HELLO()"} ], - meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id - }, - v: 42 - },{ - op: [{"p":1006,"i":"WORLD"}], - meta: { start_ts: this.ts2, end_ts: this.ts2, user_id: this.user_id - }, - v: 43 - }]); - }); } - ); } -); -}); + return it('should not combine updates with overlap beyond the end', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, i: 'foobar' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 6, d: 'bardle' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: 'foobar' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 6, d: 'bardle' }, + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) + + describe('delete - insert', function() { + it('should do a diff of the content', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, d: 'one two three four five six seven eight' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 3, i: 'one 2 three four five six seven eight' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 7, d: 'two' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + }, + { + op: { p: 7, i: '2' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + + return it('should return a no-op if the delete and insert are the same', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: { p: 3, d: 'one two three four five six seven eight' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 3, i: 'one two three four five six seven eight' }, + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: { p: 3, i: '' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) + + describe('noop - insert', function() { + return it('should leave them untouched', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: this.UpdateCompressor.NOOP, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 6, i: 'bar' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: this.UpdateCompressor.NOOP, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 6, i: 'bar' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) + + return describe('noop - delete', function() { + return it('should leave them untouched', function() { + return expect( + this.UpdateCompressor.compressUpdates([ + { + op: this.UpdateCompressor.NOOP, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 42 + }, + { + op: { p: 6, d: 'bar' }, + meta: { ts: this.ts1, user_id: this.user_id }, + v: 43 + } + ]) + ).to.deep.equal([ + { + op: this.UpdateCompressor.NOOP, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: { p: 6, d: 'bar' }, + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) + }) + + return describe('compressRawUpdates', function() { + return describe('merging in-place with an array op', function() { + return it('should not change the existing last updates', function() { + return expect( + this.UpdateCompressor.compressRawUpdates( + { + op: [ + { p: 1000, d: 'hello' }, + { p: 1000, i: 'HELLO()' } + ], + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + [ + { + op: [{ p: 1006, i: 'WORLD' }], + meta: { ts: this.ts2, user_id: this.user_id }, + v: 43 + } + ] + ) + ).to.deep.equal([ + { + op: [ + { p: 1000, d: 'hello' }, + { p: 1000, i: 'HELLO()' } + ], + meta: { + start_ts: this.ts1, + end_ts: this.ts1, + user_id: this.user_id + }, + v: 42 + }, + { + op: [{ p: 1006, i: 'WORLD' }], + meta: { + start_ts: this.ts2, + end_ts: this.ts2, + user_id: this.user_id + }, + v: 43 + } + ]) + }) + }) + }) +}) function __range__(left, right, inclusive) { - const range = []; - const ascending = left < right; - const end = !inclusive ? right : ascending ? right + 1 : right - 1; + const range = [] + const ascending = left < right + const end = !inclusive ? right : ascending ? right + 1 : right - 1 for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { - range.push(i); + range.push(i) } - return range; -} \ No newline at end of file + return range +} diff --git a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js index 83d1632ada..1a4e0f7861 100644 --- a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js +++ b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js @@ -9,157 +9,180 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/UpdateTrimmer.js"; -const SandboxedModule = require('sandboxed-module'); -const tk = require("timekeeper"); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/UpdateTrimmer.js' +const SandboxedModule = require('sandboxed-module') +const tk = require('timekeeper') -describe("UpdateTrimmer", function() { - beforeEach(function() { - this.now = new Date(); - tk.freeze(this.now); +describe('UpdateTrimmer', function() { + beforeEach(function() { + this.now = new Date() + tk.freeze(this.now) - this.UpdateTrimmer = SandboxedModule.require(modulePath, { requires: { - "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), - "./WebApiManager": (this.WebApiManager = {}), - "./MongoManager": (this.MongoManager = {}) - } - }); + this.UpdateTrimmer = SandboxedModule.require(modulePath, { + requires: { + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + error: sinon.stub() + }), + './WebApiManager': (this.WebApiManager = {}), + './MongoManager': (this.MongoManager = {}) + } + }) - this.callback = sinon.stub(); - return this.project_id = "mock-project-id"; - }); + this.callback = sinon.stub() + return (this.project_id = 'mock-project-id') + }) - afterEach(function() { return tk.reset(); }); + afterEach(function() { + return tk.reset() + }) - return describe("shouldTrimUpdates", function() { - beforeEach(function() { - this.metadata = {}; - this.details = - {features: {}}; - this.MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, this.metadata); - this.MongoManager.setProjectMetaData = sinon.stub().callsArgWith(2); - this.MongoManager.upgradeHistory = sinon.stub().callsArgWith(1); - return this.WebApiManager.getProjectDetails = sinon.stub().callsArgWith(1, null, this.details); - }); + return describe('shouldTrimUpdates', function() { + beforeEach(function() { + this.metadata = {} + this.details = { features: {} } + this.MongoManager.getProjectMetaData = sinon + .stub() + .callsArgWith(1, null, this.metadata) + this.MongoManager.setProjectMetaData = sinon.stub().callsArgWith(2) + this.MongoManager.upgradeHistory = sinon.stub().callsArgWith(1) + return (this.WebApiManager.getProjectDetails = sinon + .stub() + .callsArgWith(1, null, this.details)) + }) - describe("with preserveHistory set in the project meta data", function() { - beforeEach(function() { - this.metadata.preserveHistory = true; - return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); - }); + describe('with preserveHistory set in the project meta data', function() { + beforeEach(function() { + this.metadata.preserveHistory = true + return this.UpdateTrimmer.shouldTrimUpdates( + this.project_id, + this.callback + ) + }) - it("should look up the meta data", function() { - return this.MongoManager.getProjectMetaData - .calledWith(this.project_id) - .should.equal(true); - }); + it('should look up the meta data', function() { + return this.MongoManager.getProjectMetaData + .calledWith(this.project_id) + .should.equal(true) + }) - it("should not look up the project details", function() { - return this.WebApiManager.getProjectDetails - .called - .should.equal(false); - }); + it('should not look up the project details', function() { + return this.WebApiManager.getProjectDetails.called.should.equal(false) + }) - return it("should return false", function() { - return this.callback.calledWith(null, false).should.equal(true); - }); - }); + return it('should return false', function() { + return this.callback.calledWith(null, false).should.equal(true) + }) + }) - describe("without preserveHistory set in the project meta data", function() { - beforeEach(function() { - return this.metadata.preserveHistory = false; - }); + describe('without preserveHistory set in the project meta data', function() { + beforeEach(function() { + return (this.metadata.preserveHistory = false) + }) - describe("when the project has the versioning feature", function() { - beforeEach(function() { - this.details.features.versioning = true; - return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); - }); + describe('when the project has the versioning feature', function() { + beforeEach(function() { + this.details.features.versioning = true + return this.UpdateTrimmer.shouldTrimUpdates( + this.project_id, + this.callback + ) + }) - it("should look up the meta data", function() { - return this.MongoManager.getProjectMetaData - .calledWith(this.project_id) - .should.equal(true); - }); + it('should look up the meta data', function() { + return this.MongoManager.getProjectMetaData + .calledWith(this.project_id) + .should.equal(true) + }) - it("should look up the project details", function() { - return this.WebApiManager.getProjectDetails - .calledWith(this.project_id) - .should.equal(true); - }); + it('should look up the project details', function() { + return this.WebApiManager.getProjectDetails + .calledWith(this.project_id) + .should.equal(true) + }) - it("should insert preserveHistory into the metadata", function() { - return this.MongoManager.setProjectMetaData - .calledWith(this.project_id, {preserveHistory: true}) - .should.equal(true); - }); + it('should insert preserveHistory into the metadata', function() { + return this.MongoManager.setProjectMetaData + .calledWith(this.project_id, { preserveHistory: true }) + .should.equal(true) + }) - it("should upgrade any existing history", function() { - return this.MongoManager.upgradeHistory - .calledWith(this.project_id) - .should.equal(true); - }); + it('should upgrade any existing history', function() { + return this.MongoManager.upgradeHistory + .calledWith(this.project_id) + .should.equal(true) + }) - return it("should return false", function() { - return this.callback.calledWith(null, false).should.equal(true); - }); - }); + return it('should return false', function() { + return this.callback.calledWith(null, false).should.equal(true) + }) + }) - return describe("when the project does not have the versioning feature", function() { - beforeEach(function() { - this.details.features.versioning = false; - return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); - }); + return describe('when the project does not have the versioning feature', function() { + beforeEach(function() { + this.details.features.versioning = false + return this.UpdateTrimmer.shouldTrimUpdates( + this.project_id, + this.callback + ) + }) - return it("should return true", function() { - return this.callback.calledWith(null, true).should.equal(true); - }); - }); - }); + return it('should return true', function() { + return this.callback.calledWith(null, true).should.equal(true) + }) + }) + }) - return describe("without any meta data", function() { - beforeEach(function() { - return this.MongoManager.getProjectMetaData = sinon.stub().callsArgWith(1, null, null); - }); + return describe('without any meta data', function() { + beforeEach(function() { + return (this.MongoManager.getProjectMetaData = sinon + .stub() + .callsArgWith(1, null, null)) + }) - describe("when the project has the versioning feature", function() { - beforeEach(function() { - this.details.features.versioning = true; - return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); - }); + describe('when the project has the versioning feature', function() { + beforeEach(function() { + this.details.features.versioning = true + return this.UpdateTrimmer.shouldTrimUpdates( + this.project_id, + this.callback + ) + }) - it("should insert preserveHistory into the metadata", function() { - return this.MongoManager.setProjectMetaData - .calledWith(this.project_id, {preserveHistory: true}) - .should.equal(true); - }); + it('should insert preserveHistory into the metadata', function() { + return this.MongoManager.setProjectMetaData + .calledWith(this.project_id, { preserveHistory: true }) + .should.equal(true) + }) - it("should upgrade any existing history", function() { - return this.MongoManager.upgradeHistory - .calledWith(this.project_id) - .should.equal(true); - }); + it('should upgrade any existing history', function() { + return this.MongoManager.upgradeHistory + .calledWith(this.project_id) + .should.equal(true) + }) - return it("should return false", function() { - return this.callback.calledWith(null, false).should.equal(true); - }); - }); + return it('should return false', function() { + return this.callback.calledWith(null, false).should.equal(true) + }) + }) - return describe("when the project does not have the versioning feature", function() { - beforeEach(function() { - this.details.features.versioning = false; - return this.UpdateTrimmer.shouldTrimUpdates(this.project_id, this.callback); - }); - - return it("should return true", function() { - return this.callback.calledWith(null, true).should.equal(true); - }); - }); - }); - }); -}); + return describe('when the project does not have the versioning feature', function() { + beforeEach(function() { + this.details.features.versioning = false + return this.UpdateTrimmer.shouldTrimUpdates( + this.project_id, + this.callback + ) + }) + return it('should return true', function() { + return this.callback.calledWith(null, true).should.equal(true) + }) + }) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index 2ac748c8f6..2891d1d4ee 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -13,1009 +13,1323 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/UpdatesManager.js"; -const SandboxedModule = require('sandboxed-module'); - -describe("UpdatesManager", function() { - beforeEach(function() { - this.UpdatesManager = SandboxedModule.require(modulePath, { requires: { - "./UpdateCompressor": (this.UpdateCompressor = {}), - "./MongoManager" : (this.MongoManager = {}), - "./PackManager" : (this.PackManager = {}), - "./RedisManager" : (this.RedisManager = {}), - "./LockManager" : (this.LockManager = {}), - "./WebApiManager": (this.WebApiManager = {}), - "./UpdateTrimmer": (this.UpdateTrimmer = {}), - "./DocArchiveManager": (this.DocArchiveManager = {}), - "logger-sharelatex": { log: sinon.stub(), error: sinon.stub() }, - "settings-sharelatex": { - redis: { lock: { key_schema: { - historyLock({doc_id}) { return `HistoryLock:${doc_id}`; } - } - } - } - } - } - } - ); - this.doc_id = "doc-id-123"; - this.project_id = "project-id-123"; - this.callback = sinon.stub(); - return this.temporary = "temp-mock"; - }); - - describe("compressAndSaveRawUpdates", function() { - describe("when there are no raw ops", function() { - beforeEach(function() { - this.MongoManager.peekLastCompressedUpdate = sinon.stub(); - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, [], this.temporary, this.callback); - }); - - it("should not need to access the database", function() { - return this.MongoManager.peekLastCompressedUpdate.called.should.equal(false); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("when there is no compressed history to begin with", function() { - beforeEach(function() { - this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; - this.compressedUpdates = [ { v: 13, op: "compressed-op-12" } ]; - - this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null); - this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5); - this.UpdateCompressor.compressRawUpdates = sinon.stub().returns(this.compressedUpdates); - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - it("should look at the last compressed op", function() { - return this.MongoManager.peekLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); - - it("should save the compressed ops as a pack", function() { - return this.PackManager.insertCompressedUpdates - .calledWith(this.project_id, this.doc_id, null, this.compressedUpdates, this.temporary) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("when the raw ops need appending to existing history", function() { - beforeEach(function() { - this.lastCompressedUpdate = { v: 11, op: "compressed-op-11" }; - this.compressedUpdates = [ { v: 12, op: "compressed-op-11+12" }, { v: 13, op: "compressed-op-12" } ]; - - this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, this.lastCompressedUpdate, this.lastCompressedUpdate.v); - this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5); - return this.UpdateCompressor.compressRawUpdates = sinon.stub().returns(this.compressedUpdates); - }); - - describe("when the raw ops start where the existing history ends", function() { - beforeEach(function() { - this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - it("should look at the last compressed op", function() { - return this.MongoManager.peekLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); - - it("should compress the raw ops", function() { - return this.UpdateCompressor.compressRawUpdates - .calledWith(null, this.rawUpdates) - .should.equal(true); - }); - - it("should save the new compressed ops into a pack", function() { - return this.PackManager.insertCompressedUpdates - .calledWith(this.project_id, this.doc_id, this.lastCompressedUpdate, this.compressedUpdates, this.temporary) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("when the raw ops start where the existing history ends and the history is in a pack", function() { - beforeEach(function() { - this.lastCompressedUpdate = {pack: [{ v: 11, op: "compressed-op-11" }], v:11}; - this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; - this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, this.lastCompressedUpdate, this.lastCompressedUpdate.v); - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - it("should look at the last compressed op", function() { - return this.MongoManager.peekLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); - - it("should compress the raw ops", function() { - return this.UpdateCompressor.compressRawUpdates - .calledWith(null, this.rawUpdates) - .should.equal(true); - }); - - it("should save the new compressed ops into a pack", function() { - return this.PackManager.insertCompressedUpdates - .calledWith(this.project_id, this.doc_id, this.lastCompressedUpdate, this.compressedUpdates, this.temporary) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("when some raw ops are passed that have already been compressed", function() { - beforeEach(function() { - this.rawUpdates = [{ v: 10, op: "mock-op-10" }, { v: 11, op: "mock-op-11"}, { v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; - - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - return it("should only compress the more recent raw ops", function() { - return this.UpdateCompressor.compressRawUpdates - .calledWith(null, this.rawUpdates.slice(-2)) - .should.equal(true); - }); - }); - - describe("when the raw ops do not follow from the last compressed op version", function() { - beforeEach(function() { - this.rawUpdates = [{ v: 13, op: "mock-op-13" }]; - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - it("should call the callback with an error", function() { - return this.callback - .calledWith(sinon.match.has('message', "Tried to apply raw op at version 13 to last compressed update with version 11 from unknown time")) - .should.equal(true); - }); - - return it("should not insert any update into mongo", function() { - return this.PackManager.insertCompressedUpdates.called.should.equal(false); - }); - }); - - return describe("when the raw ops are out of order", function() { - beforeEach(function() { - this.rawUpdates = [{ v: 13, op: "mock-op-13" }, { v: 12, op: "mock-op-12" }]; - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - it("should call the callback with an error", function() { - return this.callback - .calledWith(sinon.match.has('message')) - .should.equal(true); - }); - - return it("should not insert any update into mongo", function() { - return this.PackManager.insertCompressedUpdates.called.should.equal(false); - }); - }); - }); - - - return describe("when the raw ops need appending to existing history which is in S3", function() { - beforeEach(function() { - this.lastCompressedUpdate = null; - this.lastVersion = 11; - this.compressedUpdates = [ { v: 13, op: "compressed-op-12" } ]; - - this.MongoManager.peekLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null, this.lastVersion); - this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5); - return this.UpdateCompressor.compressRawUpdates = sinon.stub().returns(this.compressedUpdates); - }); - - return describe("when the raw ops start where the existing history ends", function() { - beforeEach(function() { - this.rawUpdates = [{ v: 12, op: "mock-op-12" }, { v: 13, op: "mock-op-13" }]; - return this.UpdatesManager.compressAndSaveRawUpdates(this.project_id, this.doc_id, this.rawUpdates, this.temporary, this.callback); - }); - - it("should try to look at the last compressed op", function() { - return this.MongoManager.peekLastCompressedUpdate - .calledWith(this.doc_id) - .should.equal(true); - }); - - it("should compress the last compressed op and the raw ops", function() { - return this.UpdateCompressor.compressRawUpdates - .calledWith(this.lastCompressedUpdate, this.rawUpdates) - .should.equal(true); - }); - - it("should save the compressed ops", function() { - return this.PackManager.insertCompressedUpdates - .calledWith(this.project_id, this.doc_id, null, this.compressedUpdates, this.temporary) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - }); - - describe("processUncompressedUpdates", function() { - beforeEach(function() { - this.UpdatesManager.compressAndSaveRawUpdates = sinon.stub().callsArgWith(4); - this.RedisManager.deleteAppliedDocUpdates = sinon.stub().callsArg(3); - this.MongoManager.backportProjectId = sinon.stub().callsArg(2); - return this.UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, (this.temporary = "temp mock")); - }); - - describe("when there is fewer than one batch to send", function() { - beforeEach(function() { - this.updates = ["mock-update"]; - this.RedisManager.getOldestDocUpdates = sinon.stub().callsArgWith(2, null, this.updates); - this.RedisManager.expandDocUpdates = sinon.stub().callsArgWith(1, null, this.updates); - return this.UpdatesManager.processUncompressedUpdates(this.project_id, this.doc_id, this.temporary, this.callback); - }); - - it("should get the oldest updates", function() { - return this.RedisManager.getOldestDocUpdates - .calledWith(this.doc_id, this.UpdatesManager.REDIS_READ_BATCH_SIZE) - .should.equal(true); - }); - - it("should compress and save the updates", function() { - return this.UpdatesManager.compressAndSaveRawUpdates - .calledWith(this.project_id, this.doc_id, this.updates, this.temporary) - .should.equal(true); - }); - - it("should delete the batch of uncompressed updates that was just processed", function() { - return this.RedisManager.deleteAppliedDocUpdates - .calledWith(this.project_id, this.doc_id, this.updates) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - return describe("when there are multiple batches to send", function() { - beforeEach(function(done) { - this.UpdatesManager.REDIS_READ_BATCH_SIZE = 2; - this.updates = ["mock-update-0", "mock-update-1", "mock-update-2", "mock-update-3", "mock-update-4"]; - this.redisArray = this.updates.slice(); - this.RedisManager.getOldestDocUpdates = (doc_id, batchSize, callback) => { - if (callback == null) { callback = function(error, updates) {}; } - const updates = this.redisArray.slice(0, batchSize); - this.redisArray = this.redisArray.slice(batchSize); - return callback(null, updates); - }; - sinon.spy(this.RedisManager, "getOldestDocUpdates"); - this.RedisManager.expandDocUpdates = (jsonUpdates, callback) => { - return callback(null, jsonUpdates); - }; - sinon.spy(this.RedisManager, "expandDocUpdates"); - return this.UpdatesManager.processUncompressedUpdates(this.project_id, this.doc_id, this.temporary, (...args) => { - this.callback(...Array.from(args || [])); - return done(); - }); - }); - - it("should get the oldest updates in three batches ", function() { - return this.RedisManager.getOldestDocUpdates.callCount.should.equal(3); - }); - - it("should compress and save the updates in batches", function() { - this.UpdatesManager.compressAndSaveRawUpdates - .calledWith(this.project_id, this.doc_id, this.updates.slice(0,2), this.temporary) - .should.equal(true); - this.UpdatesManager.compressAndSaveRawUpdates - .calledWith(this.project_id, this.doc_id, this.updates.slice(2,4), this.temporary) - .should.equal(true); - return this.UpdatesManager.compressAndSaveRawUpdates - .calledWith(this.project_id, this.doc_id, this.updates.slice(4,5), this.temporary) - .should.equal(true); - }); - - it("should delete the batches of uncompressed updates", function() { - return this.RedisManager.deleteAppliedDocUpdates.callCount.should.equal(3); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - }); - - describe("processCompressedUpdatesWithLock", function() { - beforeEach(function() { - this.UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, (this.temporary = "temp mock")); - this.MongoManager.backportProjectId = sinon.stub().callsArg(2); - this.UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3); - this.LockManager.runWithLock = sinon.stub().callsArg(2); - return this.UpdatesManager.processUncompressedUpdatesWithLock(this.project_id, this.doc_id, this.callback); - }); - - it("should check if the updates are temporary", function() { - return this.UpdateTrimmer.shouldTrimUpdates - .calledWith(this.project_id) - .should.equal(true); - }); - - it("should backport the project id", function() { - return this.MongoManager.backportProjectId - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); - - it("should run processUncompressedUpdates with the lock", function() { - return this.LockManager.runWithLock - .calledWith( - `HistoryLock:${this.doc_id}` - ) - .should.equal(true); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("getDocUpdates", function() { - beforeEach(function() { - this.updates = ["mock-updates"]; - this.options = { to: "mock-to", limit: "mock-limit" }; - this.PackManager.getOpsByVersionRange = sinon.stub().callsArgWith(4, null, this.updates); - this.UpdatesManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(2); - return this.UpdatesManager.getDocUpdates(this.project_id, this.doc_id, this.options, this.callback); - }); - - it("should process outstanding updates", function() { - return this.UpdatesManager.processUncompressedUpdatesWithLock - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - }); - - it("should get the updates from the database", function() { - return this.PackManager.getOpsByVersionRange - .calledWith(this.project_id, this.doc_id, this.options.from, this.options.to) - .should.equal(true); - }); - - return it("should return the updates", function() { - return this.callback - .calledWith(null, this.updates) - .should.equal(true); - }); - }); - - describe("getDocUpdatesWithUserInfo", function() { - beforeEach(function() { - this.updates = ["mock-updates"]; - this.options = { to: "mock-to", limit: "mock-limit" }; - this.updatesWithUserInfo = ["updates-with-user-info"]; - this.UpdatesManager.getDocUpdates = sinon.stub().callsArgWith(3, null, this.updates); - this.UpdatesManager.fillUserInfo = sinon.stub().callsArgWith(1, null, this.updatesWithUserInfo); - return this.UpdatesManager.getDocUpdatesWithUserInfo(this.project_id, this.doc_id, this.options, this.callback); - }); - - it("should get the updates", function() { - return this.UpdatesManager.getDocUpdates - .calledWith(this.project_id, this.doc_id, this.options) - .should.equal(true); - }); - - it("should file the updates with the user info", function() { - return this.UpdatesManager.fillUserInfo - .calledWith(this.updates) - .should.equal(true); - }); - - return it("should return the updates with the filled details", function() { - return this.callback.calledWith(null, this.updatesWithUserInfo).should.equal(true); - }); - }); - - describe("processUncompressedUpdatesForProject", function() { - beforeEach(function(done) { - this.doc_ids = ["mock-id-1", "mock-id-2"]; - this.UpdateTrimmer.shouldTrimUpdates = sinon.stub().callsArgWith(1, null, (this.temporary = "temp mock")); - this.MongoManager.backportProjectId = sinon.stub().callsArg(2); - this.UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon.stub().callsArg(3); - this.RedisManager.getDocIdsWithHistoryOps = sinon.stub().callsArgWith(1, null, this.doc_ids); - return this.UpdatesManager.processUncompressedUpdatesForProject(this.project_id, () => { - this.callback(); - return done(); - }); - }); - - it("should get all the docs with history ops", function() { - return this.RedisManager.getDocIdsWithHistoryOps - .calledWith(this.project_id) - .should.equal(true); - }); - - it("should process the doc ops for the each doc_id", function() { - return Array.from(this.doc_ids).map((doc_id) => - this.UpdatesManager._processUncompressedUpdatesForDocWithLock - .calledWith(this.project_id, doc_id, this.temporary) - .should.equal(true)); - }); - - return it("should call the callback", function() { - return this.callback.called.should.equal(true); - }); - }); - - describe("getSummarizedProjectUpdates", function() { - beforeEach(function() { - this.updates = [{doc_id: 123, v:456, op: "mock-updates", meta: {user_id: 123, start_ts: 1233, end_ts:1234}}]; - this.options = { before: "mock-before", limit: "mock-limit" }; - this.summarizedUpdates = [ - {meta: {user_ids: [123], start_ts: 1233, end_ts:1234},docs:{"123":{fromV:456,toV:456}}} - ]; - this.updatesWithUserInfo = ["updates-with-user-info"]; - this.done_state = false; - this.iterator = { - next: cb => { - this.done_state = true; - return cb(null, this.updates); - }, - done: () => { - return this.done_state; - } - }; - this.PackManager.makeProjectIterator = sinon.stub().callsArgWith(2, null, this.iterator); - this.UpdatesManager.processUncompressedUpdatesForProject = sinon.stub().callsArg(1); - this.UpdatesManager.fillSummarizedUserInfo = sinon.stub().callsArgWith(1, null, this.updatesWithUserInfo); - return this.UpdatesManager.getSummarizedProjectUpdates(this.project_id, this.options, this.callback); - }); - - it("should process any outstanding updates", function() { - return this.UpdatesManager.processUncompressedUpdatesForProject - .calledWith(this.project_id) - .should.equal(true); - }); - - it("should get the updates", function() { - return this.PackManager.makeProjectIterator - .calledWith(this.project_id, this.options.before) - .should.equal(true); - }); - - it("should fill the updates with the user info", function() { - return this.UpdatesManager.fillSummarizedUserInfo - .calledWith(this.summarizedUpdates) - .should.equal(true); - }); - - return it("should return the updates with the filled details", function() { - return this.callback.calledWith(null, this.updatesWithUserInfo).should.equal(true); - }); - }); - - // describe "_extendBatchOfSummarizedUpdates", -> - // beforeEach -> - // @before = Date.now() - // @min_count = 2 - // @existingSummarizedUpdates = ["summarized-updates-3"] - // @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - - // describe "when there are updates to get", -> - // beforeEach -> - // @updates = [ - // {op: "mock-op-1", meta: end_ts: @before - 10}, - // {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} - // ] - // @existingSummarizedUpdates = ["summarized-updates-3"] - // @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] - // @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - // @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - // @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - - // it "should get the updates", -> - // @UpdatesManager.getProjectUpdatesWithUserInfo - // .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) - // .should.equal true - - // it "should summarize the updates", -> - // @UpdatesManager._summarizeUpdates - // .calledWith(@updates, @existingSummarizedUpdates) - // .should.equal true - - // it "should call the callback with the summarized updates and the next before timestamp", -> - // @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true - - // describe "when there are no more updates", -> - // beforeEach -> - // @updates = [] - // @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) - // @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) - // @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback - - // it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> - // @callback.calledWith(null, @summarizedUpdates, null).should.equal true - - // describe "getSummarizedProjectUpdates", -> - // describe "when one batch of updates is enough to meet the limit", -> - // beforeEach -> - // @before = Date.now() - // @min_count = 2 - // @updates = ["summarized-updates-3", "summarized-updates-2"] - // @nextBeforeTimestamp = @before - 100 - // @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) - // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - - // it "should get the batch of summarized updates", -> - // @UpdatesManager._extendBatchOfSummarizedUpdates - // .calledWith(@project_id, [], @before, @min_count) - // .should.equal true - - // it "should call the callback with the updates", -> - // @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true - - // describe "when multiple batches are needed to meet the limit", -> - // beforeEach -> - // @before = Date.now() - // @min_count = 4 - // @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - // @nextBeforeTimestamp = @before - 100 - // @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] - // @nextNextBeforeTimestamp = @before - 200 - // @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => - // if existingUpdates.length == 0 - // callback null, @firstBatch, @nextBeforeTimestamp - // else - // callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp - // sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" - // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - - // it "should get the first batch of summarized updates", -> - // @UpdatesManager._extendBatchOfSummarizedUpdates - // .calledWith(@project_id, [], @before, @min_count) - // .should.equal true - - // it "should get the second batch of summarized updates", -> - // @UpdatesManager._extendBatchOfSummarizedUpdates - // .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) - // .should.equal true - - // it "should call the callback with all the updates", -> - // @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true - - // describe "when the end of the database is hit", -> - // beforeEach -> - // @before = Date.now() - // @min_count = 4 - // @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] - // @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) - // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback - - // it "should get the batch of summarized updates", -> - // @UpdatesManager._extendBatchOfSummarizedUpdates - // .calledWith(@project_id, [], @before, @min_count) - // .should.equal true - - // it "should call the callback with the updates", -> - // @callback.calledWith(null, @updates, null).should.equal true - - describe("fillUserInfo", function() { - describe("with valid users", function() { - beforeEach(function(done) { - const {ObjectId} = require("mongojs"); - this.user_id_1 = ObjectId().toString(); - this.user_id_2 = ObjectId().toString(); - this.updates = [{ - meta: { - user_id: this.user_id_1 - }, - op: "mock-op-1" - }, { - meta: { - user_id: this.user_id_1 - }, - op: "mock-op-2" - }, { - meta: { - user_id: this.user_id_2 - }, - op: "mock-op-3" - }]; - this.user_info = {}; - this.user_info[this.user_id_1] = {email: "user1@sharelatex.com"}; - this.user_info[this.user_id_2] = {email: "user2@sharelatex.com"}; - - this.WebApiManager.getUserInfo = (user_id, callback) => { - if (callback == null) { callback = function(error, userInfo) {}; } - return callback(null, this.user_info[user_id]); - }; - sinon.spy(this.WebApiManager, "getUserInfo"); - - return this.UpdatesManager.fillUserInfo(this.updates, (error, results) => { - this.results = results; - return done(); - }); - }); - - it("should only call getUserInfo once for each user_id", function() { - this.WebApiManager.getUserInfo.calledTwice.should.equal(true); - this.WebApiManager.getUserInfo - .calledWith(this.user_id_1) - .should.equal(true); - return this.WebApiManager.getUserInfo - .calledWith(this.user_id_2) - .should.equal(true); - }); - - return it("should return the updates with the user info filled", function() { - return expect(this.results).to.deep.equal([{ - meta: { - user: { - email: "user1@sharelatex.com" - } - }, - op: "mock-op-1" - }, { - meta: { - user: { - email: "user1@sharelatex.com" - } - }, - op: "mock-op-2" - }, { - meta: { - user: { - email: "user2@sharelatex.com" - } - }, - op: "mock-op-3" - }]); - }); - }); - - - return describe("with invalid user ids", function() { - beforeEach(function(done) { - this.updates = [{ - meta: { - user_id: null - }, - op: "mock-op-1" - }, { - meta: { - user_id: "anonymous-user" - }, - op: "mock-op-2" - }]; - this.WebApiManager.getUserInfo = (user_id, callback) => { - if (callback == null) { callback = function(error, userInfo) {}; } - return callback(null, this.user_info[user_id]); - }; - sinon.spy(this.WebApiManager, "getUserInfo"); - - return this.UpdatesManager.fillUserInfo(this.updates, (error, results) => { - this.results = results; - return done(); - }); - }); - - it("should not call getUserInfo", function() { - return this.WebApiManager.getUserInfo.called.should.equal(false); - }); - - return it("should return the updates without the user info filled", function() { - return expect(this.results).to.deep.equal([{ - meta: {}, - op: "mock-op-1" - }, { - meta: {}, - op: "mock-op-2" - }]); - }); - }); -}); - - return describe("_summarizeUpdates", function() { - beforeEach(function() { - this.now = Date.now(); - this.user_1 = { id: "mock-user-1" }; - return this.user_2 = { id: "mock-user-2" };}); - - it("should concat updates that are close in time", function() { - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-1", - meta: { - user_id: this.user_1.id, - start_ts: this.now + 20, - end_ts: this.now + 30 - }, - v: 5 - }, { - doc_id: "doc-id-1", - meta: { - user_id: this.user_2.id, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }]); - - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - fromV: 4, - toV: 5 - } - }, - meta: { - user_ids: [this.user_1.id, this.user_2.id], - start_ts: this.now, - end_ts: this.now + 30 - } - }]); - }); - - it("should leave updates that are far apart in time", function() { - const oneDay = 1000 * 60 * 60 * 24; - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-1", - meta: { - user_id: this.user_2.id, - start_ts: this.now + oneDay, - end_ts: this.now + oneDay + 10 - }, - v: 5 - }, { - doc_id: "doc-id-1", - meta: { - user_id: this.user_1.id, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }]); - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - fromV: 5, - toV: 5 - } - }, - meta: { - user_ids: [this.user_2.id], - start_ts: this.now + oneDay, - end_ts: this.now + oneDay + 10 - } - }, { - docs: { - "doc-id-1": { - fromV: 4, - toV: 4 - } - }, - meta: { - user_ids: [this.user_1.id], - start_ts: this.now, - end_ts: this.now + 10 - } - }]); - }); - - it("should concat onto existing summarized updates", function() { - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-2", - meta: { - user_id: this.user_1.id, - start_ts: this.now + 20, - end_ts: this.now + 30 - }, - v: 5 - }, { - doc_id: "doc-id-2", - meta: { - user_id: this.user_2.id, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }], [{ - docs: { - "doc-id-1": { - fromV: 6, - toV: 8 - } - }, - meta: { - user_ids: [this.user_1.id], - start_ts: this.now + 40, - end_ts: this.now + 50 - } - }]); - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - toV: 8, - fromV: 6 - }, - "doc-id-2": { - toV: 5, - fromV: 4 - } - }, - meta: { - user_ids: [this.user_1.id, this.user_2.id], - start_ts: this.now, - end_ts: this.now + 50 - } - }]); - }); - - it("should include null user values", function() { - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-1", - meta: { - user_id: this.user_1.id, - start_ts: this.now + 20, - end_ts: this.now + 30 - }, - v: 5 - }, { - doc_id: "doc-id-1", - meta: { - user_id: null, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }]); - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - fromV: 4, - toV: 5 - } - }, - meta: { - user_ids: [this.user_1.id, null], - start_ts: this.now, - end_ts: this.now + 30 - } - }]); - }); - - it("should include null user values, when the null is earlier in the updates list", function() { - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-1", - meta: { - user_id: null, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }, { - doc_id: "doc-id-1", - meta: { - user_id: this.user_1.id, - start_ts: this.now + 20, - end_ts: this.now + 30 - }, - v: 5 - }]); - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - fromV: 4, - toV: 5 - } - }, - meta: { - user_ids: [null, this.user_1.id], - start_ts: this.now, - end_ts: this.now + 30 - } - }]); - }); - - it("should roll several null user values into one", function() { - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-1", - meta: { - user_id: this.user_1.id, - start_ts: this.now + 20, - end_ts: this.now + 30 - }, - v: 5 - }, { - doc_id: "doc-id-1", - meta: { - user_id: null, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }, { - doc_id: "doc-id-1", - meta: { - user_id: null, - start_ts: this.now + 2, - end_ts: this.now + 4 - }, - v: 4 - }]); - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - fromV: 4, - toV: 5 - } - }, - meta: { - user_ids: [this.user_1.id, null], - start_ts: this.now, - end_ts: this.now + 30 - } - }]); - }); - - return it("should split updates before a big delete", function() { - const result = this.UpdatesManager._summarizeUpdates([{ - doc_id: "doc-id-1", - op: [{ d: "this is a long long long long long delete", p: 34 }], - meta: { - user_id: this.user_1.id, - start_ts: this.now + 20, - end_ts: this.now + 30 - }, - v: 5 - }, { - doc_id: "doc-id-1", - meta: { - user_id: this.user_2.id, - start_ts: this.now, - end_ts: this.now + 10 - }, - v: 4 - }]); - - return expect(result).to.deep.equal([{ - docs: { - "doc-id-1": { - fromV: 5, - toV: 5 - } - }, - meta: { - user_ids: [this.user_1.id], - start_ts: this.now + 20, - end_ts: this.now + 30 - } - }, { - docs: { - "doc-id-1": { - fromV: 4, - toV: 4 - } - }, - meta: { - user_ids: [this.user_2.id], - start_ts: this.now, - end_ts: this.now + 10 - } - }]); - }); -}); -}); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/UpdatesManager.js' +const SandboxedModule = require('sandboxed-module') + +describe('UpdatesManager', function() { + beforeEach(function() { + this.UpdatesManager = SandboxedModule.require(modulePath, { + requires: { + './UpdateCompressor': (this.UpdateCompressor = {}), + './MongoManager': (this.MongoManager = {}), + './PackManager': (this.PackManager = {}), + './RedisManager': (this.RedisManager = {}), + './LockManager': (this.LockManager = {}), + './WebApiManager': (this.WebApiManager = {}), + './UpdateTrimmer': (this.UpdateTrimmer = {}), + './DocArchiveManager': (this.DocArchiveManager = {}), + 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, + 'settings-sharelatex': { + redis: { + lock: { + key_schema: { + historyLock({ doc_id }) { + return `HistoryLock:${doc_id}` + } + } + } + } + } + } + }) + this.doc_id = 'doc-id-123' + this.project_id = 'project-id-123' + this.callback = sinon.stub() + return (this.temporary = 'temp-mock') + }) + + describe('compressAndSaveRawUpdates', function() { + describe('when there are no raw ops', function() { + beforeEach(function() { + this.MongoManager.peekLastCompressedUpdate = sinon.stub() + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + [], + this.temporary, + this.callback + ) + }) + + it('should not need to access the database', function() { + return this.MongoManager.peekLastCompressedUpdate.called.should.equal( + false + ) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('when there is no compressed history to begin with', function() { + beforeEach(function() { + this.rawUpdates = [ + { v: 12, op: 'mock-op-12' }, + { v: 13, op: 'mock-op-13' } + ] + this.compressedUpdates = [{ v: 13, op: 'compressed-op-12' }] + + this.MongoManager.peekLastCompressedUpdate = sinon + .stub() + .callsArgWith(1, null, null) + this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) + this.UpdateCompressor.compressRawUpdates = sinon + .stub() + .returns(this.compressedUpdates) + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + it('should look at the last compressed op', function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) + + it('should save the compressed ops as a pack', function() { + return this.PackManager.insertCompressedUpdates + .calledWith( + this.project_id, + this.doc_id, + null, + this.compressedUpdates, + this.temporary + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('when the raw ops need appending to existing history', function() { + beforeEach(function() { + this.lastCompressedUpdate = { v: 11, op: 'compressed-op-11' } + this.compressedUpdates = [ + { v: 12, op: 'compressed-op-11+12' }, + { v: 13, op: 'compressed-op-12' } + ] + + this.MongoManager.peekLastCompressedUpdate = sinon + .stub() + .callsArgWith( + 1, + null, + this.lastCompressedUpdate, + this.lastCompressedUpdate.v + ) + this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) + return (this.UpdateCompressor.compressRawUpdates = sinon + .stub() + .returns(this.compressedUpdates)) + }) + + describe('when the raw ops start where the existing history ends', function() { + beforeEach(function() { + this.rawUpdates = [ + { v: 12, op: 'mock-op-12' }, + { v: 13, op: 'mock-op-13' } + ] + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + it('should look at the last compressed op', function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) + + it('should compress the raw ops', function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(null, this.rawUpdates) + .should.equal(true) + }) + + it('should save the new compressed ops into a pack', function() { + return this.PackManager.insertCompressedUpdates + .calledWith( + this.project_id, + this.doc_id, + this.lastCompressedUpdate, + this.compressedUpdates, + this.temporary + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('when the raw ops start where the existing history ends and the history is in a pack', function() { + beforeEach(function() { + this.lastCompressedUpdate = { + pack: [{ v: 11, op: 'compressed-op-11' }], + v: 11 + } + this.rawUpdates = [ + { v: 12, op: 'mock-op-12' }, + { v: 13, op: 'mock-op-13' } + ] + this.MongoManager.peekLastCompressedUpdate = sinon + .stub() + .callsArgWith( + 1, + null, + this.lastCompressedUpdate, + this.lastCompressedUpdate.v + ) + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + it('should look at the last compressed op', function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) + + it('should compress the raw ops', function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(null, this.rawUpdates) + .should.equal(true) + }) + + it('should save the new compressed ops into a pack', function() { + return this.PackManager.insertCompressedUpdates + .calledWith( + this.project_id, + this.doc_id, + this.lastCompressedUpdate, + this.compressedUpdates, + this.temporary + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('when some raw ops are passed that have already been compressed', function() { + beforeEach(function() { + this.rawUpdates = [ + { v: 10, op: 'mock-op-10' }, + { v: 11, op: 'mock-op-11' }, + { v: 12, op: 'mock-op-12' }, + { v: 13, op: 'mock-op-13' } + ] + + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + return it('should only compress the more recent raw ops', function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(null, this.rawUpdates.slice(-2)) + .should.equal(true) + }) + }) + + describe('when the raw ops do not follow from the last compressed op version', function() { + beforeEach(function() { + this.rawUpdates = [{ v: 13, op: 'mock-op-13' }] + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + it('should call the callback with an error', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + 'Tried to apply raw op at version 13 to last compressed update with version 11 from unknown time' + ) + ) + .should.equal(true) + }) + + return it('should not insert any update into mongo', function() { + return this.PackManager.insertCompressedUpdates.called.should.equal( + false + ) + }) + }) + + return describe('when the raw ops are out of order', function() { + beforeEach(function() { + this.rawUpdates = [ + { v: 13, op: 'mock-op-13' }, + { v: 12, op: 'mock-op-12' } + ] + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + it('should call the callback with an error', function() { + return this.callback + .calledWith(sinon.match.has('message')) + .should.equal(true) + }) + + return it('should not insert any update into mongo', function() { + return this.PackManager.insertCompressedUpdates.called.should.equal( + false + ) + }) + }) + }) + + return describe('when the raw ops need appending to existing history which is in S3', function() { + beforeEach(function() { + this.lastCompressedUpdate = null + this.lastVersion = 11 + this.compressedUpdates = [{ v: 13, op: 'compressed-op-12' }] + + this.MongoManager.peekLastCompressedUpdate = sinon + .stub() + .callsArgWith(1, null, null, this.lastVersion) + this.PackManager.insertCompressedUpdates = sinon.stub().callsArg(5) + return (this.UpdateCompressor.compressRawUpdates = sinon + .stub() + .returns(this.compressedUpdates)) + }) + + return describe('when the raw ops start where the existing history ends', function() { + beforeEach(function() { + this.rawUpdates = [ + { v: 12, op: 'mock-op-12' }, + { v: 13, op: 'mock-op-13' } + ] + return this.UpdatesManager.compressAndSaveRawUpdates( + this.project_id, + this.doc_id, + this.rawUpdates, + this.temporary, + this.callback + ) + }) + + it('should try to look at the last compressed op', function() { + return this.MongoManager.peekLastCompressedUpdate + .calledWith(this.doc_id) + .should.equal(true) + }) + + it('should compress the last compressed op and the raw ops', function() { + return this.UpdateCompressor.compressRawUpdates + .calledWith(this.lastCompressedUpdate, this.rawUpdates) + .should.equal(true) + }) + + it('should save the compressed ops', function() { + return this.PackManager.insertCompressedUpdates + .calledWith( + this.project_id, + this.doc_id, + null, + this.compressedUpdates, + this.temporary + ) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + }) + + describe('processUncompressedUpdates', function() { + beforeEach(function() { + this.UpdatesManager.compressAndSaveRawUpdates = sinon + .stub() + .callsArgWith(4) + this.RedisManager.deleteAppliedDocUpdates = sinon.stub().callsArg(3) + this.MongoManager.backportProjectId = sinon.stub().callsArg(2) + return (this.UpdateTrimmer.shouldTrimUpdates = sinon + .stub() + .callsArgWith(1, null, (this.temporary = 'temp mock'))) + }) + + describe('when there is fewer than one batch to send', function() { + beforeEach(function() { + this.updates = ['mock-update'] + this.RedisManager.getOldestDocUpdates = sinon + .stub() + .callsArgWith(2, null, this.updates) + this.RedisManager.expandDocUpdates = sinon + .stub() + .callsArgWith(1, null, this.updates) + return this.UpdatesManager.processUncompressedUpdates( + this.project_id, + this.doc_id, + this.temporary, + this.callback + ) + }) + + it('should get the oldest updates', function() { + return this.RedisManager.getOldestDocUpdates + .calledWith(this.doc_id, this.UpdatesManager.REDIS_READ_BATCH_SIZE) + .should.equal(true) + }) + + it('should compress and save the updates', function() { + return this.UpdatesManager.compressAndSaveRawUpdates + .calledWith( + this.project_id, + this.doc_id, + this.updates, + this.temporary + ) + .should.equal(true) + }) + + it('should delete the batch of uncompressed updates that was just processed', function() { + return this.RedisManager.deleteAppliedDocUpdates + .calledWith(this.project_id, this.doc_id, this.updates) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + return describe('when there are multiple batches to send', function() { + beforeEach(function(done) { + this.UpdatesManager.REDIS_READ_BATCH_SIZE = 2 + this.updates = [ + 'mock-update-0', + 'mock-update-1', + 'mock-update-2', + 'mock-update-3', + 'mock-update-4' + ] + this.redisArray = this.updates.slice() + this.RedisManager.getOldestDocUpdates = ( + doc_id, + batchSize, + callback + ) => { + if (callback == null) { + callback = function(error, updates) {} + } + const updates = this.redisArray.slice(0, batchSize) + this.redisArray = this.redisArray.slice(batchSize) + return callback(null, updates) + } + sinon.spy(this.RedisManager, 'getOldestDocUpdates') + this.RedisManager.expandDocUpdates = (jsonUpdates, callback) => { + return callback(null, jsonUpdates) + } + sinon.spy(this.RedisManager, 'expandDocUpdates') + return this.UpdatesManager.processUncompressedUpdates( + this.project_id, + this.doc_id, + this.temporary, + (...args) => { + this.callback(...Array.from(args || [])) + return done() + } + ) + }) + + it('should get the oldest updates in three batches ', function() { + return this.RedisManager.getOldestDocUpdates.callCount.should.equal(3) + }) + + it('should compress and save the updates in batches', function() { + this.UpdatesManager.compressAndSaveRawUpdates + .calledWith( + this.project_id, + this.doc_id, + this.updates.slice(0, 2), + this.temporary + ) + .should.equal(true) + this.UpdatesManager.compressAndSaveRawUpdates + .calledWith( + this.project_id, + this.doc_id, + this.updates.slice(2, 4), + this.temporary + ) + .should.equal(true) + return this.UpdatesManager.compressAndSaveRawUpdates + .calledWith( + this.project_id, + this.doc_id, + this.updates.slice(4, 5), + this.temporary + ) + .should.equal(true) + }) + + it('should delete the batches of uncompressed updates', function() { + return this.RedisManager.deleteAppliedDocUpdates.callCount.should.equal( + 3 + ) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + }) + + describe('processCompressedUpdatesWithLock', function() { + beforeEach(function() { + this.UpdateTrimmer.shouldTrimUpdates = sinon + .stub() + .callsArgWith(1, null, (this.temporary = 'temp mock')) + this.MongoManager.backportProjectId = sinon.stub().callsArg(2) + this.UpdatesManager._processUncompressedUpdates = sinon.stub().callsArg(3) + this.LockManager.runWithLock = sinon.stub().callsArg(2) + return this.UpdatesManager.processUncompressedUpdatesWithLock( + this.project_id, + this.doc_id, + this.callback + ) + }) + + it('should check if the updates are temporary', function() { + return this.UpdateTrimmer.shouldTrimUpdates + .calledWith(this.project_id) + .should.equal(true) + }) + + it('should backport the project id', function() { + return this.MongoManager.backportProjectId + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) + + it('should run processUncompressedUpdates with the lock', function() { + return this.LockManager.runWithLock + .calledWith(`HistoryLock:${this.doc_id}`) + .should.equal(true) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('getDocUpdates', function() { + beforeEach(function() { + this.updates = ['mock-updates'] + this.options = { to: 'mock-to', limit: 'mock-limit' } + this.PackManager.getOpsByVersionRange = sinon + .stub() + .callsArgWith(4, null, this.updates) + this.UpdatesManager.processUncompressedUpdatesWithLock = sinon + .stub() + .callsArg(2) + return this.UpdatesManager.getDocUpdates( + this.project_id, + this.doc_id, + this.options, + this.callback + ) + }) + + it('should process outstanding updates', function() { + return this.UpdatesManager.processUncompressedUpdatesWithLock + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + }) + + it('should get the updates from the database', function() { + return this.PackManager.getOpsByVersionRange + .calledWith( + this.project_id, + this.doc_id, + this.options.from, + this.options.to + ) + .should.equal(true) + }) + + return it('should return the updates', function() { + return this.callback.calledWith(null, this.updates).should.equal(true) + }) + }) + + describe('getDocUpdatesWithUserInfo', function() { + beforeEach(function() { + this.updates = ['mock-updates'] + this.options = { to: 'mock-to', limit: 'mock-limit' } + this.updatesWithUserInfo = ['updates-with-user-info'] + this.UpdatesManager.getDocUpdates = sinon + .stub() + .callsArgWith(3, null, this.updates) + this.UpdatesManager.fillUserInfo = sinon + .stub() + .callsArgWith(1, null, this.updatesWithUserInfo) + return this.UpdatesManager.getDocUpdatesWithUserInfo( + this.project_id, + this.doc_id, + this.options, + this.callback + ) + }) + + it('should get the updates', function() { + return this.UpdatesManager.getDocUpdates + .calledWith(this.project_id, this.doc_id, this.options) + .should.equal(true) + }) + + it('should file the updates with the user info', function() { + return this.UpdatesManager.fillUserInfo + .calledWith(this.updates) + .should.equal(true) + }) + + return it('should return the updates with the filled details', function() { + return this.callback + .calledWith(null, this.updatesWithUserInfo) + .should.equal(true) + }) + }) + + describe('processUncompressedUpdatesForProject', function() { + beforeEach(function(done) { + this.doc_ids = ['mock-id-1', 'mock-id-2'] + this.UpdateTrimmer.shouldTrimUpdates = sinon + .stub() + .callsArgWith(1, null, (this.temporary = 'temp mock')) + this.MongoManager.backportProjectId = sinon.stub().callsArg(2) + this.UpdatesManager._processUncompressedUpdatesForDocWithLock = sinon + .stub() + .callsArg(3) + this.RedisManager.getDocIdsWithHistoryOps = sinon + .stub() + .callsArgWith(1, null, this.doc_ids) + return this.UpdatesManager.processUncompressedUpdatesForProject( + this.project_id, + () => { + this.callback() + return done() + } + ) + }) + + it('should get all the docs with history ops', function() { + return this.RedisManager.getDocIdsWithHistoryOps + .calledWith(this.project_id) + .should.equal(true) + }) + + it('should process the doc ops for the each doc_id', function() { + return Array.from(this.doc_ids).map(doc_id => + this.UpdatesManager._processUncompressedUpdatesForDocWithLock + .calledWith(this.project_id, doc_id, this.temporary) + .should.equal(true) + ) + }) + + return it('should call the callback', function() { + return this.callback.called.should.equal(true) + }) + }) + + describe('getSummarizedProjectUpdates', function() { + beforeEach(function() { + this.updates = [ + { + doc_id: 123, + v: 456, + op: 'mock-updates', + meta: { user_id: 123, start_ts: 1233, end_ts: 1234 } + } + ] + this.options = { before: 'mock-before', limit: 'mock-limit' } + this.summarizedUpdates = [ + { + meta: { user_ids: [123], start_ts: 1233, end_ts: 1234 }, + docs: { '123': { fromV: 456, toV: 456 } } + } + ] + this.updatesWithUserInfo = ['updates-with-user-info'] + this.done_state = false + this.iterator = { + next: cb => { + this.done_state = true + return cb(null, this.updates) + }, + done: () => { + return this.done_state + } + } + this.PackManager.makeProjectIterator = sinon + .stub() + .callsArgWith(2, null, this.iterator) + this.UpdatesManager.processUncompressedUpdatesForProject = sinon + .stub() + .callsArg(1) + this.UpdatesManager.fillSummarizedUserInfo = sinon + .stub() + .callsArgWith(1, null, this.updatesWithUserInfo) + return this.UpdatesManager.getSummarizedProjectUpdates( + this.project_id, + this.options, + this.callback + ) + }) + + it('should process any outstanding updates', function() { + return this.UpdatesManager.processUncompressedUpdatesForProject + .calledWith(this.project_id) + .should.equal(true) + }) + + it('should get the updates', function() { + return this.PackManager.makeProjectIterator + .calledWith(this.project_id, this.options.before) + .should.equal(true) + }) + + it('should fill the updates with the user info', function() { + return this.UpdatesManager.fillSummarizedUserInfo + .calledWith(this.summarizedUpdates) + .should.equal(true) + }) + + return it('should return the updates with the filled details', function() { + return this.callback + .calledWith(null, this.updatesWithUserInfo) + .should.equal(true) + }) + }) + + // describe "_extendBatchOfSummarizedUpdates", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 2 + // @existingSummarizedUpdates = ["summarized-updates-3"] + // @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + + // describe "when there are updates to get", -> + // beforeEach -> + // @updates = [ + // {op: "mock-op-1", meta: end_ts: @before - 10}, + // {op: "mock-op-1", meta: end_ts: @nextBeforeTimestamp = @before - 20} + // ] + // @existingSummarizedUpdates = ["summarized-updates-3"] + // @summarizedUpdates = ["summarized-updates-3", "summarized-update-2", "summarized-update-1"] + // @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + // @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + // @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback + + // it "should get the updates", -> + // @UpdatesManager.getProjectUpdatesWithUserInfo + // .calledWith(@project_id, { before: @before, limit: 3 * @min_count }) + // .should.equal true + + // it "should summarize the updates", -> + // @UpdatesManager._summarizeUpdates + // .calledWith(@updates, @existingSummarizedUpdates) + // .should.equal true + + // it "should call the callback with the summarized updates and the next before timestamp", -> + // @callback.calledWith(null, @summarizedUpdates, @nextBeforeTimestamp).should.equal true + + // describe "when there are no more updates", -> + // beforeEach -> + // @updates = [] + // @UpdatesManager._summarizeUpdates = sinon.stub().returns(@summarizedUpdates) + // @UpdatesManager.getProjectUpdatesWithUserInfo = sinon.stub().callsArgWith(2, null, @updates) + // @UpdatesManager._extendBatchOfSummarizedUpdates @project_id, @existingSummarizedUpdates, @before, @min_count, @callback + + // it "should call the callback with the summarized updates and null for nextBeforeTimestamp", -> + // @callback.calledWith(null, @summarizedUpdates, null).should.equal true + + // describe "getSummarizedProjectUpdates", -> + // describe "when one batch of updates is enough to meet the limit", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 2 + // @updates = ["summarized-updates-3", "summarized-updates-2"] + // @nextBeforeTimestamp = @before - 100 + // @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, @nextBeforeTimestamp) + // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + + // it "should get the batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, [], @before, @min_count) + // .should.equal true + + // it "should call the callback with the updates", -> + // @callback.calledWith(null, @updates, @nextBeforeTimestamp).should.equal true + + // describe "when multiple batches are needed to meet the limit", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 4 + // @firstBatch = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + // @nextBeforeTimestamp = @before - 100 + // @secondBatch = [{ toV: 4, fromV: 4 }, { toV: 3, fromV: 3 }] + // @nextNextBeforeTimestamp = @before - 200 + // @UpdatesManager._extendBatchOfSummarizedUpdates = (project_id, existingUpdates, before, desiredLength, callback) => + // if existingUpdates.length == 0 + // callback null, @firstBatch, @nextBeforeTimestamp + // else + // callback null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp + // sinon.spy @UpdatesManager, "_extendBatchOfSummarizedUpdates" + // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + + // it "should get the first batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, [], @before, @min_count) + // .should.equal true + + // it "should get the second batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, @firstBatch, @nextBeforeTimestamp, @min_count) + // .should.equal true + + // it "should call the callback with all the updates", -> + // @callback.calledWith(null, @firstBatch.concat(@secondBatch), @nextNextBeforeTimestamp).should.equal true + + // describe "when the end of the database is hit", -> + // beforeEach -> + // @before = Date.now() + // @min_count = 4 + // @updates = [{ toV: 6, fromV: 6 }, { toV: 5, fromV: 5 }] + // @UpdatesManager._extendBatchOfSummarizedUpdates = sinon.stub().callsArgWith(4, null, @updates, null) + // @UpdatesManager.getSummarizedProjectUpdates @project_id, { before: @before, min_count: @min_count }, @callback + + // it "should get the batch of summarized updates", -> + // @UpdatesManager._extendBatchOfSummarizedUpdates + // .calledWith(@project_id, [], @before, @min_count) + // .should.equal true + + // it "should call the callback with the updates", -> + // @callback.calledWith(null, @updates, null).should.equal true + + describe('fillUserInfo', function() { + describe('with valid users', function() { + beforeEach(function(done) { + const { ObjectId } = require('mongojs') + this.user_id_1 = ObjectId().toString() + this.user_id_2 = ObjectId().toString() + this.updates = [ + { + meta: { + user_id: this.user_id_1 + }, + op: 'mock-op-1' + }, + { + meta: { + user_id: this.user_id_1 + }, + op: 'mock-op-2' + }, + { + meta: { + user_id: this.user_id_2 + }, + op: 'mock-op-3' + } + ] + this.user_info = {} + this.user_info[this.user_id_1] = { email: 'user1@sharelatex.com' } + this.user_info[this.user_id_2] = { email: 'user2@sharelatex.com' } + + this.WebApiManager.getUserInfo = (user_id, callback) => { + if (callback == null) { + callback = function(error, userInfo) {} + } + return callback(null, this.user_info[user_id]) + } + sinon.spy(this.WebApiManager, 'getUserInfo') + + return this.UpdatesManager.fillUserInfo( + this.updates, + (error, results) => { + this.results = results + return done() + } + ) + }) + + it('should only call getUserInfo once for each user_id', function() { + this.WebApiManager.getUserInfo.calledTwice.should.equal(true) + this.WebApiManager.getUserInfo + .calledWith(this.user_id_1) + .should.equal(true) + return this.WebApiManager.getUserInfo + .calledWith(this.user_id_2) + .should.equal(true) + }) + + return it('should return the updates with the user info filled', function() { + return expect(this.results).to.deep.equal([ + { + meta: { + user: { + email: 'user1@sharelatex.com' + } + }, + op: 'mock-op-1' + }, + { + meta: { + user: { + email: 'user1@sharelatex.com' + } + }, + op: 'mock-op-2' + }, + { + meta: { + user: { + email: 'user2@sharelatex.com' + } + }, + op: 'mock-op-3' + } + ]) + }) + }) + + return describe('with invalid user ids', function() { + beforeEach(function(done) { + this.updates = [ + { + meta: { + user_id: null + }, + op: 'mock-op-1' + }, + { + meta: { + user_id: 'anonymous-user' + }, + op: 'mock-op-2' + } + ] + this.WebApiManager.getUserInfo = (user_id, callback) => { + if (callback == null) { + callback = function(error, userInfo) {} + } + return callback(null, this.user_info[user_id]) + } + sinon.spy(this.WebApiManager, 'getUserInfo') + + return this.UpdatesManager.fillUserInfo( + this.updates, + (error, results) => { + this.results = results + return done() + } + ) + }) + + it('should not call getUserInfo', function() { + return this.WebApiManager.getUserInfo.called.should.equal(false) + }) + + return it('should return the updates without the user info filled', function() { + return expect(this.results).to.deep.equal([ + { + meta: {}, + op: 'mock-op-1' + }, + { + meta: {}, + op: 'mock-op-2' + } + ]) + }) + }) + }) + + return describe('_summarizeUpdates', function() { + beforeEach(function() { + this.now = Date.now() + this.user_1 = { id: 'mock-user-1' } + return (this.user_2 = { id: 'mock-user-2' }) + }) + + it('should concat updates that are close in time', function() { + const result = this.UpdatesManager._summarizeUpdates([ + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, + v: 5 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_2.id, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + } + ]) + + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + fromV: 4, + toV: 5 + } + }, + meta: { + user_ids: [this.user_1.id, this.user_2.id], + start_ts: this.now, + end_ts: this.now + 30 + } + } + ]) + }) + + it('should leave updates that are far apart in time', function() { + const oneDay = 1000 * 60 * 60 * 24 + const result = this.UpdatesManager._summarizeUpdates([ + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_2.id, + start_ts: this.now + oneDay, + end_ts: this.now + oneDay + 10 + }, + v: 5 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_1.id, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + } + ]) + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + fromV: 5, + toV: 5 + } + }, + meta: { + user_ids: [this.user_2.id], + start_ts: this.now + oneDay, + end_ts: this.now + oneDay + 10 + } + }, + { + docs: { + 'doc-id-1': { + fromV: 4, + toV: 4 + } + }, + meta: { + user_ids: [this.user_1.id], + start_ts: this.now, + end_ts: this.now + 10 + } + } + ]) + }) + + it('should concat onto existing summarized updates', function() { + const result = this.UpdatesManager._summarizeUpdates( + [ + { + doc_id: 'doc-id-2', + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, + v: 5 + }, + { + doc_id: 'doc-id-2', + meta: { + user_id: this.user_2.id, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + } + ], + [ + { + docs: { + 'doc-id-1': { + fromV: 6, + toV: 8 + } + }, + meta: { + user_ids: [this.user_1.id], + start_ts: this.now + 40, + end_ts: this.now + 50 + } + } + ] + ) + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + toV: 8, + fromV: 6 + }, + 'doc-id-2': { + toV: 5, + fromV: 4 + } + }, + meta: { + user_ids: [this.user_1.id, this.user_2.id], + start_ts: this.now, + end_ts: this.now + 50 + } + } + ]) + }) + + it('should include null user values', function() { + const result = this.UpdatesManager._summarizeUpdates([ + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, + v: 5 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: null, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + } + ]) + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + fromV: 4, + toV: 5 + } + }, + meta: { + user_ids: [this.user_1.id, null], + start_ts: this.now, + end_ts: this.now + 30 + } + } + ]) + }) + + it('should include null user values, when the null is earlier in the updates list', function() { + const result = this.UpdatesManager._summarizeUpdates([ + { + doc_id: 'doc-id-1', + meta: { + user_id: null, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, + v: 5 + } + ]) + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + fromV: 4, + toV: 5 + } + }, + meta: { + user_ids: [null, this.user_1.id], + start_ts: this.now, + end_ts: this.now + 30 + } + } + ]) + }) + + it('should roll several null user values into one', function() { + const result = this.UpdatesManager._summarizeUpdates([ + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, + v: 5 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: null, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: null, + start_ts: this.now + 2, + end_ts: this.now + 4 + }, + v: 4 + } + ]) + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + fromV: 4, + toV: 5 + } + }, + meta: { + user_ids: [this.user_1.id, null], + start_ts: this.now, + end_ts: this.now + 30 + } + } + ]) + }) + + return it('should split updates before a big delete', function() { + const result = this.UpdatesManager._summarizeUpdates([ + { + doc_id: 'doc-id-1', + op: [{ d: 'this is a long long long long long delete', p: 34 }], + meta: { + user_id: this.user_1.id, + start_ts: this.now + 20, + end_ts: this.now + 30 + }, + v: 5 + }, + { + doc_id: 'doc-id-1', + meta: { + user_id: this.user_2.id, + start_ts: this.now, + end_ts: this.now + 10 + }, + v: 4 + } + ]) + + return expect(result).to.deep.equal([ + { + docs: { + 'doc-id-1': { + fromV: 5, + toV: 5 + } + }, + meta: { + user_ids: [this.user_1.id], + start_ts: this.now + 20, + end_ts: this.now + 30 + } + }, + { + docs: { + 'doc-id-1': { + fromV: 4, + toV: 4 + } + }, + meta: { + user_ids: [this.user_2.id], + start_ts: this.now, + end_ts: this.now + 10 + } + } + ]) + }) + }) +}) diff --git a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js index 984493afc4..d62466b0b9 100644 --- a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js @@ -9,162 +9,206 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require('sinon'); -const chai = require('chai'); -const should = chai.should(); -const { expect } = chai; -const modulePath = "../../../../app/js/WebApiManager.js"; -const SandboxedModule = require('sandboxed-module'); +const sinon = require('sinon') +const chai = require('chai') +const should = chai.should() +const { expect } = chai +const modulePath = '../../../../app/js/WebApiManager.js' +const SandboxedModule = require('sandboxed-module') -describe("WebApiManager", function() { - beforeEach(function() { - this.WebApiManager = SandboxedModule.require(modulePath, { requires: { - "requestretry": (this.request = {}), - "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub() }), - 'settings-sharelatex': (this.settings = { - apis: { - web: { - url: "http://example.com", - user: "sharelatex", - pass: "password" - } - } - }) - } - } - ); - this.callback = sinon.stub(); - this.user_id = "mock-user-id"; - this.project_id = "mock-project-id"; - this.user_info = { - email: "leo@sharelatex.com", - id: this.user_id, - first_name: "Leo", - last_nane: "Lion", - extra_param: "blah" - }; - return this.project = - {features: "mock-features"}; - }); +describe('WebApiManager', function() { + beforeEach(function() { + this.WebApiManager = SandboxedModule.require(modulePath, { + requires: { + requestretry: (this.request = {}), + 'logger-sharelatex': (this.logger = { + log: sinon.stub(), + error: sinon.stub() + }), + 'settings-sharelatex': (this.settings = { + apis: { + web: { + url: 'http://example.com', + user: 'sharelatex', + pass: 'password' + } + } + }) + } + }) + this.callback = sinon.stub() + this.user_id = 'mock-user-id' + this.project_id = 'mock-project-id' + this.user_info = { + email: 'leo@sharelatex.com', + id: this.user_id, + first_name: 'Leo', + last_nane: 'Lion', + extra_param: 'blah' + } + return (this.project = { features: 'mock-features' }) + }) - describe("getUserInfo", function() { - describe("successfully", function() { - beforeEach(function() { - this.body = JSON.stringify(this.user_info); - this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body); - return this.WebApiManager.getUserInfo(this.user_id, this.callback); - }); + describe('getUserInfo', function() { + describe('successfully', function() { + beforeEach(function() { + this.body = JSON.stringify(this.user_info) + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200 }, this.body) + return this.WebApiManager.getUserInfo(this.user_id, this.callback) + }) - it('should get the user from the web api', function() { - return this.request.get - .calledWithMatch({ - url: `${this.settings.apis.web.url}/user/${this.user_id}/personal_info`, - auth: { - user: this.settings.apis.web.user, - pass: this.settings.apis.web.pass, - sendImmediately: true - } - }) - .should.equal(true); - }); + it('should get the user from the web api', function() { + return this.request.get + .calledWithMatch({ + url: `${this.settings.apis.web.url}/user/${this.user_id}/personal_info`, + auth: { + user: this.settings.apis.web.user, + pass: this.settings.apis.web.pass, + sendImmediately: true + } + }) + .should.equal(true) + }) - return it("should call the callback with only the email, id and names", function() { - return this.callback.calledWith(null, { - id: this.user_id, - email: this.user_info.email, - first_name: this.user_info.first_name, - last_name: this.user_info.last_name - }).should.equal(true); - }); - }); + return it('should call the callback with only the email, id and names', function() { + return this.callback + .calledWith(null, { + id: this.user_id, + email: this.user_info.email, + first_name: this.user_info.first_name, + last_name: this.user_info.last_name + }) + .should.equal(true) + }) + }) - describe("when the web API returns an error", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); - return this.WebApiManager.getUserInfo(this.user_id, this.callback); - }); + describe('when the web API returns an error', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith( + 1, + (this.error = new Error('something went wrong')), + null, + null + ) + return this.WebApiManager.getUserInfo(this.user_id, this.callback) + }) - return it("should return an error to the callback", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); + return it('should return an error to the callback', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) - describe("when the web returns a failure error code", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42}, ""); - return this.WebApiManager.getUserInfo(this.user_id, this.callback); - }); + describe('when the web returns a failure error code', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 500, attempts: 42 }, '') + return this.WebApiManager.getUserInfo(this.user_id, this.callback) + }) - return it("should return the callback with an error", function() { - return this.callback - .calledWith(sinon.match.has('message', "web returned a non-success status code: 500 (attempts: 42)")) - .should.equal(true); - }); - }); + return it('should return the callback with an error', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + 'web returned a non-success status code: 500 (attempts: 42)' + ) + ) + .should.equal(true) + }) + }) - return describe("when the user cannot be found", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 404}, "nothing"); - return this.WebApiManager.getUserInfo(this.user_id, this.callback); - }); + return describe('when the user cannot be found', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 404 }, 'nothing') + return this.WebApiManager.getUserInfo(this.user_id, this.callback) + }) - return it("should return a null value", function() { - return this.callback - .calledWith(null, null) - .should.equal(true); - }); - }); - }); + return it('should return a null value', function() { + return this.callback.calledWith(null, null).should.equal(true) + }) + }) + }) + return describe('getProjectDetails', function() { + describe('successfully', function() { + beforeEach(function() { + this.body = JSON.stringify(this.project) + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 200 }, this.body) + return this.WebApiManager.getProjectDetails( + this.project_id, + this.callback + ) + }) - return describe("getProjectDetails", function() { - describe("successfully", function() { - beforeEach(function() { - this.body = JSON.stringify(this.project); - this.request.get = sinon.stub().callsArgWith(1, null, {statusCode: 200}, this.body); - return this.WebApiManager.getProjectDetails(this.project_id, this.callback); - }); + it('should get the project from the web api', function() { + return this.request.get + .calledWithMatch({ + url: `${this.settings.apis.web.url}/project/${this.project_id}/details`, + auth: { + user: this.settings.apis.web.user, + pass: this.settings.apis.web.pass, + sendImmediately: true + } + }) + .should.equal(true) + }) - it('should get the project from the web api', function() { - return this.request.get - .calledWithMatch({ - url: `${this.settings.apis.web.url}/project/${this.project_id}/details`, - auth: { - user: this.settings.apis.web.user, - pass: this.settings.apis.web.pass, - sendImmediately: true - } - }) - .should.equal(true); - }); + return it('should call the callback with the project', function() { + return this.callback.calledWith(null, this.project).should.equal(true) + }) + }) - return it("should call the callback with the project", function() { - return this.callback.calledWith(null, this.project).should.equal(true); - }); - }); + describe('when the web API returns an error', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith( + 1, + (this.error = new Error('something went wrong')), + null, + null + ) + return this.WebApiManager.getProjectDetails( + this.project_id, + this.callback + ) + }) - describe("when the web API returns an error", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, (this.error = new Error("something went wrong")), null, null); - return this.WebApiManager.getProjectDetails(this.project_id, this.callback); - }); + return it('should return an error to the callback', function() { + return this.callback.calledWith(this.error).should.equal(true) + }) + }) - return it("should return an error to the callback", function() { - return this.callback.calledWith(this.error).should.equal(true); - }); - }); + return describe('when the web returns a failure error code', function() { + beforeEach(function() { + this.request.get = sinon + .stub() + .callsArgWith(1, null, { statusCode: 500, attempts: 42 }, '') + return this.WebApiManager.getProjectDetails( + this.project_id, + this.callback + ) + }) - return describe("when the web returns a failure error code", function() { - beforeEach(function() { - this.request.get = sinon.stub().callsArgWith(1, null, { statusCode: 500, attempts: 42 }, ""); - return this.WebApiManager.getProjectDetails(this.project_id, this.callback); - }); - - return it("should return the callback with an error", function() { - return this.callback - .calledWith(sinon.match.has('message', "web returned a non-success status code: 500 (attempts: 42)")) - .should.equal(true); - }); - }); - }); -}); + return it('should return the callback with an error', function() { + return this.callback + .calledWith( + sinon.match.has( + 'message', + 'web returned a non-success status code: 500 (attempts: 42)' + ) + ) + .should.equal(true) + }) + }) + }) +}) From c97a2a3c07252934044e13b1e39b61f9ff1de9bb Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:35:33 +0100 Subject: [PATCH 475/549] decaffeinate: Rename AppendingUpdatesTests.coffee and 11 other files from .coffee to .js --- .../{AppendingUpdatesTests.coffee => AppendingUpdatesTests.js} | 0 .../{ArchivingUpdatesTests.coffee => ArchivingUpdatesTests.js} | 0 .../{FlushingUpdatesTests.coffee => FlushingUpdatesTests.js} | 0 .../coffee/{GettingADiffTests.coffee => GettingADiffTests.js} | 0 .../coffee/{GettingUpdatesTests.coffee => GettingUpdatesTests.js} | 0 .../coffee/{LockManagerTests.coffee => LockManagerTests.js} | 0 .../coffee/{RestoringVersions.coffee => RestoringVersions.js} | 0 .../coffee/helpers/{MockDocStoreApi.coffee => MockDocStoreApi.js} | 0 .../helpers/{MockDocUpdaterApi.coffee => MockDocUpdaterApi.js} | 0 .../coffee/helpers/{MockWebApi.coffee => MockWebApi.js} | 0 .../coffee/helpers/{TrackChangesApp.coffee => TrackChangesApp.js} | 0 .../helpers/{TrackChangesClient.coffee => TrackChangesClient.js} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/test/acceptance/coffee/{AppendingUpdatesTests.coffee => AppendingUpdatesTests.js} (100%) rename services/track-changes/test/acceptance/coffee/{ArchivingUpdatesTests.coffee => ArchivingUpdatesTests.js} (100%) rename services/track-changes/test/acceptance/coffee/{FlushingUpdatesTests.coffee => FlushingUpdatesTests.js} (100%) rename services/track-changes/test/acceptance/coffee/{GettingADiffTests.coffee => GettingADiffTests.js} (100%) rename services/track-changes/test/acceptance/coffee/{GettingUpdatesTests.coffee => GettingUpdatesTests.js} (100%) rename services/track-changes/test/acceptance/coffee/{LockManagerTests.coffee => LockManagerTests.js} (100%) rename services/track-changes/test/acceptance/coffee/{RestoringVersions.coffee => RestoringVersions.js} (100%) rename services/track-changes/test/acceptance/coffee/helpers/{MockDocStoreApi.coffee => MockDocStoreApi.js} (100%) rename services/track-changes/test/acceptance/coffee/helpers/{MockDocUpdaterApi.coffee => MockDocUpdaterApi.js} (100%) rename services/track-changes/test/acceptance/coffee/helpers/{MockWebApi.coffee => MockWebApi.js} (100%) rename services/track-changes/test/acceptance/coffee/helpers/{TrackChangesApp.coffee => TrackChangesApp.js} (100%) rename services/track-changes/test/acceptance/coffee/helpers/{TrackChangesClient.coffee => TrackChangesClient.js} (100%) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.coffee rename to services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.coffee rename to services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.coffee rename to services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee b/services/track-changes/test/acceptance/coffee/GettingADiffTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/GettingADiffTests.coffee rename to services/track-changes/test/acceptance/coffee/GettingADiffTests.js diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/GettingUpdatesTests.coffee rename to services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.coffee b/services/track-changes/test/acceptance/coffee/LockManagerTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/LockManagerTests.coffee rename to services/track-changes/test/acceptance/coffee/LockManagerTests.js diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.coffee b/services/track-changes/test/acceptance/coffee/RestoringVersions.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/RestoringVersions.coffee rename to services/track-changes/test/acceptance/coffee/RestoringVersions.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.coffee rename to services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee rename to services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/MockWebApi.coffee rename to services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.coffee rename to services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.coffee rename to services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js From 11c39cde65e120f8f46f6c25e7ec36802024c501 Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:35:39 +0100 Subject: [PATCH 476/549] decaffeinate: Convert AppendingUpdatesTests.coffee and 11 other files to JS --- .../coffee/AppendingUpdatesTests.js | 550 ++++++++++-------- .../coffee/ArchivingUpdatesTests.js | 279 +++++---- .../acceptance/coffee/FlushingUpdatesTests.js | 272 +++++---- .../acceptance/coffee/GettingADiffTests.js | 148 ++--- .../acceptance/coffee/GettingUpdatesTests.js | 251 ++++---- .../acceptance/coffee/LockManagerTests.js | 83 +-- .../acceptance/coffee/RestoringVersions.js | 125 ++-- .../coffee/helpers/MockDocStoreApi.js | 58 +- .../coffee/helpers/MockDocUpdaterApi.js | 83 ++- .../acceptance/coffee/helpers/MockWebApi.js | 86 ++- .../coffee/helpers/TrackChangesApp.js | 66 ++- .../coffee/helpers/TrackChangesClient.js | 282 +++++---- 12 files changed, 1331 insertions(+), 952 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js index 303f71660b..b0a9db982a 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js @@ -1,311 +1,387 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" -request = require "request" -rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); +const request = require("request"); +const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now -TrackChangesApp = require "./helpers/TrackChangesApp" -TrackChangesClient = require "./helpers/TrackChangesClient" -MockWebApi = require "./helpers/MockWebApi" +const TrackChangesApp = require("./helpers/TrackChangesApp"); +const TrackChangesClient = require("./helpers/TrackChangesClient"); +const MockWebApi = require("./helpers/MockWebApi"); -describe "Appending doc ops to the history", -> - before (done)-> - TrackChangesApp.ensureRunning done +describe("Appending doc ops to the history", function() { + before(done=> TrackChangesApp.ensureRunning(done)); - describe "when the history does not exist yet", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + describe("when the history does not exist yet", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 }, { - op: [{ i: "o", p: 4 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "o", p: 4 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 4 }, { - op: [{ i: "o", p: 5 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "o", p: 5 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 5 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should insert the compressed op into mongo", -> - expect(@updates[0].pack[0].op).to.deep.equal [{ + it("should insert the compressed op into mongo", function() { + return expect(this.updates[0].pack[0].op).to.deep.equal([{ p: 3, i: "foo" - }] + }]); + }); - it "should insert the correct version number into mongo", -> - expect(@updates[0].v).to.equal 5 + it("should insert the correct version number into mongo", function() { + return expect(this.updates[0].v).to.equal(5); + }); - it "should store the doc id", -> - expect(@updates[0].doc_id.toString()).to.equal @doc_id + it("should store the doc id", function() { + return expect(this.updates[0].doc_id.toString()).to.equal(this.doc_id); + }); - it "should store the project id", -> - expect(@updates[0].project_id.toString()).to.equal @project_id + it("should store the project id", function() { + return expect(this.updates[0].project_id.toString()).to.equal(this.project_id); + }); - it "should clear the doc from the DocsWithHistoryOps set", (done) -> - rclient.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, member) -> - member.should.equal 0 - done() - return null + return it("should clear the doc from the DocsWithHistoryOps set", function(done) { + rclient.sismember(`DocsWithHistoryOps:${this.project_id}`, this.doc_id, function(error, member) { + member.should.equal(0); + return done(); + }); + return null; + }); + }); - describe "when the history has already been started", -> - beforeEach (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + describe("when the history has already been started", function() { + beforeEach(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 }, { - op: [{ i: "o", p: 4 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "o", p: 4 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 4 }, { - op: [{ i: "o", p: 5 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "o", p: 5 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 5 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - describe "when the updates are recent and from the same user", -> - beforeEach (done) -> - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "b", p: 6 }] - meta: { ts: Date.now(), user_id: @user_id } + describe("when the updates are recent and from the same user", function() { + beforeEach(function(done) { + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "b", p: 6 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 6 }, { - op: [{ i: "a", p: 7 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "a", p: 7 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 7 }, { - op: [{ i: "r", p: 8 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "r", p: 8 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 8 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should combine all the updates into one pack", -> - expect(@updates[0].pack[1].op).to.deep.equal [{ + it("should combine all the updates into one pack", function() { + return expect(this.updates[0].pack[1].op).to.deep.equal([{ p: 6, i: "bar" - }] + }]); + }); - it "should insert the correct version number into mongo", -> - expect(@updates[0].v_end).to.equal 8 + return it("should insert the correct version number into mongo", function() { + return expect(this.updates[0].v_end).to.equal(8); + }); + }); - describe "when the updates are far apart", -> - beforeEach (done) -> - oneDay = 24 * 60 * 60 * 1000 - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "b", p: 6 }] - meta: { ts: Date.now() + oneDay, user_id: @user_id } + return describe("when the updates are far apart", function() { + beforeEach(function(done) { + const oneDay = 24 * 60 * 60 * 1000; + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "b", p: 6 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, v: 6 }, { - op: [{ i: "a", p: 7 }] - meta: { ts: Date.now() + oneDay, user_id: @user_id } + op: [{ i: "a", p: 7 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, v: 7 }, { - op: [{ i: "r", p: 8 }] - meta: { ts: Date.now() + oneDay, user_id: @user_id } + op: [{ i: "r", p: 8 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, v: 8 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should combine the updates into one pack", -> - expect(@updates[0].pack[0].op).to.deep.equal [{ + return it("should combine the updates into one pack", function() { + expect(this.updates[0].pack[0].op).to.deep.equal([{ p: 3, i: "foo" - }] - expect(@updates[0].pack[1].op).to.deep.equal [{ + }]); + return expect(this.updates[0].pack[1].op).to.deep.equal([{ p: 6, i: "bar" - }] + }]); + }); + }); +}); - describe "when the updates need processing in batches", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false - updates = [] - @expectedOp = [{ p:0, i: "" }] - for i in [0..250] - updates.push { - op: [{i: "a", p: 0}] - meta: { ts: Date.now(), user_id: @user_id } + describe("when the updates need processing in batches", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; + const updates = []; + this.expectedOp = [{ p:0, i: "" }]; + for (let i = 0; i <= 250; i++) { + updates.push({ + op: [{i: "a", p: 0}], + meta: { ts: Date.now(), user_id: this.user_id }, v: i - } - @expectedOp[0].i = "a" + @expectedOp[0].i + }); + this.expectedOp[0].i = `a${this.expectedOp[0].i}`; + } - TrackChangesClient.pushRawUpdates @project_id, @doc_id, updates, (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, updates, error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates1) => { + this.updates = updates1; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should concat the compressed op into mongo", -> - expect(@updates[0].pack.length).to.deep.equal 3 # batch size is 100 + it("should concat the compressed op into mongo", function() { + return expect(this.updates[0].pack.length).to.deep.equal(3); + }); // batch size is 100 - it "should insert the correct version number into mongo", -> - expect(@updates[0].v_end).to.equal 250 + return it("should insert the correct version number into mongo", function() { + return expect(this.updates[0].v_end).to.equal(250); + }); + }); - describe "when there are multiple ops in each update", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false - oneDay = 24 * 60 * 60 * 1000 - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }] - meta: { ts: Date.now(), user_id: @user_id } + describe("when there are multiple ops in each update", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; + const oneDay = 24 * 60 * 60 * 1000; + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 }, { - op: [{ i: "b", p: 6 }, { i: "a", p: 7 }, { i: "r", p: 8 }] - meta: { ts: Date.now() + oneDay, user_id: @user_id } + op: [{ i: "b", p: 6 }, { i: "a", p: 7 }, { i: "r", p: 8 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, v: 4 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should insert the compressed ops into mongo", -> - expect(@updates[0].pack[0].op).to.deep.equal [{ + it("should insert the compressed ops into mongo", function() { + expect(this.updates[0].pack[0].op).to.deep.equal([{ p: 3, i: "foo" - }] - expect(@updates[0].pack[1].op).to.deep.equal [{ + }]); + return expect(this.updates[0].pack[1].op).to.deep.equal([{ p: 6, i: "bar" - }] + }]); + }); - it "should insert the correct version numbers into mongo", -> - expect(@updates[0].pack[0].v).to.equal 3 - expect(@updates[0].pack[1].v).to.equal 4 + return it("should insert the correct version numbers into mongo", function() { + expect(this.updates[0].pack[0].v).to.equal(3); + return expect(this.updates[0].pack[1].v).to.equal(4); + }); + }); - describe "when there is a no-op update", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false - oneDay = 24 * 60 * 60 * 1000 - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [] - meta: { ts: Date.now(), user_id: @user_id } + describe("when there is a no-op update", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; + const oneDay = 24 * 60 * 60 * 1000; + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 }, { - op: [{ i: "foo", p: 3 }] - meta: { ts: Date.now() + oneDay, user_id: @user_id } + op: [{ i: "foo", p: 3 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, v: 4 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should insert the compressed no-op into mongo", -> - expect(@updates[0].pack[0].op).to.deep.equal [] + it("should insert the compressed no-op into mongo", function() { + return expect(this.updates[0].pack[0].op).to.deep.equal([]); + }); - it "should insert the compressed next update into mongo", -> - expect(@updates[0].pack[1].op).to.deep.equal [{ + it("should insert the compressed next update into mongo", function() { + return expect(this.updates[0].pack[1].op).to.deep.equal([{ p: 3, i: "foo" - }] + }]); + }); - it "should insert the correct version numbers into mongo", -> - expect(@updates[0].pack[0].v).to.equal 3 - expect(@updates[0].pack[1].v).to.equal 4 + return it("should insert the correct version numbers into mongo", function() { + expect(this.updates[0].pack[0].v).to.equal(3); + return expect(this.updates[0].pack[1].v).to.equal(4); + }); + }); - describe "when there is a comment update", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ c: "foo", p: 3 }, {d: "bar", p: 6}] - meta: { ts: Date.now(), user_id: @user_id } + describe("when there is a comment update", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ c: "foo", p: 3 }, {d: "bar", p: 6}], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should ignore the comment op", -> - expect(@updates[0].pack[0].op).to.deep.equal [{d: "bar", p: 6}] + it("should ignore the comment op", function() { + return expect(this.updates[0].pack[0].op).to.deep.equal([{d: "bar", p: 6}]); + }); - it "should insert the correct version numbers into mongo", -> - expect(@updates[0].pack[0].v).to.equal 3 + return it("should insert the correct version numbers into mongo", function() { + return expect(this.updates[0].pack[0].v).to.equal(3); + }); + }); - describe "when the project has versioning enabled", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: true + describe("when the project has versioning enabled", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: true}}; - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should not add a expiresAt entry in the update in mongo", -> - expect(@updates[0].expiresAt).to.be.undefined + return it("should not add a expiresAt entry in the update in mongo", function() { + return expect(this.updates[0].expiresAt).to.be.undefined; + }); + }); - describe "when the project does not have versioning enabled", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: false + return describe("when the project does not have versioning enabled", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) => - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { + this.updates = updates; + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should add a expiresAt entry in the update in mongo", -> - expect(@updates[0].expiresAt).to.exist + return it("should add a expiresAt entry in the update in mongo", function() { + return expect(this.updates[0].expiresAt).to.exist; + }); + }); +}); diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js index 282aaec2f5..cb832274f5 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js @@ -1,134 +1,181 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -db = mongojs.db -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" -request = require "request" -rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS202: Simplify dynamic range loops + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { db } = mongojs; +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); +const request = require("request"); +const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now -TrackChangesApp = require "./helpers/TrackChangesApp" -TrackChangesClient = require "./helpers/TrackChangesClient" -MockDocStoreApi = require "./helpers/MockDocStoreApi" -MockWebApi = require "./helpers/MockWebApi" +const TrackChangesApp = require("./helpers/TrackChangesApp"); +const TrackChangesClient = require("./helpers/TrackChangesClient"); +const MockDocStoreApi = require("./helpers/MockDocStoreApi"); +const MockWebApi = require("./helpers/MockWebApi"); -describe "Archiving updates", -> - before (done) -> - if Settings?.trackchanges?.s3?.key.length < 1 - message = new Error("s3 keys not setup, this test setup will fail") - return done(message) +describe("Archiving updates", function() { + before(function(done) { + if (__guard__(__guard__(Settings != null ? Settings.trackchanges : undefined, x1 => x1.s3), x => x.key.length) < 1) { + const message = new Error("s3 keys not setup, this test setup will fail"); + return done(message); + } - TrackChangesClient.waitForS3 done + return TrackChangesClient.waitForS3(done); + }); - before (done) -> - @now = Date.now() - @to = @now - @user_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() + before(function(done) { + this.now = Date.now(); + this.to = this.now; + this.user_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.project_id = ObjectId().toString(); - @minutes = 60 * 1000 - @hours = 60 * @minutes + this.minutes = 60 * 1000; + this.hours = 60 * this.minutes; - MockWebApi.projects[@project_id] = - features: + MockWebApi.projects[this.project_id] = { + features: { versioning: true - sinon.spy MockWebApi, "getProjectDetails" - - MockWebApi.users[@user_id] = @user = - email: "user@sharelatex.com" - first_name: "Leo" - last_name: "Lion" - id: @user_id - sinon.spy MockWebApi, "getUserInfo" - - MockDocStoreApi.docs[@doc_id] = @doc = - _id: @doc_id - project_id: @project_id - sinon.spy MockDocStoreApi, "getAllDoc" - - @updates = [] - for i in [0..512+10] - @updates.push { - op: [{ i: "a", p: 0 }] - meta: { ts: @now + (i-2048) * @hours, user_id: @user_id } - v: 2 * i + 1 } - @updates.push { - op: [{ i: "b", p: 0 }] - meta: { ts: @now + (i-2048) * @hours + 10*@minutes, user_id: @user_id } - v: 2 * i + 2 - } - TrackChangesApp.ensureRunning => - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> - throw error if error? - done() - return null + }; + sinon.spy(MockWebApi, "getProjectDetails"); - after (done) -> - MockWebApi.getUserInfo.restore() - db.docHistory.remove {project_id: ObjectId(@project_id)}, () => - db.docHistoryIndex.remove {project_id: ObjectId(@project_id)}, () => - TrackChangesClient.removeS3Doc @project_id, @doc_id, done + MockWebApi.users[this.user_id] = (this.user = { + email: "user@sharelatex.com", + first_name: "Leo", + last_name: "Lion", + id: this.user_id + }); + sinon.spy(MockWebApi, "getUserInfo"); - describe "archiving a doc's updates", -> - before (done) -> - TrackChangesClient.pushDocHistory @project_id, @doc_id, (error) -> - throw error if error? - done() - return null + MockDocStoreApi.docs[this.doc_id] = (this.doc = { + _id: this.doc_id, + project_id: this.project_id + }); + sinon.spy(MockDocStoreApi, "getAllDoc"); - it "should have one cached pack", (done) -> - db.docHistory.count { doc_id: ObjectId(@doc_id), expiresAt:{$exists:true}}, (error, count) -> - throw error if error? - count.should.equal 1 - done() + this.updates = []; + for (let i = 0, end = 512+10, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { + this.updates.push({ + op: [{ i: "a", p: 0 }], + meta: { ts: this.now + ((i-2048) * this.hours), user_id: this.user_id }, + v: (2 * i) + 1 + }); + this.updates.push({ + op: [{ i: "b", p: 0 }], + meta: { ts: this.now + ((i-2048) * this.hours) + (10*this.minutes), user_id: this.user_id }, + v: (2 * i) + 2 + }); + } + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { + if (error != null) { throw error; } + return TrackChangesClient.flushDoc(this.project_id, this.doc_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + }); + }); + return null; + }); - it "should have one remaining pack after cache is expired", (done) -> - db.docHistory.remove { - doc_id: ObjectId(@doc_id), + after(function(done) { + MockWebApi.getUserInfo.restore(); + return db.docHistory.remove({project_id: ObjectId(this.project_id)}, () => { + return db.docHistoryIndex.remove({project_id: ObjectId(this.project_id)}, () => { + return TrackChangesClient.removeS3Doc(this.project_id, this.doc_id, done); + }); + }); + }); + + describe("archiving a doc's updates", function() { + before(function(done) { + TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + return null; + }); + + it("should have one cached pack", function(done) { + return db.docHistory.count({ doc_id: ObjectId(this.doc_id), expiresAt:{$exists:true}}, function(error, count) { + if (error != null) { throw error; } + count.should.equal(1); + return done(); + }); + }); + + it("should have one remaining pack after cache is expired", function(done) { + return db.docHistory.remove({ + doc_id: ObjectId(this.doc_id), expiresAt:{$exists:true} - }, (err, result) => - throw error if error? - db.docHistory.count { doc_id: ObjectId(@doc_id)}, (error, count) -> - throw error if error? - count.should.equal 1 - done() + }, (err, result) => { + if (typeof error !== 'undefined' && error !== null) { throw error; } + return db.docHistory.count({ doc_id: ObjectId(this.doc_id)}, function(error, count) { + if (error != null) { throw error; } + count.should.equal(1); + return done(); + }); + }); + }); - it "should have a docHistoryIndex entry marked as inS3", (done) -> - db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) -> - throw error if error? - index.packs[0].inS3.should.equal true - done() + it("should have a docHistoryIndex entry marked as inS3", function(done) { + return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, function(error, index) { + if (error != null) { throw error; } + index.packs[0].inS3.should.equal(true); + return done(); + }); + }); - it "should have a docHistoryIndex entry with the last version", (done) -> - db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) -> - throw error if error? - index.packs[0].v_end.should.equal 1024 - done() + it("should have a docHistoryIndex entry with the last version", function(done) { + return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, function(error, index) { + if (error != null) { throw error; } + index.packs[0].v_end.should.equal(1024); + return done(); + }); + }); - it "should store 1024 doc changes in S3 in one pack", (done) -> - db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) => - throw error if error? - pack_id = index.packs[0]._id - TrackChangesClient.getS3Doc @project_id, @doc_id, pack_id, (error, doc) => - doc.n.should.equal 1024 - doc.pack.length.should.equal 1024 - done() + return it("should store 1024 doc changes in S3 in one pack", function(done) { + return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => { + if (error != null) { throw error; } + const pack_id = index.packs[0]._id; + return TrackChangesClient.getS3Doc(this.project_id, this.doc_id, pack_id, (error, doc) => { + doc.n.should.equal(1024); + doc.pack.length.should.equal(1024); + return done(); + }); + }); + }); + }); - describe "unarchiving a doc's updates", -> - before (done) -> - TrackChangesClient.pullDocHistory @project_id, @doc_id, (error) -> - throw error if error? - done() - return null + return describe("unarchiving a doc's updates", function() { + before(function(done) { + TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + return null; + }); - it "should restore both packs", (done) -> - db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) -> - throw error if error? - count.should.equal 2 - done() + return it("should restore both packs", function(done) { + return db.docHistory.count({ doc_id: ObjectId(this.doc_id) }, function(error, count) { + if (error != null) { throw error; } + count.should.equal(2); + return done(); + }); + }); + }); +}); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js index aaf9710d60..61d46ec98c 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js @@ -1,151 +1,191 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" -request = require "request" -rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); +const request = require("request"); +const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now -TrackChangesApp = require "./helpers/TrackChangesApp" -TrackChangesClient = require "./helpers/TrackChangesClient" -MockWebApi = require "./helpers/MockWebApi" +const TrackChangesApp = require("./helpers/TrackChangesApp"); +const TrackChangesClient = require("./helpers/TrackChangesClient"); +const MockWebApi = require("./helpers/MockWebApi"); -describe "Flushing updates", -> - before (done)-> - TrackChangesApp.ensureRunning done +describe("Flushing updates", function() { + before(done=> TrackChangesApp.ensureRunning(done)); - describe "flushing a doc's updates", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: true + describe("flushing a doc's updates", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: true}}; - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushDoc @project_id, @doc_id, (error) -> - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushDoc(this.project_id, this.doc_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should flush the op into mongo", (done) -> - TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates[0].pack[0].op).to.deep.equal [{ + return it("should flush the op into mongo", function(done) { + TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + expect(updates[0].pack[0].op).to.deep.equal([{ p: 3, i: "f" - }] - done() - return null + }]); + return done(); + }); + return null; + }); + }); - describe "flushing a project's updates", -> - describe "with versioning enabled", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() + return describe("flushing a project's updates", function() { + describe("with versioning enabled", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); - @weeks = 7 * 24 * 60 * 60 * 1000 + this.weeks = 7 * 24 * 60 * 60 * 1000; - MockWebApi.projects[@project_id] = - features: + MockWebApi.projects[this.project_id] = { + features: { versioning: true + } + }; - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "g", p: 2 }] - meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id } + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "g", p: 2 }], + meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id }, v: 2 }, { - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushProject @project_id, (error) -> - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushProject(this.project_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should not mark the updates for deletion", (done) -> - TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates[0].expiresAt).to.not.exist - done() - return null + it("should not mark the updates for deletion", function(done) { + TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + expect(updates[0].expiresAt).to.not.exist; + return done(); + }); + return null; + }); - it "should preserve history forever", (done) -> - TrackChangesClient.getProjectMetaData @project_id, (error, project) -> - expect(project.preserveHistory).to.equal true - done() - return null + return it("should preserve history forever", function(done) { + TrackChangesClient.getProjectMetaData(this.project_id, function(error, project) { + expect(project.preserveHistory).to.equal(true); + return done(); + }); + return null; + }); + }); - describe "without versioning enabled", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() + describe("without versioning enabled", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); - @weeks = 7 * 24 * 60 * 60 * 1000 + this.weeks = 7 * 24 * 60 * 60 * 1000; - MockWebApi.projects[@project_id] = - features: + MockWebApi.projects[this.project_id] = { + features: { versioning: false + } + }; - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "g", p: 2 }] - meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id } + TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "g", p: 2 }], + meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id }, v: 2 }, { - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushProject @project_id, (error) -> - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushProject(this.project_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should mark the updates for deletion", (done) -> - TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates[0].expiresAt).to.exist - done() - return null + return it("should mark the updates for deletion", function(done) { + TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + expect(updates[0].expiresAt).to.exist; + return done(); + }); + return null; + }); + }); - describe "without versioning enabled but with preserveHistory set to true", -> - before (done) -> - @project_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @user_id = ObjectId().toString() + return describe("without versioning enabled but with preserveHistory set to true", function() { + before(function(done) { + this.project_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.user_id = ObjectId().toString(); - @weeks = 7 * 24 * 60 * 60 * 1000 + this.weeks = 7 * 24 * 60 * 60 * 1000; - MockWebApi.projects[@project_id] = - features: + MockWebApi.projects[this.project_id] = { + features: { versioning: false + } + }; - TrackChangesClient.setPreserveHistoryForProject @project_id, (error) => - throw error if error? - TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{ - op: [{ i: "g", p: 2 }] - meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id } + TrackChangesClient.setPreserveHistoryForProject(this.project_id, error => { + if (error != null) { throw error; } + return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ + op: [{ i: "g", p: 2 }], + meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id }, v: 2 }, { - op: [{ i: "f", p: 3 }] - meta: { ts: Date.now(), user_id: @user_id } + op: [{ i: "f", p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, v: 3 - }], (error) => - throw error if error? - TrackChangesClient.flushProject @project_id, (error) -> - throw error if error? - done() - return null + }], error => { + if (error != null) { throw error; } + return TrackChangesClient.flushProject(this.project_id, function(error) { + if (error != null) { throw error; } + return done(); + }); + }); + }); + return null; + }); - it "should not mark the updates for deletion", (done) -> - TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) -> - expect(updates[0].expiresAt).to.not.exist - done() - return null + return it("should not mark the updates for deletion", function(done) { + TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + expect(updates[0].expiresAt).to.not.exist; + return done(); + }); + return null; + }); + }); + }); +}); diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.js b/services/track-changes/test/acceptance/coffee/GettingADiffTests.js index 1e9bed197b..ab7c26372c 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.js @@ -1,84 +1,100 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -db = mongojs.db -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { db } = mongojs; +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); -TrackChangesApp = require "./helpers/TrackChangesApp" -TrackChangesClient = require "./helpers/TrackChangesClient" -MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" -MockWebApi = require "./helpers/MockWebApi" +const TrackChangesApp = require("./helpers/TrackChangesApp"); +const TrackChangesClient = require("./helpers/TrackChangesClient"); +const MockDocUpdaterApi = require("./helpers/MockDocUpdaterApi"); +const MockWebApi = require("./helpers/MockWebApi"); -describe "Getting a diff", -> +describe("Getting a diff", function() { - beforeEach (done) -> - sinon.spy MockDocUpdaterApi, "getDoc" + beforeEach(function(done) { + sinon.spy(MockDocUpdaterApi, "getDoc"); - @now = Date.now() - @from = @now - 100000000 - @to = @now - @user_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: true + this.now = Date.now(); + this.from = this.now - 100000000; + this.to = this.now; + this.user_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.project_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: true}}; - MockWebApi.users[@user_id] = @user = - email: "user@sharelatex.com" - first_name: "Leo" - last_name: "Lion" - id: @user_id - sinon.spy MockWebApi, "getUserInfo" + MockWebApi.users[this.user_id] = (this.user = { + email: "user@sharelatex.com", + first_name: "Leo", + last_name: "Lion", + id: this.user_id + }); + sinon.spy(MockWebApi, "getUserInfo"); - twoMinutes = 2 * 60 * 1000 + const twoMinutes = 2 * 60 * 1000; - @updates = [{ - op: [{ i: "one ", p: 0 }] - meta: { ts: @from - twoMinutes, user_id: @user_id } + this.updates = [{ + op: [{ i: "one ", p: 0 }], + meta: { ts: this.from - twoMinutes, user_id: this.user_id }, v: 3 }, { - op: [{ i: "two ", p: 4 }] - meta: { ts: @from + twoMinutes, user_id: @user_id } - v: @fromVersion = 4 + op: [{ i: "two ", p: 4 }], + meta: { ts: this.from + twoMinutes, user_id: this.user_id }, + v: (this.fromVersion = 4) }, { - op: [{ i: "three ", p: 8 }] - meta: { ts: @to - twoMinutes, user_id: @user_id } - v: @toVersion = 5 + op: [{ i: "three ", p: 8 }], + meta: { ts: this.to - twoMinutes, user_id: this.user_id }, + v: (this.toVersion = 5) }, { - op: [{ i: "four", p: 14 }] - meta: { ts: @to + twoMinutes, user_id: @user_id } + op: [{ i: "four", p: 14 }], + meta: { ts: this.to + twoMinutes, user_id: this.user_id }, v: 6 - }] - @lines = ["one two three four"] - @expected_diff = [ - { u: "one " } - { i: "two three ", meta: { start_ts: @from + twoMinutes, end_ts: @to - twoMinutes, user: @user } } - ] + }]; + this.lines = ["one two three four"]; + this.expected_diff = [ + { u: "one " }, + { i: "two three ", meta: { start_ts: this.from + twoMinutes, end_ts: this.to - twoMinutes, user: this.user } } + ]; - MockDocUpdaterApi.docs[@doc_id] = - lines: @lines + MockDocUpdaterApi.docs[this.doc_id] = { + lines: this.lines, version: 7 - TrackChangesApp.ensureRunning => - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.getDiff @project_id, @doc_id, @fromVersion, @toVersion, (error, diff) => - throw error if error? - @diff = diff.diff - done() - return null + }; + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { + if (error != null) { throw error; } + return TrackChangesClient.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, (error, diff) => { + if (error != null) { throw error; } + this.diff = diff.diff; + return done(); + }); + }); + }); + return null; + }); - afterEach () -> - MockDocUpdaterApi.getDoc.restore() - MockWebApi.getUserInfo.restore() - return null + afterEach(function() { + MockDocUpdaterApi.getDoc.restore(); + MockWebApi.getUserInfo.restore(); + return null; + }); - it "should return the diff", -> - expect(@diff).to.deep.equal @expected_diff + it("should return the diff", function() { + return expect(this.diff).to.deep.equal(this.expected_diff); + }); - it "should get the doc from the doc updater", -> + return it("should get the doc from the doc updater", function() { MockDocUpdaterApi.getDoc - .calledWith(@project_id, @doc_id) - .should.equal true - return null + .calledWith(this.project_id, this.doc_id) + .should.equal(true); + return null; + }); +}); diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js index 4a9e9586d6..a4519d11f3 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js @@ -1,126 +1,157 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -db = mongojs.db -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { db } = mongojs; +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); -TrackChangesApp = require "./helpers/TrackChangesApp" -TrackChangesClient = require "./helpers/TrackChangesClient" -MockWebApi = require "./helpers/MockWebApi" +const TrackChangesApp = require("./helpers/TrackChangesApp"); +const TrackChangesClient = require("./helpers/TrackChangesClient"); +const MockWebApi = require("./helpers/MockWebApi"); -describe "Getting updates", -> - before (done) -> - @now = Date.now() - @to = @now - @user_id = ObjectId().toString() - @deleted_user_id = 'deleted_user' - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() +describe("Getting updates", function() { + before(function(done) { + this.now = Date.now(); + this.to = this.now; + this.user_id = ObjectId().toString(); + this.deleted_user_id = 'deleted_user'; + this.doc_id = ObjectId().toString(); + this.project_id = ObjectId().toString(); - @minutes = 60 * 1000 - @hours = 60 * @minutes + this.minutes = 60 * 1000; + this.hours = 60 * this.minutes; - MockWebApi.projects[@project_id] = - features: + MockWebApi.projects[this.project_id] = { + features: { versioning: true - - MockWebApi.users[@user_id] = @user = - email: "user@sharelatex.com" - first_name: "Leo" - last_name: "Lion" - id: @user_id - sinon.spy MockWebApi, "getUserInfo" - - @updates = [] - for i in [0..9] - @updates.push { - op: [{ i: "a", p: 0 }] - meta: { ts: @now - (9 - i) * @hours - 2 * @minutes, user_id: @user_id } - v: 2 * i + 1 } - @updates.push { - op: [{ i: "b", p: 0 }] - meta: { ts: @now - (9 - i) * @hours, user_id: @user_id } - v: 2 * i + 2 - } - @updates[0].meta.user_id = @deleted_user_id + }; - TrackChangesApp.ensureRunning => - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - done() - return null + MockWebApi.users[this.user_id] = (this.user = { + email: "user@sharelatex.com", + first_name: "Leo", + last_name: "Lion", + id: this.user_id + }); + sinon.spy(MockWebApi, "getUserInfo"); - after: () -> - MockWebApi.getUserInfo.restore() - return null + this.updates = []; + for (let i = 0; i <= 9; i++) { + this.updates.push({ + op: [{ i: "a", p: 0 }], + meta: { ts: this.now - ((9 - i) * this.hours) - (2 * this.minutes), user_id: this.user_id }, + v: (2 * i) + 1 + }); + this.updates.push({ + op: [{ i: "b", p: 0 }], + meta: { ts: this.now - ((9 - i) * this.hours), user_id: this.user_id }, + v: (2 * i) + 2 + }); + } + this.updates[0].meta.user_id = this.deleted_user_id; - describe "getting updates up to the limit", -> - before (done) -> - TrackChangesClient.getUpdates @project_id, { before: @to + 1, min_count: 3 }, (error, body) => - throw error if error? - @updates = body.updates - done() - return null + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { + if (error != null) { throw error; } + return done(); + }); + }); + return null; + }); - it "should fetch the user details from the web api", -> - MockWebApi.getUserInfo - .calledWith(@user_id) - .should.equal true + ({ + after() { + MockWebApi.getUserInfo.restore(); + return null; + } + }); - it "should return at least the min_count number of summarized updates", -> - docs1 = {} - docs1[@doc_id] = toV: 20, fromV: 19 - docs2 = {} - docs2[@doc_id] = toV: 18, fromV: 17 - docs3 = {} - docs3[@doc_id] = toV: 16, fromV: 15 - expect(@updates.slice(0,3)).to.deep.equal [{ - docs: docs1 - meta: - start_ts: @to - 2 * @minutes - end_ts: @to - users: [@user] + describe("getting updates up to the limit", function() { + before(function(done) { + TrackChangesClient.getUpdates(this.project_id, { before: this.to + 1, min_count: 3 }, (error, body) => { + if (error != null) { throw error; } + this.updates = body.updates; + return done(); + }); + return null; + }); + + it("should fetch the user details from the web api", function() { + return MockWebApi.getUserInfo + .calledWith(this.user_id) + .should.equal(true); + }); + + return it("should return at least the min_count number of summarized updates", function() { + const docs1 = {}; + docs1[this.doc_id] = {toV: 20, fromV: 19}; + const docs2 = {}; + docs2[this.doc_id] = {toV: 18, fromV: 17}; + const docs3 = {}; + docs3[this.doc_id] = {toV: 16, fromV: 15}; + return expect(this.updates.slice(0,3)).to.deep.equal([{ + docs: docs1, + meta: { + start_ts: this.to - (2 * this.minutes), + end_ts: this.to, + users: [this.user] + } }, { - docs: docs2 - meta: - start_ts: @to - 1 * @hours - 2 * @minutes - end_ts: @to - 1 * @hours - users: [@user] + docs: docs2, + meta: { + start_ts: this.to - (1 * this.hours) - (2 * this.minutes), + end_ts: this.to - (1 * this.hours), + users: [this.user] + } }, { - docs: docs3 - meta: - start_ts: @to - 2 * @hours - 2 * @minutes - end_ts: @to - 2 * @hours - users: [@user] - }] + docs: docs3, + meta: { + start_ts: this.to - (2 * this.hours) - (2 * this.minutes), + end_ts: this.to - (2 * this.hours), + users: [this.user] + } + }]); + }); +}); - describe "getting updates beyond the end of the database", -> - before (done) -> - TrackChangesClient.getUpdates @project_id, { before: @to - 8 * @hours + 1, min_count: 30 }, (error, body) => - throw error if error? - @updates = body.updates - done() - return null + return describe("getting updates beyond the end of the database", function() { + before(function(done) { + TrackChangesClient.getUpdates(this.project_id, { before: (this.to - (8 * this.hours)) + 1, min_count: 30 }, (error, body) => { + if (error != null) { throw error; } + this.updates = body.updates; + return done(); + }); + return null; + }); - it "should return as many updates as it can", -> - docs1 = {} - docs1[@doc_id] = toV: 4, fromV: 3 - docs2 = {} - docs2[@doc_id] = toV: 2, fromV: 1 - expect(@updates).to.deep.equal [{ - docs: docs1 - meta: - start_ts: @to - 8 * @hours - 2 * @minutes - end_ts: @to - 8 * @hours - users: [@user] + return it("should return as many updates as it can", function() { + const docs1 = {}; + docs1[this.doc_id] = {toV: 4, fromV: 3}; + const docs2 = {}; + docs2[this.doc_id] = {toV: 2, fromV: 1}; + return expect(this.updates).to.deep.equal([{ + docs: docs1, + meta: { + start_ts: this.to - (8 * this.hours) - (2 * this.minutes), + end_ts: this.to - (8 * this.hours), + users: [this.user] + } }, { - docs: docs2 - meta: - start_ts: @to - 9 * @hours - 2 * @minutes - end_ts: @to - 9 * @hours - users: [@user, null] - }] + docs: docs2, + meta: { + start_ts: this.to - (9 * this.hours) - (2 * this.minutes), + end_ts: this.to - (9 * this.hours), + users: [this.user, null] + } + }]); + }); +}); +}); diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.js b/services/track-changes/test/acceptance/coffee/LockManagerTests.js index 5bf01e4d9c..84a5c51209 100644 --- a/services/track-changes/test/acceptance/coffee/LockManagerTests.js +++ b/services/track-changes/test/acceptance/coffee/LockManagerTests.js @@ -1,39 +1,54 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" -LockManager = require "../../../app/js/LockManager" -rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now -TrackChangesApp = require "./helpers/TrackChangesApp" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); +const LockManager = require("../../../app/js/LockManager"); +const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now +const TrackChangesApp = require("./helpers/TrackChangesApp"); -describe "Locking document", -> +describe("Locking document", function() { - before (done)-> - TrackChangesApp.ensureRunning done - return null + before(function(done){ + TrackChangesApp.ensureRunning(done); + return null; + }); - describe "when the lock has expired in redis", -> - before (done) -> - LockManager.LOCK_TTL = 1 # second - LockManager.runWithLock "doc123", (releaseA) => - # we create a lock A and allow it to expire in redis - setTimeout () -> - # now we create a new lock B and try to release A - LockManager.runWithLock "doc123", (releaseB) => - releaseA() # try to release lock A to see if it wipes out lock B - , (error) -> - # we never release lock B so nothing should happen here - , 1500 # enough time to wait until the lock has expired - , (error) -> - # we get here after trying to release lock A + return describe("when the lock has expired in redis", function() { + before(function(done) { + LockManager.LOCK_TTL = 1; // second + LockManager.runWithLock("doc123", releaseA => { + // we create a lock A and allow it to expire in redis + return setTimeout(() => + // now we create a new lock B and try to release A + LockManager.runWithLock("doc123", releaseB => { + return releaseA(); + } // try to release lock A to see if it wipes out lock B + , function(error) {}) + + // we never release lock B so nothing should happen here + , 1500); + } // enough time to wait until the lock has expired + , error => + // we get here after trying to release lock A done() - return null + ); + return null; + }); - it "the new lock should not be removed by the expired locker", (done) -> - LockManager.checkLock "doc123", (err, isFree) -> - expect(isFree).to.equal false - done() - return null + return it("the new lock should not be removed by the expired locker", function(done) { + LockManager.checkLock("doc123", function(err, isFree) { + expect(isFree).to.equal(false); + return done(); + }); + return null; + }); + }); +}); diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.js b/services/track-changes/test/acceptance/coffee/RestoringVersions.js index 647db4952b..b9da0d7045 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.js +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.js @@ -1,74 +1,89 @@ -sinon = require "sinon" -chai = require("chai") -chai.should() -expect = chai.expect -mongojs = require "../../../app/js/mongojs" -db = mongojs.db -ObjectId = mongojs.ObjectId -Settings = require "settings-sharelatex" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const sinon = require("sinon"); +const chai = require("chai"); +chai.should(); +const { expect } = chai; +const mongojs = require("../../../app/js/mongojs"); +const { db } = mongojs; +const { ObjectId } = mongojs; +const Settings = require("settings-sharelatex"); -TrackChangesApp = require "./helpers/TrackChangesApp" -TrackChangesClient = require "./helpers/TrackChangesClient" -MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi" -MockWebApi = require "./helpers/MockWebApi" +const TrackChangesApp = require("./helpers/TrackChangesApp"); +const TrackChangesClient = require("./helpers/TrackChangesClient"); +const MockDocUpdaterApi = require("./helpers/MockDocUpdaterApi"); +const MockWebApi = require("./helpers/MockWebApi"); -describe "Restoring a version", -> - before (done) -> - sinon.spy MockDocUpdaterApi, "setDoc" +describe("Restoring a version", function() { + before(function(done) { + sinon.spy(MockDocUpdaterApi, "setDoc"); - @now = Date.now() - @user_id = ObjectId().toString() - @doc_id = ObjectId().toString() - @project_id = ObjectId().toString() - MockWebApi.projects[@project_id] = features: versioning: true + this.now = Date.now(); + this.user_id = ObjectId().toString(); + this.doc_id = ObjectId().toString(); + this.project_id = ObjectId().toString(); + MockWebApi.projects[this.project_id] = {features: {versioning: true}}; - minutes = 60 * 1000 + const minutes = 60 * 1000; - @updates = [{ - op: [{ i: "one ", p: 0 }] - meta: { ts: @now - 6 * minutes, user_id: @user_id } + this.updates = [{ + op: [{ i: "one ", p: 0 }], + meta: { ts: this.now - (6 * minutes), user_id: this.user_id }, v: 3 }, { - op: [{ i: "two ", p: 4 }] - meta: { ts: @now - 4 * minutes, user_id: @user_id } + op: [{ i: "two ", p: 4 }], + meta: { ts: this.now - (4 * minutes), user_id: this.user_id }, v: 4 }, { - op: [{ i: "three ", p: 8 }] - meta: { ts: @now - 2 * minutes, user_id: @user_id } + op: [{ i: "three ", p: 8 }], + meta: { ts: this.now - (2 * minutes), user_id: this.user_id }, v: 5 }, { - op: [{ i: "four", p: 14 }] - meta: { ts: @now, user_id: @user_id } + op: [{ i: "four", p: 14 }], + meta: { ts: this.now, user_id: this.user_id }, v: 6 - }] - @lines = ["one two three four"] - @restored_lines = ["one two "] - @beforeVersion = 5 + }]; + this.lines = ["one two three four"]; + this.restored_lines = ["one two "]; + this.beforeVersion = 5; - MockWebApi.users[@user_id] = @user = - email: "user@sharelatex.com" - first_name: "Leo" - last_name: "Lion" - id: @user_id + MockWebApi.users[this.user_id] = (this.user = { + email: "user@sharelatex.com", + first_name: "Leo", + last_name: "Lion", + id: this.user_id + }); - MockDocUpdaterApi.docs[@doc_id] = - lines: @lines + MockDocUpdaterApi.docs[this.doc_id] = { + lines: this.lines, version: 7 + }; - TrackChangesApp.ensureRunning => - TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) => - throw error if error? - TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) => - throw error if error? - done() - return null + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { + if (error != null) { throw error; } + return TrackChangesClient.restoreDoc(this.project_id, this.doc_id, this.beforeVersion, this.user_id, error => { + if (error != null) { throw error; } + return done(); + }); + }); + }); + return null; + }); - after () -> - MockDocUpdaterApi.setDoc.restore() - return null + after(function() { + MockDocUpdaterApi.setDoc.restore(); + return null; + }); - it "should set the doc in the doc updater", -> + return it("should set the doc in the doc updater", function() { MockDocUpdaterApi.setDoc - .calledWith(@project_id, @doc_id, @restored_lines, @user_id, true) - .should.equal true - return null + .calledWith(this.project_id, this.doc_id, this.restored_lines, this.user_id, true) + .should.equal(true); + return null; + }); +}); diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js index 0a0ddc72e3..8aec89535a 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js @@ -1,27 +1,43 @@ -express = require("express") -app = express() +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let MockDocUpdaterApi; +const express = require("express"); +const app = express(); -module.exports = MockDocUpdaterApi = - docs: {} +module.exports = (MockDocUpdaterApi = { + docs: {}, - getAllDoc: (project_id, callback = (error) ->) -> - callback null, @docs + getAllDoc(project_id, callback) { + if (callback == null) { callback = function(error) {}; } + return callback(null, this.docs); + }, - run: () -> - app.get "/project/:project_id/doc", (req, res, next) => - @getAllDoc req.params.project_id, (error, docs) -> - if error? - res.send 500 - if !docs? - res.send 404 - else - res.send JSON.stringify docs + run() { + app.get("/project/:project_id/doc", (req, res, next) => { + return this.getAllDoc(req.params.project_id, function(error, docs) { + if (error != null) { + res.send(500); + } + if ((docs == null)) { + return res.send(404); + } else { + return res.send(JSON.stringify(docs)); + } + }); + }); - app.listen 3016, (error) -> - throw error if error? - .on "error", (error) -> - console.error "error starting MockDocStoreApi:", error.message - process.exit(1) + return app.listen(3016, function(error) { + if (error != null) { throw error; } + }).on("error", function(error) { + console.error("error starting MockDocStoreApi:", error.message); + return process.exit(1); + }); + } +}); -MockDocUpdaterApi.run() +MockDocUpdaterApi.run(); diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js index 2c87fde358..27b62576a1 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js @@ -1,40 +1,61 @@ -express = require("express") -app = express() +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let MockDocUpdaterApi; +const express = require("express"); +const app = express(); -module.exports = MockDocUpdaterApi = - docs: {} +module.exports = (MockDocUpdaterApi = { + docs: {}, - getDoc: (project_id, doc_id, callback = (error) ->) -> - callback null, @docs[doc_id] + getDoc(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return callback(null, this.docs[doc_id]); + }, - setDoc: (project_id, doc_id, lines, user_id, undoing, callback = (error) ->) -> - @docs[doc_id] ||= {} - @docs[doc_id].lines = lines - callback() + setDoc(project_id, doc_id, lines, user_id, undoing, callback) { + if (callback == null) { callback = function(error) {}; } + if (!this.docs[doc_id]) { this.docs[doc_id] = {}; } + this.docs[doc_id].lines = lines; + return callback(); + }, - run: () -> - app.get "/project/:project_id/doc/:doc_id", (req, res, next) => - @getDoc req.params.project_id, req.params.doc_id, (error, doc) -> - if error? - res.send 500 - if !doc? - res.send 404 - else - res.send JSON.stringify doc + run() { + app.get("/project/:project_id/doc/:doc_id", (req, res, next) => { + return this.getDoc(req.params.project_id, req.params.doc_id, function(error, doc) { + if (error != null) { + res.send(500); + } + if ((doc == null)) { + return res.send(404); + } else { + return res.send(JSON.stringify(doc)); + } + }); + }); - app.post "/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => - @setDoc req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, req.body.undoing, (errr, doc) -> - if error? - res.send 500 - else - res.send 204 + app.post("/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => { + return this.setDoc(req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, req.body.undoing, function(errr, doc) { + if (typeof error !== 'undefined' && error !== null) { + return res.send(500); + } else { + return res.send(204); + } + }); + }); - app.listen 3003, (error) -> - throw error if error? - .on "error", (error) -> - console.error "error starting MockDocUpdaterApi:", error.message - process.exit(1) + return app.listen(3003, function(error) { + if (error != null) { throw error; } + }).on("error", function(error) { + console.error("error starting MockDocUpdaterApi:", error.message); + return process.exit(1); + }); + } +}); -MockDocUpdaterApi.run() +MockDocUpdaterApi.run(); diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js index 97b4ea33ca..41bb890a86 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js @@ -1,41 +1,63 @@ -express = require("express") -app = express() +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let MockWebApi; +const express = require("express"); +const app = express(); -module.exports = MockWebApi = - users: {} +module.exports = (MockWebApi = { + users: {}, - projects: {} + projects: {}, - getUserInfo: (user_id, callback = (error) ->) -> - callback null, @users[user_id] or null + getUserInfo(user_id, callback) { + if (callback == null) { callback = function(error) {}; } + return callback(null, this.users[user_id] || null); + }, - getProjectDetails: (project_id, callback = (error, project) ->) -> - callback null, @projects[project_id] + getProjectDetails(project_id, callback) { + if (callback == null) { callback = function(error, project) {}; } + return callback(null, this.projects[project_id]); + }, - run: () -> - app.get "/user/:user_id/personal_info", (req, res, next) => - @getUserInfo req.params.user_id, (error, user) -> - if error? - res.send 500 - if !user? - res.send 404 - else - res.send JSON.stringify user + run() { + app.get("/user/:user_id/personal_info", (req, res, next) => { + return this.getUserInfo(req.params.user_id, function(error, user) { + if (error != null) { + res.send(500); + } + if ((user == null)) { + return res.send(404); + } else { + return res.send(JSON.stringify(user)); + } + }); + }); - app.get "/project/:project_id/details", (req, res, next) => - @getProjectDetails req.params.project_id, (error, project) -> - if error? - res.send 500 - if !project? - res.send 404 - else - res.send JSON.stringify project + app.get("/project/:project_id/details", (req, res, next) => { + return this.getProjectDetails(req.params.project_id, function(error, project) { + if (error != null) { + res.send(500); + } + if ((project == null)) { + return res.send(404); + } else { + return res.send(JSON.stringify(project)); + } + }); + }); - app.listen 3000, (error) -> - throw error if error? - .on "error", (error) -> - console.error "error starting MockWebApiServer:", error.message - process.exit(1) + return app.listen(3000, function(error) { + if (error != null) { throw error; } + }).on("error", function(error) { + console.error("error starting MockWebApiServer:", error.message); + return process.exit(1); + }); + } +}); -MockWebApi.run() +MockWebApi.run(); diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js index e46d35768a..31bf505b33 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js @@ -1,24 +1,46 @@ -app = require('../../../../app') -require("logger-sharelatex") -logger = require("logger-sharelatex") -Settings = require("settings-sharelatex") +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const app = require('../../../../app'); +require("logger-sharelatex"); +const logger = require("logger-sharelatex"); +const Settings = require("settings-sharelatex"); -module.exports = - running: false - initing: false - callbacks: [] - ensureRunning: (callback = (error) ->) -> - if @running - return callback() - else if @initing - @callbacks.push callback - else - @initing = true - @callbacks.push callback - app.listen Settings.internal?.trackchanges?.port, "localhost", (error) => - throw error if error? - @running = true - logger.log("track changes running in dev mode") +module.exports = { + running: false, + initing: false, + callbacks: [], + ensureRunning(callback) { + if (callback == null) { callback = function(error) {}; } + if (this.running) { + return callback(); + } else if (this.initing) { + return this.callbacks.push(callback); + } else { + this.initing = true; + this.callbacks.push(callback); + return app.listen(__guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x => x.port), "localhost", error => { + if (error != null) { throw error; } + this.running = true; + logger.log("track changes running in dev mode"); - for callback in @callbacks - callback() \ No newline at end of file + return (() => { + const result = []; + for (callback of Array.from(this.callbacks)) { + result.push(callback()); + } + return result; + })(); + }); + } + } +}; +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js index 1ae07426b2..3cf1a26a33 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js @@ -1,144 +1,202 @@ -async = require 'async' -zlib = require 'zlib' -request = require "request" -Settings = require "settings-sharelatex" -rclient = require("redis-sharelatex").createClient(Settings.redis.history) # Only works locally for now -Keys = Settings.redis.history.key_schema -{db, ObjectId} = require "../../../../app/js/mongojs" +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +let TrackChangesClient; +const async = require('async'); +const zlib = require('zlib'); +const request = require("request"); +const Settings = require("settings-sharelatex"); +const rclient = require("redis-sharelatex").createClient(Settings.redis.history); // Only works locally for now +const Keys = Settings.redis.history.key_schema; +const {db, ObjectId} = require("../../../../app/js/mongojs"); -aws = require "aws-sdk" -s3 = new aws.S3( - accessKeyId: Settings.trackchanges.s3.key - secretAccessKey: Settings.trackchanges.s3.secret - endpoint: Settings.trackchanges.s3.endpoint +const aws = require("aws-sdk"); +const s3 = new aws.S3({ + accessKeyId: Settings.trackchanges.s3.key, + secretAccessKey: Settings.trackchanges.s3.secret, + endpoint: Settings.trackchanges.s3.endpoint, s3ForcePathStyle: Settings.trackchanges.s3.pathStyle -) -S3_BUCKET = Settings.trackchanges.stores.doc_history +}); +const S3_BUCKET = Settings.trackchanges.stores.doc_history; -module.exports = TrackChangesClient = - flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) -> - TrackChangesClient.flushDoc project_id, doc_id, (error) -> - return callback(error) if error? - TrackChangesClient.getCompressedUpdates doc_id, callback +module.exports = (TrackChangesClient = { + flushAndGetCompressedUpdates(project_id, doc_id, callback) { + if (callback == null) { callback = function(error, updates) {}; } + return TrackChangesClient.flushDoc(project_id, doc_id, function(error) { + if (error != null) { return callback(error); } + return TrackChangesClient.getCompressedUpdates(doc_id, callback); + }); + }, - flushDoc: (project_id, doc_id, callback = (error) ->) -> - request.post { - url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/flush" - }, (error, response, body) => - response.statusCode.should.equal 204 - callback(error) + flushDoc(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return request.post({ + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush` + }, (error, response, body) => { + response.statusCode.should.equal(204); + return callback(error); + }); + }, - flushProject: (project_id, callback = (error) ->) -> - request.post { - url: "http://localhost:3015/project/#{project_id}/flush" - }, (error, response, body) => - response.statusCode.should.equal 204 - callback(error) + flushProject(project_id, callback) { + if (callback == null) { callback = function(error) {}; } + return request.post({ + url: `http://localhost:3015/project/${project_id}/flush` + }, (error, response, body) => { + response.statusCode.should.equal(204); + return callback(error); + }); + }, - getCompressedUpdates: (doc_id, callback = (error, updates) ->) -> - db.docHistory - .find(doc_id: ObjectId(doc_id)) - .sort("meta.end_ts": 1) - .toArray callback + getCompressedUpdates(doc_id, callback) { + if (callback == null) { callback = function(error, updates) {}; } + return db.docHistory + .find({doc_id: ObjectId(doc_id)}) + .sort({"meta.end_ts": 1}) + .toArray(callback); + }, - getProjectMetaData: (project_id, callback = (error, updates) ->) -> - db.projectHistoryMetaData - .find { + getProjectMetaData(project_id, callback) { + if (callback == null) { callback = function(error, updates) {}; } + return db.projectHistoryMetaData + .find({ project_id: ObjectId(project_id) }, - (error, projects) -> - callback error, projects[0] + (error, projects) => callback(error, projects[0])); + }, - setPreserveHistoryForProject: (project_id, callback = (error) ->) -> - db.projectHistoryMetaData.update { + setPreserveHistoryForProject(project_id, callback) { + if (callback == null) { callback = function(error) {}; } + return db.projectHistoryMetaData.update({ project_id: ObjectId(project_id) }, { $set: { preserveHistory: true } }, { upsert: true - }, callback + }, callback); + }, - pushRawUpdates: (project_id, doc_id, updates, callback = (error) ->) -> - rclient.sadd Keys.docsWithHistoryOps({project_id}), doc_id, (error) -> - return callback(error) if error? - rclient.rpush Keys.uncompressedHistoryOps({doc_id}), (JSON.stringify(u) for u in updates)..., callback + pushRawUpdates(project_id, doc_id, updates, callback) { + if (callback == null) { callback = function(error) {}; } + return rclient.sadd(Keys.docsWithHistoryOps({project_id}), doc_id, function(error) { + if (error != null) { return callback(error); } + return rclient.rpush(Keys.uncompressedHistoryOps({doc_id}), ...Array.from(((Array.from(updates).map((u) => JSON.stringify(u))))), callback); + }); + }, - getDiff: (project_id, doc_id, from, to, callback = (error, diff) ->) -> - request.get { - url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/diff?from=#{from}&to=#{to}" - }, (error, response, body) => - response.statusCode.should.equal 200 - callback null, JSON.parse(body) + getDiff(project_id, doc_id, from, to, callback) { + if (callback == null) { callback = function(error, diff) {}; } + return request.get({ + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}` + }, (error, response, body) => { + response.statusCode.should.equal(200); + return callback(null, JSON.parse(body)); + }); + }, - getUpdates: (project_id, options, callback = (error, body) ->) -> - request.get { - url: "http://localhost:3015/project/#{project_id}/updates?before=#{options.before}&min_count=#{options.min_count}" - }, (error, response, body) => - response.statusCode.should.equal 200 - callback null, JSON.parse(body) + getUpdates(project_id, options, callback) { + if (callback == null) { callback = function(error, body) {}; } + return request.get({ + url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}` + }, (error, response, body) => { + response.statusCode.should.equal(200); + return callback(null, JSON.parse(body)); + }); + }, - restoreDoc: (project_id, doc_id, version, user_id, callback = (error) ->) -> - request.post { - url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/version/#{version}/restore" - headers: + restoreDoc(project_id, doc_id, version, user_id, callback) { + if (callback == null) { callback = function(error) {}; } + return request.post({ + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/version/${version}/restore`, + headers: { "X-User-Id": user_id - }, (error, response, body) => - response.statusCode.should.equal 204 - callback null + } + }, (error, response, body) => { + response.statusCode.should.equal(204); + return callback(null); + }); + }, - pushDocHistory: (project_id, doc_id, callback = (error) ->) -> - request.post { - url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/push" - }, (error, response, body) => - response.statusCode.should.equal 204 - callback(error) + pushDocHistory(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return request.post({ + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push` + }, (error, response, body) => { + response.statusCode.should.equal(204); + return callback(error); + }); + }, - pullDocHistory: (project_id, doc_id, callback = (error) ->) -> - request.post { - url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/pull" - }, (error, response, body) => - response.statusCode.should.equal 204 - callback(error) + pullDocHistory(project_id, doc_id, callback) { + if (callback == null) { callback = function(error) {}; } + return request.post({ + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull` + }, (error, response, body) => { + response.statusCode.should.equal(204); + return callback(error); + }); + }, - waitForS3: (done, retries=42) -> - if !Settings.trackchanges.s3.endpoint - return done() + waitForS3(done, retries) { + if (retries == null) { retries = 42; } + if (!Settings.trackchanges.s3.endpoint) { + return done(); + } - request.get "#{Settings.trackchanges.s3.endpoint}/", (err, res) -> - if res && res.statusCode < 500 - return done() + return request.get(`${Settings.trackchanges.s3.endpoint}/`, function(err, res) { + if (res && (res.statusCode < 500)) { + return done(); + } - if retries == 0 - return done(err or new Error("s3 returned #{res.statusCode}")) + if (retries === 0) { + return done(err || new Error(`s3 returned ${res.statusCode}`)); + } - setTimeout () -> - TrackChangesClient.waitForS3(done, --retries) - , 1000 + return setTimeout(() => TrackChangesClient.waitForS3(done, --retries) + , 1000); + }); + }, - getS3Doc: (project_id, doc_id, pack_id, callback = (error, body) ->) -> - params = - Bucket: S3_BUCKET - Key: "#{project_id}/changes-#{doc_id}/pack-#{pack_id}" + getS3Doc(project_id, doc_id, pack_id, callback) { + if (callback == null) { callback = function(error, body) {}; } + const params = { + Bucket: S3_BUCKET, + Key: `${project_id}/changes-${doc_id}/pack-${pack_id}` + }; - s3.getObject params, (error, data) -> - return callback(error) if error? - body = data.Body - return callback(new Error("empty response from s3")) if not body? - zlib.gunzip body, (err, result) -> - return callback(err) if err? - callback(null, JSON.parse(result.toString())) + return s3.getObject(params, function(error, data) { + if (error != null) { return callback(error); } + const body = data.Body; + if ((body == null)) { return callback(new Error("empty response from s3")); } + return zlib.gunzip(body, function(err, result) { + if (err != null) { return callback(err); } + return callback(null, JSON.parse(result.toString())); + }); + }); + }, - removeS3Doc: (project_id, doc_id, callback = (error, res, body) ->) -> - params = - Bucket: S3_BUCKET - Prefix: "#{project_id}/changes-#{doc_id}" + removeS3Doc(project_id, doc_id, callback) { + if (callback == null) { callback = function(error, res, body) {}; } + let params = { + Bucket: S3_BUCKET, + Prefix: `${project_id}/changes-${doc_id}` + }; - s3.listObjects params, (error, data) -> - return callback(error) if error? + return s3.listObjects(params, function(error, data) { + if (error != null) { return callback(error); } - params = - Bucket: S3_BUCKET - Delete: - Objects: data.Contents.map((s3object) -> {Key: s3object.Key}) + params = { + Bucket: S3_BUCKET, + Delete: { + Objects: data.Contents.map(s3object => ({Key: s3object.Key})) + } + }; - s3.deleteObjects params, callback + return s3.deleteObjects(params, callback); + }); + } +}); From 8fdf89b87cfcd3a653bf0057d3d38e3174482d7b Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Mon, 17 Feb 2020 18:35:50 +0100 Subject: [PATCH 477/549] decaffeinate: Run post-processing cleanups on AppendingUpdatesTests.coffee and 11 other files --- .../coffee/AppendingUpdatesTests.js | 10 +++++-- .../coffee/ArchivingUpdatesTests.js | 26 ++++++++++++------- .../acceptance/coffee/FlushingUpdatesTests.js | 26 ++++++++++++------- .../acceptance/coffee/GettingADiffTests.js | 5 ++++ .../acceptance/coffee/GettingUpdatesTests.js | 6 +++++ .../acceptance/coffee/LockManagerTests.js | 10 +++++-- .../acceptance/coffee/RestoringVersions.js | 5 ++++ .../coffee/helpers/MockDocStoreApi.js | 12 ++++++--- .../coffee/helpers/MockDocUpdaterApi.js | 15 ++++++++--- .../acceptance/coffee/helpers/MockWebApi.js | 14 +++++++--- .../coffee/helpers/TrackChangesApp.js | 5 ++++ .../coffee/helpers/TrackChangesClient.js | 19 +++++++++----- 12 files changed, 113 insertions(+), 40 deletions(-) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js index b0a9db982a..e2cda7f6e9 100644 --- a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -19,7 +25,7 @@ const TrackChangesClient = require("./helpers/TrackChangesClient"); const MockWebApi = require("./helpers/MockWebApi"); describe("Appending doc ops to the history", function() { - before(done=> TrackChangesApp.ensureRunning(done)); + before(function(done) { return TrackChangesApp.ensureRunning(done); }); describe("when the history does not exist yet", function() { before(function(done) { @@ -69,7 +75,7 @@ describe("Appending doc ops to the history", function() { }); return it("should clear the doc from the DocsWithHistoryOps set", function(done) { - rclient.sismember(`DocsWithHistoryOps:${this.project_id}`, this.doc_id, function(error, member) { + rclient.sismember(`DocsWithHistoryOps:${this.project_id}`, this.doc_id, (error, member) => { member.should.equal(0); return done(); }); diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js index cb832274f5..ceee1767ce 100644 --- a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js @@ -1,3 +1,11 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-undef, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -64,7 +72,7 @@ describe("Archiving updates", function() { sinon.spy(MockDocStoreApi, "getAllDoc"); this.updates = []; - for (let i = 0, end = 512+10, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { + for (let i = 0, end = 512+10, asc = end >= 0; asc ? i <= end : i >= end; asc ? i++ : i--) { this.updates.push({ op: [{ i: "a", p: 0 }], meta: { ts: this.now + ((i-2048) * this.hours), user_id: this.user_id }, @@ -79,7 +87,7 @@ describe("Archiving updates", function() { TrackChangesApp.ensureRunning(() => { return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { if (error != null) { throw error; } - return TrackChangesClient.flushDoc(this.project_id, this.doc_id, function(error) { + return TrackChangesClient.flushDoc(this.project_id, this.doc_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -99,7 +107,7 @@ describe("Archiving updates", function() { describe("archiving a doc's updates", function() { before(function(done) { - TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, function(error) { + TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -107,7 +115,7 @@ describe("Archiving updates", function() { }); it("should have one cached pack", function(done) { - return db.docHistory.count({ doc_id: ObjectId(this.doc_id), expiresAt:{$exists:true}}, function(error, count) { + return db.docHistory.count({ doc_id: ObjectId(this.doc_id), expiresAt:{$exists:true}}, (error, count) => { if (error != null) { throw error; } count.should.equal(1); return done(); @@ -120,7 +128,7 @@ describe("Archiving updates", function() { expiresAt:{$exists:true} }, (err, result) => { if (typeof error !== 'undefined' && error !== null) { throw error; } - return db.docHistory.count({ doc_id: ObjectId(this.doc_id)}, function(error, count) { + return db.docHistory.count({ doc_id: ObjectId(this.doc_id)}, (error, count) => { if (error != null) { throw error; } count.should.equal(1); return done(); @@ -129,7 +137,7 @@ describe("Archiving updates", function() { }); it("should have a docHistoryIndex entry marked as inS3", function(done) { - return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, function(error, index) { + return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => { if (error != null) { throw error; } index.packs[0].inS3.should.equal(true); return done(); @@ -137,7 +145,7 @@ describe("Archiving updates", function() { }); it("should have a docHistoryIndex entry with the last version", function(done) { - return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, function(error, index) { + return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => { if (error != null) { throw error; } index.packs[0].v_end.should.equal(1024); return done(); @@ -159,7 +167,7 @@ describe("Archiving updates", function() { return describe("unarchiving a doc's updates", function() { before(function(done) { - TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, function(error) { + TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -167,7 +175,7 @@ describe("Archiving updates", function() { }); return it("should restore both packs", function(done) { - return db.docHistory.count({ doc_id: ObjectId(this.doc_id) }, function(error, count) { + return db.docHistory.count({ doc_id: ObjectId(this.doc_id) }, (error, count) => { if (error != null) { throw error; } count.should.equal(2); return done(); diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js index 61d46ec98c..bc7a6b886a 100644 --- a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -19,7 +25,7 @@ const TrackChangesClient = require("./helpers/TrackChangesClient"); const MockWebApi = require("./helpers/MockWebApi"); describe("Flushing updates", function() { - before(done=> TrackChangesApp.ensureRunning(done)); + before(function(done) { return TrackChangesApp.ensureRunning(done); }); describe("flushing a doc's updates", function() { before(function(done) { @@ -34,7 +40,7 @@ describe("Flushing updates", function() { v: 3 }], error => { if (error != null) { throw error; } - return TrackChangesClient.flushDoc(this.project_id, this.doc_id, function(error) { + return TrackChangesClient.flushDoc(this.project_id, this.doc_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -43,7 +49,7 @@ describe("Flushing updates", function() { }); return it("should flush the op into mongo", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { expect(updates[0].pack[0].op).to.deep.equal([{ p: 3, i: "f" }]); @@ -78,7 +84,7 @@ describe("Flushing updates", function() { v: 3 }], error => { if (error != null) { throw error; } - return TrackChangesClient.flushProject(this.project_id, function(error) { + return TrackChangesClient.flushProject(this.project_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -87,7 +93,7 @@ describe("Flushing updates", function() { }); it("should not mark the updates for deletion", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { expect(updates[0].expiresAt).to.not.exist; return done(); }); @@ -95,7 +101,7 @@ describe("Flushing updates", function() { }); return it("should preserve history forever", function(done) { - TrackChangesClient.getProjectMetaData(this.project_id, function(error, project) { + TrackChangesClient.getProjectMetaData(this.project_id, (error, project) => { expect(project.preserveHistory).to.equal(true); return done(); }); @@ -127,7 +133,7 @@ describe("Flushing updates", function() { v: 3 }], error => { if (error != null) { throw error; } - return TrackChangesClient.flushProject(this.project_id, function(error) { + return TrackChangesClient.flushProject(this.project_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -136,7 +142,7 @@ describe("Flushing updates", function() { }); return it("should mark the updates for deletion", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { expect(updates[0].expiresAt).to.exist; return done(); }); @@ -170,7 +176,7 @@ describe("Flushing updates", function() { v: 3 }], error => { if (error != null) { throw error; } - return TrackChangesClient.flushProject(this.project_id, function(error) { + return TrackChangesClient.flushProject(this.project_id, (error) => { if (error != null) { throw error; } return done(); }); @@ -180,7 +186,7 @@ describe("Flushing updates", function() { }); return it("should not mark the updates for deletion", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) { + TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { expect(updates[0].expiresAt).to.not.exist; return done(); }); diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.js b/services/track-changes/test/acceptance/coffee/GettingADiffTests.js index ab7c26372c..7dd63698ea 100644 --- a/services/track-changes/test/acceptance/coffee/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/coffee/GettingADiffTests.js @@ -1,3 +1,8 @@ +/* eslint-disable + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js index a4519d11f3..299ba96afd 100644 --- a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + chai-friendly/no-unused-expressions, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.js b/services/track-changes/test/acceptance/coffee/LockManagerTests.js index 84a5c51209..19e0a13914 100644 --- a/services/track-changes/test/acceptance/coffee/LockManagerTests.js +++ b/services/track-changes/test/acceptance/coffee/LockManagerTests.js @@ -1,3 +1,9 @@ +/* eslint-disable + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -31,7 +37,7 @@ describe("Locking document", function() { LockManager.runWithLock("doc123", releaseB => { return releaseA(); } // try to release lock A to see if it wipes out lock B - , function(error) {}) + , (error) => {}) // we never release lock B so nothing should happen here , 1500); @@ -44,7 +50,7 @@ describe("Locking document", function() { }); return it("the new lock should not be removed by the expired locker", function(done) { - LockManager.checkLock("doc123", function(err, isFree) { + LockManager.checkLock("doc123", (err, isFree) => { expect(isFree).to.equal(false); return done(); }); diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.js b/services/track-changes/test/acceptance/coffee/RestoringVersions.js index b9da0d7045..3c03848a8b 100644 --- a/services/track-changes/test/acceptance/coffee/RestoringVersions.js +++ b/services/track-changes/test/acceptance/coffee/RestoringVersions.js @@ -1,3 +1,8 @@ +/* eslint-disable + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js index 8aec89535a..c55260a007 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -18,7 +24,7 @@ module.exports = (MockDocUpdaterApi = { run() { app.get("/project/:project_id/doc", (req, res, next) => { - return this.getAllDoc(req.params.project_id, function(error, docs) { + return this.getAllDoc(req.params.project_id, (error, docs) => { if (error != null) { res.send(500); } @@ -30,9 +36,9 @@ module.exports = (MockDocUpdaterApi = { }); }); - return app.listen(3016, function(error) { + return app.listen(3016, (error) => { if (error != null) { throw error; } - }).on("error", function(error) { + }).on("error", (error) => { console.error("error starting MockDocStoreApi:", error.message); return process.exit(1); }); diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js index 27b62576a1..8ba6f2c6b2 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-undef, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -25,7 +32,7 @@ module.exports = (MockDocUpdaterApi = { run() { app.get("/project/:project_id/doc/:doc_id", (req, res, next) => { - return this.getDoc(req.params.project_id, req.params.doc_id, function(error, doc) { + return this.getDoc(req.params.project_id, req.params.doc_id, (error, doc) => { if (error != null) { res.send(500); } @@ -38,7 +45,7 @@ module.exports = (MockDocUpdaterApi = { }); app.post("/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => { - return this.setDoc(req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, req.body.undoing, function(errr, doc) { + return this.setDoc(req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, req.body.undoing, (errr, doc) => { if (typeof error !== 'undefined' && error !== null) { return res.send(500); } else { @@ -47,9 +54,9 @@ module.exports = (MockDocUpdaterApi = { }); }); - return app.listen(3003, function(error) { + return app.listen(3003, (error) => { if (error != null) { throw error; } - }).on("error", function(error) { + }).on("error", (error) => { console.error("error starting MockDocUpdaterApi:", error.message); return process.exit(1); }); diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js index 41bb890a86..cf1350f97d 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js +++ b/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js @@ -1,3 +1,9 @@ +/* eslint-disable + camelcase, + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns @@ -25,7 +31,7 @@ module.exports = (MockWebApi = { run() { app.get("/user/:user_id/personal_info", (req, res, next) => { - return this.getUserInfo(req.params.user_id, function(error, user) { + return this.getUserInfo(req.params.user_id, (error, user) => { if (error != null) { res.send(500); } @@ -38,7 +44,7 @@ module.exports = (MockWebApi = { }); app.get("/project/:project_id/details", (req, res, next) => { - return this.getProjectDetails(req.params.project_id, function(error, project) { + return this.getProjectDetails(req.params.project_id, (error, project) => { if (error != null) { res.send(500); } @@ -50,9 +56,9 @@ module.exports = (MockWebApi = { }); }); - return app.listen(3000, function(error) { + return app.listen(3000, (error) => { if (error != null) { throw error; } - }).on("error", function(error) { + }).on("error", (error) => { console.error("error starting MockWebApiServer:", error.message); return process.exit(1); }); diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js index 31bf505b33..464961c689 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js @@ -1,3 +1,8 @@ +/* eslint-disable + handle-callback-err, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js index 3cf1a26a33..df66425a49 100644 --- a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js @@ -1,3 +1,10 @@ +/* eslint-disable + camelcase, + handle-callback-err, + no-unused-vars, +*/ +// TODO: This file was created by bulk-decaffeinate. +// Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from @@ -26,7 +33,7 @@ const S3_BUCKET = Settings.trackchanges.stores.doc_history; module.exports = (TrackChangesClient = { flushAndGetCompressedUpdates(project_id, doc_id, callback) { if (callback == null) { callback = function(error, updates) {}; } - return TrackChangesClient.flushDoc(project_id, doc_id, function(error) { + return TrackChangesClient.flushDoc(project_id, doc_id, (error) => { if (error != null) { return callback(error); } return TrackChangesClient.getCompressedUpdates(doc_id, callback); }); @@ -82,7 +89,7 @@ module.exports = (TrackChangesClient = { pushRawUpdates(project_id, doc_id, updates, callback) { if (callback == null) { callback = function(error) {}; } - return rclient.sadd(Keys.docsWithHistoryOps({project_id}), doc_id, function(error) { + return rclient.sadd(Keys.docsWithHistoryOps({project_id}), doc_id, (error) => { if (error != null) { return callback(error); } return rclient.rpush(Keys.uncompressedHistoryOps({doc_id}), ...Array.from(((Array.from(updates).map((u) => JSON.stringify(u))))), callback); }); @@ -147,7 +154,7 @@ module.exports = (TrackChangesClient = { return done(); } - return request.get(`${Settings.trackchanges.s3.endpoint}/`, function(err, res) { + return request.get(`${Settings.trackchanges.s3.endpoint}/`, (err, res) => { if (res && (res.statusCode < 500)) { return done(); } @@ -168,11 +175,11 @@ module.exports = (TrackChangesClient = { Key: `${project_id}/changes-${doc_id}/pack-${pack_id}` }; - return s3.getObject(params, function(error, data) { + return s3.getObject(params, (error, data) => { if (error != null) { return callback(error); } const body = data.Body; if ((body == null)) { return callback(new Error("empty response from s3")); } - return zlib.gunzip(body, function(err, result) { + return zlib.gunzip(body, (err, result) => { if (err != null) { return callback(err); } return callback(null, JSON.parse(result.toString())); }); @@ -186,7 +193,7 @@ module.exports = (TrackChangesClient = { Prefix: `${project_id}/changes-${doc_id}` }; - return s3.listObjects(params, function(error, data) { + return s3.listObjects(params, (error, data) => { if (error != null) { return callback(error); } params = { From 011262986dc479e87fa66db0bc2439c7768d6177 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:35:55 +0100 Subject: [PATCH 478/549] decaffeinate: rename test/acceptance/coffee to test/acceptance/js --- .../test/acceptance/{coffee => js}/AppendingUpdatesTests.js | 0 .../test/acceptance/{coffee => js}/ArchivingUpdatesTests.js | 0 .../test/acceptance/{coffee => js}/FlushingUpdatesTests.js | 0 .../test/acceptance/{coffee => js}/GettingADiffTests.js | 0 .../test/acceptance/{coffee => js}/GettingUpdatesTests.js | 0 .../test/acceptance/{coffee => js}/LockManagerTests.js | 0 .../test/acceptance/{coffee => js}/RestoringVersions.js | 0 .../test/acceptance/{coffee => js}/helpers/MockDocStoreApi.js | 0 .../test/acceptance/{coffee => js}/helpers/MockDocUpdaterApi.js | 0 .../test/acceptance/{coffee => js}/helpers/MockWebApi.js | 0 .../test/acceptance/{coffee => js}/helpers/TrackChangesApp.js | 0 .../test/acceptance/{coffee => js}/helpers/TrackChangesClient.js | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/test/acceptance/{coffee => js}/AppendingUpdatesTests.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/ArchivingUpdatesTests.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/FlushingUpdatesTests.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/GettingADiffTests.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/GettingUpdatesTests.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/LockManagerTests.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/RestoringVersions.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/helpers/MockDocStoreApi.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/helpers/MockDocUpdaterApi.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/helpers/MockWebApi.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/helpers/TrackChangesApp.js (100%) rename services/track-changes/test/acceptance/{coffee => js}/helpers/TrackChangesClient.js (100%) diff --git a/services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/AppendingUpdatesTests.js rename to services/track-changes/test/acceptance/js/AppendingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/ArchivingUpdatesTests.js rename to services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/FlushingUpdatesTests.js rename to services/track-changes/test/acceptance/js/FlushingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/GettingADiffTests.js rename to services/track-changes/test/acceptance/js/GettingADiffTests.js diff --git a/services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/GettingUpdatesTests.js rename to services/track-changes/test/acceptance/js/GettingUpdatesTests.js diff --git a/services/track-changes/test/acceptance/coffee/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/LockManagerTests.js rename to services/track-changes/test/acceptance/js/LockManagerTests.js diff --git a/services/track-changes/test/acceptance/coffee/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/RestoringVersions.js rename to services/track-changes/test/acceptance/js/RestoringVersions.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/MockDocStoreApi.js rename to services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/MockDocUpdaterApi.js rename to services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/MockWebApi.js rename to services/track-changes/test/acceptance/js/helpers/MockWebApi.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/TrackChangesApp.js rename to services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js diff --git a/services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js similarity index 100% rename from services/track-changes/test/acceptance/coffee/helpers/TrackChangesClient.js rename to services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js From 3183aca524fc49db9943c892e5b9e6ca4938b510 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:35:59 +0100 Subject: [PATCH 479/549] prettier: convert test/acceptance decaffeinated files to Prettier format --- .../acceptance/js/AppendingUpdatesTests.js | 891 +++++++++++------- .../acceptance/js/ArchivingUpdatesTests.js | 390 +++++--- .../acceptance/js/FlushingUpdatesTests.js | 407 ++++---- .../test/acceptance/js/GettingADiffTests.js | 194 ++-- .../test/acceptance/js/GettingUpdatesTests.js | 303 +++--- .../test/acceptance/js/LockManagerTests.js | 98 +- .../test/acceptance/js/RestoringVersions.js | 174 ++-- .../acceptance/js/helpers/MockDocStoreApi.js | 69 +- .../js/helpers/MockDocUpdaterApi.js | 119 ++- .../test/acceptance/js/helpers/MockWebApi.js | 105 ++- .../acceptance/js/helpers/TrackChangesApp.js | 81 +- .../js/helpers/TrackChangesClient.js | 421 +++++---- 12 files changed, 1913 insertions(+), 1339 deletions(-) diff --git a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js index e2cda7f6e9..c20703d71f 100644 --- a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js @@ -10,384 +10,581 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); -const request = require("request"); -const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') +const request = require('request') +const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now -const TrackChangesApp = require("./helpers/TrackChangesApp"); -const TrackChangesClient = require("./helpers/TrackChangesClient"); -const MockWebApi = require("./helpers/MockWebApi"); +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') +const MockWebApi = require('./helpers/MockWebApi') -describe("Appending doc ops to the history", function() { - before(function(done) { return TrackChangesApp.ensureRunning(done); }); +describe('Appending doc ops to the history', function() { + before(function(done) { + return TrackChangesApp.ensureRunning(done) + }) - describe("when the history does not exist yet", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }, { - op: [{ i: "o", p: 4 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 4 - }, { - op: [{ i: "o", p: 5 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 5 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + describe('when the history does not exist yet', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + }, + { + op: [{ i: 'o', p: 4 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 4 + }, + { + op: [{ i: 'o', p: 5 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 5 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - it("should insert the compressed op into mongo", function() { - return expect(this.updates[0].pack[0].op).to.deep.equal([{ - p: 3, i: "foo" - }]); - }); + it('should insert the compressed op into mongo', function() { + return expect(this.updates[0].pack[0].op).to.deep.equal([ + { + p: 3, + i: 'foo' + } + ]) + }) - it("should insert the correct version number into mongo", function() { - return expect(this.updates[0].v).to.equal(5); - }); + it('should insert the correct version number into mongo', function() { + return expect(this.updates[0].v).to.equal(5) + }) - it("should store the doc id", function() { - return expect(this.updates[0].doc_id.toString()).to.equal(this.doc_id); - }); + it('should store the doc id', function() { + return expect(this.updates[0].doc_id.toString()).to.equal(this.doc_id) + }) - it("should store the project id", function() { - return expect(this.updates[0].project_id.toString()).to.equal(this.project_id); - }); + it('should store the project id', function() { + return expect(this.updates[0].project_id.toString()).to.equal( + this.project_id + ) + }) - return it("should clear the doc from the DocsWithHistoryOps set", function(done) { - rclient.sismember(`DocsWithHistoryOps:${this.project_id}`, this.doc_id, (error, member) => { - member.should.equal(0); - return done(); - }); - return null; - }); - }); + return it('should clear the doc from the DocsWithHistoryOps set', function(done) { + rclient.sismember( + `DocsWithHistoryOps:${this.project_id}`, + this.doc_id, + (error, member) => { + member.should.equal(0) + return done() + } + ) + return null + }) + }) - describe("when the history has already been started", function() { - beforeEach(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }, { - op: [{ i: "o", p: 4 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 4 - }, { - op: [{ i: "o", p: 5 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 5 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + describe('when the history has already been started', function() { + beforeEach(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + }, + { + op: [{ i: 'o', p: 4 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 4 + }, + { + op: [{ i: 'o', p: 5 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 5 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - describe("when the updates are recent and from the same user", function() { - beforeEach(function(done) { - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "b", p: 6 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 6 - }, { - op: [{ i: "a", p: 7 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 7 - }, { - op: [{ i: "r", p: 8 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 8 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + describe('when the updates are recent and from the same user', function() { + beforeEach(function(done) { + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'b', p: 6 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 6 + }, + { + op: [{ i: 'a', p: 7 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 7 + }, + { + op: [{ i: 'r', p: 8 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 8 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - it("should combine all the updates into one pack", function() { - return expect(this.updates[0].pack[1].op).to.deep.equal([{ - p: 6, i: "bar" - }]); - }); + it('should combine all the updates into one pack', function() { + return expect(this.updates[0].pack[1].op).to.deep.equal([ + { + p: 6, + i: 'bar' + } + ]) + }) - return it("should insert the correct version number into mongo", function() { - return expect(this.updates[0].v_end).to.equal(8); - }); - }); + return it('should insert the correct version number into mongo', function() { + return expect(this.updates[0].v_end).to.equal(8) + }) + }) + return describe('when the updates are far apart', function() { + beforeEach(function(done) { + const oneDay = 24 * 60 * 60 * 1000 + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'b', p: 6 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, + v: 6 + }, + { + op: [{ i: 'a', p: 7 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, + v: 7 + }, + { + op: [{ i: 'r', p: 8 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, + v: 8 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - return describe("when the updates are far apart", function() { - beforeEach(function(done) { - const oneDay = 24 * 60 * 60 * 1000; - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "b", p: 6 }], - meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 6 - }, { - op: [{ i: "a", p: 7 }], - meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 7 - }, { - op: [{ i: "r", p: 8 }], - meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 8 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + return it('should combine the updates into one pack', function() { + expect(this.updates[0].pack[0].op).to.deep.equal([ + { + p: 3, + i: 'foo' + } + ]) + return expect(this.updates[0].pack[1].op).to.deep.equal([ + { + p: 6, + i: 'bar' + } + ]) + }) + }) + }) - return it("should combine the updates into one pack", function() { - expect(this.updates[0].pack[0].op).to.deep.equal([{ - p: 3, i: "foo" - }]); - return expect(this.updates[0].pack[1].op).to.deep.equal([{ - p: 6, i: "bar" - }]); - }); - }); -}); + describe('when the updates need processing in batches', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } + const updates = [] + this.expectedOp = [{ p: 0, i: '' }] + for (let i = 0; i <= 250; i++) { + updates.push({ + op: [{ i: 'a', p: 0 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: i + }) + this.expectedOp[0].i = `a${this.expectedOp[0].i}` + } - describe("when the updates need processing in batches", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - const updates = []; - this.expectedOp = [{ p:0, i: "" }]; - for (let i = 0; i <= 250; i++) { - updates.push({ - op: [{i: "a", p: 0}], - meta: { ts: Date.now(), user_id: this.user_id }, - v: i - }); - this.expectedOp[0].i = `a${this.expectedOp[0].i}`; - } + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + updates, + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates1) => { + this.updates = updates1 + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, updates, error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates1) => { - this.updates = updates1; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + it('should concat the compressed op into mongo', function() { + return expect(this.updates[0].pack.length).to.deep.equal(3) + }) // batch size is 100 - it("should concat the compressed op into mongo", function() { - return expect(this.updates[0].pack.length).to.deep.equal(3); - }); // batch size is 100 + return it('should insert the correct version number into mongo', function() { + return expect(this.updates[0].v_end).to.equal(250) + }) + }) - return it("should insert the correct version number into mongo", function() { - return expect(this.updates[0].v_end).to.equal(250); - }); - }); + describe('when there are multiple ops in each update', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } + const oneDay = 24 * 60 * 60 * 1000 + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [ + { i: 'f', p: 3 }, + { i: 'o', p: 4 }, + { i: 'o', p: 5 } + ], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + }, + { + op: [ + { i: 'b', p: 6 }, + { i: 'a', p: 7 }, + { i: 'r', p: 8 } + ], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, + v: 4 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) + it('should insert the compressed ops into mongo', function() { + expect(this.updates[0].pack[0].op).to.deep.equal([ + { + p: 3, + i: 'foo' + } + ]) + return expect(this.updates[0].pack[1].op).to.deep.equal([ + { + p: 6, + i: 'bar' + } + ]) + }) - describe("when there are multiple ops in each update", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - const oneDay = 24 * 60 * 60 * 1000; - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }, { - op: [{ i: "b", p: 6 }, { i: "a", p: 7 }, { i: "r", p: 8 }], - meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 4 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + return it('should insert the correct version numbers into mongo', function() { + expect(this.updates[0].pack[0].v).to.equal(3) + return expect(this.updates[0].pack[1].v).to.equal(4) + }) + }) - it("should insert the compressed ops into mongo", function() { - expect(this.updates[0].pack[0].op).to.deep.equal([{ - p: 3, i: "foo" - }]); - return expect(this.updates[0].pack[1].op).to.deep.equal([{ - p: 6, i: "bar" - }]); - }); + describe('when there is a no-op update', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } + const oneDay = 24 * 60 * 60 * 1000 + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + }, + { + op: [{ i: 'foo', p: 3 }], + meta: { ts: Date.now() + oneDay, user_id: this.user_id }, + v: 4 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - return it("should insert the correct version numbers into mongo", function() { - expect(this.updates[0].pack[0].v).to.equal(3); - return expect(this.updates[0].pack[1].v).to.equal(4); - }); - }); + it('should insert the compressed no-op into mongo', function() { + return expect(this.updates[0].pack[0].op).to.deep.equal([]) + }) - describe("when there is a no-op update", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - const oneDay = 24 * 60 * 60 * 1000; - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }, { - op: [{ i: "foo", p: 3 }], - meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 4 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + it('should insert the compressed next update into mongo', function() { + return expect(this.updates[0].pack[1].op).to.deep.equal([ + { + p: 3, + i: 'foo' + } + ]) + }) - it("should insert the compressed no-op into mongo", function() { - return expect(this.updates[0].pack[0].op).to.deep.equal([]); - }); + return it('should insert the correct version numbers into mongo', function() { + expect(this.updates[0].pack[0].v).to.equal(3) + return expect(this.updates[0].pack[1].v).to.equal(4) + }) + }) + describe('when there is a comment update', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [ + { c: 'foo', p: 3 }, + { d: 'bar', p: 6 } + ], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - it("should insert the compressed next update into mongo", function() { - return expect(this.updates[0].pack[1].op).to.deep.equal([{ - p: 3, i: "foo" - }]); - }); + it('should ignore the comment op', function() { + return expect(this.updates[0].pack[0].op).to.deep.equal([ + { d: 'bar', p: 6 } + ]) + }) - return it("should insert the correct version numbers into mongo", function() { - expect(this.updates[0].pack[0].v).to.equal(3); - return expect(this.updates[0].pack[1].v).to.equal(4); - }); - }); + return it('should insert the correct version numbers into mongo', function() { + return expect(this.updates[0].pack[0].v).to.equal(3) + }) + }) - describe("when there is a comment update", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ c: "foo", p: 3 }, {d: "bar", p: 6}], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + describe('when the project has versioning enabled', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: true } } - it("should ignore the comment op", function() { - return expect(this.updates[0].pack[0].op).to.deep.equal([{d: "bar", p: 6}]); - }); + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - return it("should insert the correct version numbers into mongo", function() { - return expect(this.updates[0].pack[0].v).to.equal(3); - }); - }); + return it('should not add a expiresAt entry in the update in mongo', function() { + return expect(this.updates[0].expiresAt).to.be.undefined + }) + }) - describe("when the project has versioning enabled", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: true}}; + return describe('when the project does not have versioning enabled', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: false } } - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushAndGetCompressedUpdates( + this.project_id, + this.doc_id, + (error, updates) => { + this.updates = updates + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - return it("should not add a expiresAt entry in the update in mongo", function() { - return expect(this.updates[0].expiresAt).to.be.undefined; - }); - }); - - return describe("when the project does not have versioning enabled", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: false}}; - - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => { - this.updates = updates; - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); - - return it("should add a expiresAt entry in the update in mongo", function() { - return expect(this.updates[0].expiresAt).to.exist; - }); - }); -}); + return it('should add a expiresAt entry in the update in mongo', function() { + return expect(this.updates[0].expiresAt).to.exist + }) + }) +}) diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index ceee1767ce..d66ed60940 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -14,176 +14,260 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { db } = mongojs; -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); -const request = require("request"); -const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { db } = mongojs +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') +const request = require('request') +const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now -const TrackChangesApp = require("./helpers/TrackChangesApp"); -const TrackChangesClient = require("./helpers/TrackChangesClient"); -const MockDocStoreApi = require("./helpers/MockDocStoreApi"); -const MockWebApi = require("./helpers/MockWebApi"); +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') +const MockDocStoreApi = require('./helpers/MockDocStoreApi') +const MockWebApi = require('./helpers/MockWebApi') -describe("Archiving updates", function() { - before(function(done) { - if (__guard__(__guard__(Settings != null ? Settings.trackchanges : undefined, x1 => x1.s3), x => x.key.length) < 1) { - const message = new Error("s3 keys not setup, this test setup will fail"); - return done(message); - } +describe('Archiving updates', function() { + before(function(done) { + if ( + __guard__( + __guard__( + Settings != null ? Settings.trackchanges : undefined, + x1 => x1.s3 + ), + x => x.key.length + ) < 1 + ) { + const message = new Error('s3 keys not setup, this test setup will fail') + return done(message) + } - return TrackChangesClient.waitForS3(done); - }); + return TrackChangesClient.waitForS3(done) + }) - before(function(done) { - this.now = Date.now(); - this.to = this.now; - this.user_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.project_id = ObjectId().toString(); + before(function(done) { + this.now = Date.now() + this.to = this.now + this.user_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.project_id = ObjectId().toString() - this.minutes = 60 * 1000; - this.hours = 60 * this.minutes; + this.minutes = 60 * 1000 + this.hours = 60 * this.minutes - MockWebApi.projects[this.project_id] = { - features: { - versioning: true - } - }; - sinon.spy(MockWebApi, "getProjectDetails"); + MockWebApi.projects[this.project_id] = { + features: { + versioning: true + } + } + sinon.spy(MockWebApi, 'getProjectDetails') - MockWebApi.users[this.user_id] = (this.user = { - email: "user@sharelatex.com", - first_name: "Leo", - last_name: "Lion", - id: this.user_id - }); - sinon.spy(MockWebApi, "getUserInfo"); + MockWebApi.users[this.user_id] = this.user = { + email: 'user@sharelatex.com', + first_name: 'Leo', + last_name: 'Lion', + id: this.user_id + } + sinon.spy(MockWebApi, 'getUserInfo') - MockDocStoreApi.docs[this.doc_id] = (this.doc = { - _id: this.doc_id, - project_id: this.project_id - }); - sinon.spy(MockDocStoreApi, "getAllDoc"); + MockDocStoreApi.docs[this.doc_id] = this.doc = { + _id: this.doc_id, + project_id: this.project_id + } + sinon.spy(MockDocStoreApi, 'getAllDoc') - this.updates = []; - for (let i = 0, end = 512+10, asc = end >= 0; asc ? i <= end : i >= end; asc ? i++ : i--) { - this.updates.push({ - op: [{ i: "a", p: 0 }], - meta: { ts: this.now + ((i-2048) * this.hours), user_id: this.user_id }, - v: (2 * i) + 1 - }); - this.updates.push({ - op: [{ i: "b", p: 0 }], - meta: { ts: this.now + ((i-2048) * this.hours) + (10*this.minutes), user_id: this.user_id }, - v: (2 * i) + 2 - }); - } - TrackChangesApp.ensureRunning(() => { - return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { - if (error != null) { throw error; } - return TrackChangesClient.flushDoc(this.project_id, this.doc_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - }); - }); - return null; - }); + this.updates = [] + for ( + let i = 0, end = 512 + 10, asc = end >= 0; + asc ? i <= end : i >= end; + asc ? i++ : i-- + ) { + this.updates.push({ + op: [{ i: 'a', p: 0 }], + meta: { ts: this.now + (i - 2048) * this.hours, user_id: this.user_id }, + v: 2 * i + 1 + }) + this.updates.push({ + op: [{ i: 'b', p: 0 }], + meta: { + ts: this.now + (i - 2048) * this.hours + 10 * this.minutes, + user_id: this.user_id + }, + v: 2 * i + 2 + }) + } + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + this.updates, + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushDoc( + this.project_id, + this.doc_id, + error => { + if (error != null) { + throw error + } + return done() + } + ) + } + ) + }) + return null + }) - after(function(done) { - MockWebApi.getUserInfo.restore(); - return db.docHistory.remove({project_id: ObjectId(this.project_id)}, () => { - return db.docHistoryIndex.remove({project_id: ObjectId(this.project_id)}, () => { - return TrackChangesClient.removeS3Doc(this.project_id, this.doc_id, done); - }); - }); - }); + after(function(done) { + MockWebApi.getUserInfo.restore() + return db.docHistory.remove( + { project_id: ObjectId(this.project_id) }, + () => { + return db.docHistoryIndex.remove( + { project_id: ObjectId(this.project_id) }, + () => { + return TrackChangesClient.removeS3Doc( + this.project_id, + this.doc_id, + done + ) + } + ) + } + ) + }) - describe("archiving a doc's updates", function() { - before(function(done) { - TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - return null; - }); + describe("archiving a doc's updates", function() { + before(function(done) { + TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, error => { + if (error != null) { + throw error + } + return done() + }) + return null + }) - it("should have one cached pack", function(done) { - return db.docHistory.count({ doc_id: ObjectId(this.doc_id), expiresAt:{$exists:true}}, (error, count) => { - if (error != null) { throw error; } - count.should.equal(1); - return done(); - }); - }); + it('should have one cached pack', function(done) { + return db.docHistory.count( + { doc_id: ObjectId(this.doc_id), expiresAt: { $exists: true } }, + (error, count) => { + if (error != null) { + throw error + } + count.should.equal(1) + return done() + } + ) + }) - it("should have one remaining pack after cache is expired", function(done) { - return db.docHistory.remove({ - doc_id: ObjectId(this.doc_id), - expiresAt:{$exists:true} - }, (err, result) => { - if (typeof error !== 'undefined' && error !== null) { throw error; } - return db.docHistory.count({ doc_id: ObjectId(this.doc_id)}, (error, count) => { - if (error != null) { throw error; } - count.should.equal(1); - return done(); - }); - }); - }); + it('should have one remaining pack after cache is expired', function(done) { + return db.docHistory.remove( + { + doc_id: ObjectId(this.doc_id), + expiresAt: { $exists: true } + }, + (err, result) => { + if (typeof error !== 'undefined' && error !== null) { + throw error + } + return db.docHistory.count( + { doc_id: ObjectId(this.doc_id) }, + (error, count) => { + if (error != null) { + throw error + } + count.should.equal(1) + return done() + } + ) + } + ) + }) - it("should have a docHistoryIndex entry marked as inS3", function(done) { - return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => { - if (error != null) { throw error; } - index.packs[0].inS3.should.equal(true); - return done(); - }); - }); + it('should have a docHistoryIndex entry marked as inS3', function(done) { + return db.docHistoryIndex.findOne( + { _id: ObjectId(this.doc_id) }, + (error, index) => { + if (error != null) { + throw error + } + index.packs[0].inS3.should.equal(true) + return done() + } + ) + }) - it("should have a docHistoryIndex entry with the last version", function(done) { - return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => { - if (error != null) { throw error; } - index.packs[0].v_end.should.equal(1024); - return done(); - }); - }); + it('should have a docHistoryIndex entry with the last version', function(done) { + return db.docHistoryIndex.findOne( + { _id: ObjectId(this.doc_id) }, + (error, index) => { + if (error != null) { + throw error + } + index.packs[0].v_end.should.equal(1024) + return done() + } + ) + }) - return it("should store 1024 doc changes in S3 in one pack", function(done) { - return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => { - if (error != null) { throw error; } - const pack_id = index.packs[0]._id; - return TrackChangesClient.getS3Doc(this.project_id, this.doc_id, pack_id, (error, doc) => { - doc.n.should.equal(1024); - doc.pack.length.should.equal(1024); - return done(); - }); - }); - }); - }); + return it('should store 1024 doc changes in S3 in one pack', function(done) { + return db.docHistoryIndex.findOne( + { _id: ObjectId(this.doc_id) }, + (error, index) => { + if (error != null) { + throw error + } + const pack_id = index.packs[0]._id + return TrackChangesClient.getS3Doc( + this.project_id, + this.doc_id, + pack_id, + (error, doc) => { + doc.n.should.equal(1024) + doc.pack.length.should.equal(1024) + return done() + } + ) + } + ) + }) + }) - return describe("unarchiving a doc's updates", function() { - before(function(done) { - TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - return null; - }); + return describe("unarchiving a doc's updates", function() { + before(function(done) { + TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, error => { + if (error != null) { + throw error + } + return done() + }) + return null + }) - return it("should restore both packs", function(done) { - return db.docHistory.count({ doc_id: ObjectId(this.doc_id) }, (error, count) => { - if (error != null) { throw error; } - count.should.equal(2); - return done(); - }); - }); - }); -}); + return it('should restore both packs', function(done) { + return db.docHistory.count( + { doc_id: ObjectId(this.doc_id) }, + (error, count) => { + if (error != null) { + throw error + } + count.should.equal(2) + return done() + } + ) + }) + }) +}) function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js index bc7a6b886a..9eaf86e85e 100644 --- a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js @@ -10,188 +10,267 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); -const request = require("request"); -const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') +const request = require('request') +const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now -const TrackChangesApp = require("./helpers/TrackChangesApp"); -const TrackChangesClient = require("./helpers/TrackChangesClient"); -const MockWebApi = require("./helpers/MockWebApi"); +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') +const MockWebApi = require('./helpers/MockWebApi') -describe("Flushing updates", function() { - before(function(done) { return TrackChangesApp.ensureRunning(done); }); +describe('Flushing updates', function() { + before(function(done) { + return TrackChangesApp.ensureRunning(done) + }) - describe("flushing a doc's updates", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: true}}; + describe("flushing a doc's updates", function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: true } } - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushDoc(this.project_id, this.doc_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushDoc( + this.project_id, + this.doc_id, + error => { + if (error != null) { + throw error + } + return done() + } + ) + } + ) + return null + }) - return it("should flush the op into mongo", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { - expect(updates[0].pack[0].op).to.deep.equal([{ - p: 3, i: "f" - }]); - return done(); - }); - return null; - }); - }); + return it('should flush the op into mongo', function(done) { + TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { + expect(updates[0].pack[0].op).to.deep.equal([ + { + p: 3, + i: 'f' + } + ]) + return done() + }) + return null + }) + }) - return describe("flushing a project's updates", function() { - describe("with versioning enabled", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); + return describe("flushing a project's updates", function() { + describe('with versioning enabled', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() - this.weeks = 7 * 24 * 60 * 60 * 1000; + this.weeks = 7 * 24 * 60 * 60 * 1000 - MockWebApi.projects[this.project_id] = { - features: { - versioning: true - } - }; + MockWebApi.projects[this.project_id] = { + features: { + versioning: true + } + } - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "g", p: 2 }], - meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id }, - v: 2 - }, { - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushProject(this.project_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'g', p: 2 }], + meta: { ts: Date.now() - 2 * this.weeks, user_id: this.user_id }, + v: 2 + }, + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushProject(this.project_id, error => { + if (error != null) { + throw error + } + return done() + }) + } + ) + return null + }) - it("should not mark the updates for deletion", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { - expect(updates[0].expiresAt).to.not.exist; - return done(); - }); - return null; - }); + it('should not mark the updates for deletion', function(done) { + TrackChangesClient.getCompressedUpdates( + this.doc_id, + (error, updates) => { + expect(updates[0].expiresAt).to.not.exist + return done() + } + ) + return null + }) - return it("should preserve history forever", function(done) { - TrackChangesClient.getProjectMetaData(this.project_id, (error, project) => { - expect(project.preserveHistory).to.equal(true); - return done(); - }); - return null; - }); - }); + return it('should preserve history forever', function(done) { + TrackChangesClient.getProjectMetaData( + this.project_id, + (error, project) => { + expect(project.preserveHistory).to.equal(true) + return done() + } + ) + return null + }) + }) - describe("without versioning enabled", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); + describe('without versioning enabled', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() - this.weeks = 7 * 24 * 60 * 60 * 1000; + this.weeks = 7 * 24 * 60 * 60 * 1000 - MockWebApi.projects[this.project_id] = { - features: { - versioning: false - } - }; + MockWebApi.projects[this.project_id] = { + features: { + versioning: false + } + } - TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "g", p: 2 }], - meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id }, - v: 2 - }, { - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushProject(this.project_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'g', p: 2 }], + meta: { ts: Date.now() - 2 * this.weeks, user_id: this.user_id }, + v: 2 + }, + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushProject(this.project_id, error => { + if (error != null) { + throw error + } + return done() + }) + } + ) + return null + }) - return it("should mark the updates for deletion", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { - expect(updates[0].expiresAt).to.exist; - return done(); - }); - return null; - }); - }); + return it('should mark the updates for deletion', function(done) { + TrackChangesClient.getCompressedUpdates( + this.doc_id, + (error, updates) => { + expect(updates[0].expiresAt).to.exist + return done() + } + ) + return null + }) + }) - return describe("without versioning enabled but with preserveHistory set to true", function() { - before(function(done) { - this.project_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.user_id = ObjectId().toString(); + return describe('without versioning enabled but with preserveHistory set to true', function() { + before(function(done) { + this.project_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.user_id = ObjectId().toString() - this.weeks = 7 * 24 * 60 * 60 * 1000; + this.weeks = 7 * 24 * 60 * 60 * 1000 - MockWebApi.projects[this.project_id] = { - features: { - versioning: false - } - }; + MockWebApi.projects[this.project_id] = { + features: { + versioning: false + } + } - TrackChangesClient.setPreserveHistoryForProject(this.project_id, error => { - if (error != null) { throw error; } - return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{ - op: [{ i: "g", p: 2 }], - meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id }, - v: 2 - }, { - op: [{ i: "f", p: 3 }], - meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - }], error => { - if (error != null) { throw error; } - return TrackChangesClient.flushProject(this.project_id, (error) => { - if (error != null) { throw error; } - return done(); - }); - }); - }); - return null; - }); + TrackChangesClient.setPreserveHistoryForProject( + this.project_id, + error => { + if (error != null) { + throw error + } + return TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + [ + { + op: [{ i: 'g', p: 2 }], + meta: { + ts: Date.now() - 2 * this.weeks, + user_id: this.user_id + }, + v: 2 + }, + { + op: [{ i: 'f', p: 3 }], + meta: { ts: Date.now(), user_id: this.user_id }, + v: 3 + } + ], + error => { + if (error != null) { + throw error + } + return TrackChangesClient.flushProject( + this.project_id, + error => { + if (error != null) { + throw error + } + return done() + } + ) + } + ) + } + ) + return null + }) - return it("should not mark the updates for deletion", function(done) { - TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { - expect(updates[0].expiresAt).to.not.exist; - return done(); - }); - return null; - }); - }); - }); -}); + return it('should not mark the updates for deletion', function(done) { + TrackChangesClient.getCompressedUpdates( + this.doc_id, + (error, updates) => { + expect(updates[0].expiresAt).to.not.exist + return done() + } + ) + return null + }) + }) + }) +}) diff --git a/services/track-changes/test/acceptance/js/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js index 7dd63698ea..871258b30d 100644 --- a/services/track-changes/test/acceptance/js/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/js/GettingADiffTests.js @@ -9,97 +9,123 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { db } = mongojs; -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { db } = mongojs +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') -const TrackChangesApp = require("./helpers/TrackChangesApp"); -const TrackChangesClient = require("./helpers/TrackChangesClient"); -const MockDocUpdaterApi = require("./helpers/MockDocUpdaterApi"); -const MockWebApi = require("./helpers/MockWebApi"); +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') +const MockDocUpdaterApi = require('./helpers/MockDocUpdaterApi') +const MockWebApi = require('./helpers/MockWebApi') -describe("Getting a diff", function() { +describe('Getting a diff', function() { + beforeEach(function(done) { + sinon.spy(MockDocUpdaterApi, 'getDoc') - beforeEach(function(done) { - sinon.spy(MockDocUpdaterApi, "getDoc"); + this.now = Date.now() + this.from = this.now - 100000000 + this.to = this.now + this.user_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.project_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: true } } - this.now = Date.now(); - this.from = this.now - 100000000; - this.to = this.now; - this.user_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.project_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: true}}; + MockWebApi.users[this.user_id] = this.user = { + email: 'user@sharelatex.com', + first_name: 'Leo', + last_name: 'Lion', + id: this.user_id + } + sinon.spy(MockWebApi, 'getUserInfo') - MockWebApi.users[this.user_id] = (this.user = { - email: "user@sharelatex.com", - first_name: "Leo", - last_name: "Lion", - id: this.user_id - }); - sinon.spy(MockWebApi, "getUserInfo"); + const twoMinutes = 2 * 60 * 1000 - const twoMinutes = 2 * 60 * 1000; + this.updates = [ + { + op: [{ i: 'one ', p: 0 }], + meta: { ts: this.from - twoMinutes, user_id: this.user_id }, + v: 3 + }, + { + op: [{ i: 'two ', p: 4 }], + meta: { ts: this.from + twoMinutes, user_id: this.user_id }, + v: (this.fromVersion = 4) + }, + { + op: [{ i: 'three ', p: 8 }], + meta: { ts: this.to - twoMinutes, user_id: this.user_id }, + v: (this.toVersion = 5) + }, + { + op: [{ i: 'four', p: 14 }], + meta: { ts: this.to + twoMinutes, user_id: this.user_id }, + v: 6 + } + ] + this.lines = ['one two three four'] + this.expected_diff = [ + { u: 'one ' }, + { + i: 'two three ', + meta: { + start_ts: this.from + twoMinutes, + end_ts: this.to - twoMinutes, + user: this.user + } + } + ] - this.updates = [{ - op: [{ i: "one ", p: 0 }], - meta: { ts: this.from - twoMinutes, user_id: this.user_id }, - v: 3 - }, { - op: [{ i: "two ", p: 4 }], - meta: { ts: this.from + twoMinutes, user_id: this.user_id }, - v: (this.fromVersion = 4) - }, { - op: [{ i: "three ", p: 8 }], - meta: { ts: this.to - twoMinutes, user_id: this.user_id }, - v: (this.toVersion = 5) - }, { - op: [{ i: "four", p: 14 }], - meta: { ts: this.to + twoMinutes, user_id: this.user_id }, - v: 6 - }]; - this.lines = ["one two three four"]; - this.expected_diff = [ - { u: "one " }, - { i: "two three ", meta: { start_ts: this.from + twoMinutes, end_ts: this.to - twoMinutes, user: this.user } } - ]; + MockDocUpdaterApi.docs[this.doc_id] = { + lines: this.lines, + version: 7 + } + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + this.updates, + error => { + if (error != null) { + throw error + } + return TrackChangesClient.getDiff( + this.project_id, + this.doc_id, + this.fromVersion, + this.toVersion, + (error, diff) => { + if (error != null) { + throw error + } + this.diff = diff.diff + return done() + } + ) + } + ) + }) + return null + }) - MockDocUpdaterApi.docs[this.doc_id] = { - lines: this.lines, - version: 7 - }; - TrackChangesApp.ensureRunning(() => { - return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { - if (error != null) { throw error; } - return TrackChangesClient.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, (error, diff) => { - if (error != null) { throw error; } - this.diff = diff.diff; - return done(); - }); - }); - }); - return null; - }); + afterEach(function() { + MockDocUpdaterApi.getDoc.restore() + MockWebApi.getUserInfo.restore() + return null + }) - afterEach(function() { - MockDocUpdaterApi.getDoc.restore(); - MockWebApi.getUserInfo.restore(); - return null; - }); + it('should return the diff', function() { + return expect(this.diff).to.deep.equal(this.expected_diff) + }) - it("should return the diff", function() { - return expect(this.diff).to.deep.equal(this.expected_diff); - }); - - return it("should get the doc from the doc updater", function() { - MockDocUpdaterApi.getDoc - .calledWith(this.project_id, this.doc_id) - .should.equal(true); - return null; - }); -}); + return it('should get the doc from the doc updater', function() { + MockDocUpdaterApi.getDoc + .calledWith(this.project_id, this.doc_id) + .should.equal(true) + return null + }) +}) diff --git a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js index 299ba96afd..d9cd43f1f8 100644 --- a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js @@ -10,154 +10,181 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { db } = mongojs; -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { db } = mongojs +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') -const TrackChangesApp = require("./helpers/TrackChangesApp"); -const TrackChangesClient = require("./helpers/TrackChangesClient"); -const MockWebApi = require("./helpers/MockWebApi"); +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') +const MockWebApi = require('./helpers/MockWebApi') -describe("Getting updates", function() { - before(function(done) { - this.now = Date.now(); - this.to = this.now; - this.user_id = ObjectId().toString(); - this.deleted_user_id = 'deleted_user'; - this.doc_id = ObjectId().toString(); - this.project_id = ObjectId().toString(); +describe('Getting updates', function() { + before(function(done) { + this.now = Date.now() + this.to = this.now + this.user_id = ObjectId().toString() + this.deleted_user_id = 'deleted_user' + this.doc_id = ObjectId().toString() + this.project_id = ObjectId().toString() - this.minutes = 60 * 1000; - this.hours = 60 * this.minutes; + this.minutes = 60 * 1000 + this.hours = 60 * this.minutes - MockWebApi.projects[this.project_id] = { - features: { - versioning: true - } - }; + MockWebApi.projects[this.project_id] = { + features: { + versioning: true + } + } - MockWebApi.users[this.user_id] = (this.user = { - email: "user@sharelatex.com", - first_name: "Leo", - last_name: "Lion", - id: this.user_id - }); - sinon.spy(MockWebApi, "getUserInfo"); + MockWebApi.users[this.user_id] = this.user = { + email: 'user@sharelatex.com', + first_name: 'Leo', + last_name: 'Lion', + id: this.user_id + } + sinon.spy(MockWebApi, 'getUserInfo') - this.updates = []; - for (let i = 0; i <= 9; i++) { - this.updates.push({ - op: [{ i: "a", p: 0 }], - meta: { ts: this.now - ((9 - i) * this.hours) - (2 * this.minutes), user_id: this.user_id }, - v: (2 * i) + 1 - }); - this.updates.push({ - op: [{ i: "b", p: 0 }], - meta: { ts: this.now - ((9 - i) * this.hours), user_id: this.user_id }, - v: (2 * i) + 2 - }); - } - this.updates[0].meta.user_id = this.deleted_user_id; + this.updates = [] + for (let i = 0; i <= 9; i++) { + this.updates.push({ + op: [{ i: 'a', p: 0 }], + meta: { + ts: this.now - (9 - i) * this.hours - 2 * this.minutes, + user_id: this.user_id + }, + v: 2 * i + 1 + }) + this.updates.push({ + op: [{ i: 'b', p: 0 }], + meta: { ts: this.now - (9 - i) * this.hours, user_id: this.user_id }, + v: 2 * i + 2 + }) + } + this.updates[0].meta.user_id = this.deleted_user_id - TrackChangesApp.ensureRunning(() => { - return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { - if (error != null) { throw error; } - return done(); - }); - }); - return null; - }); + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + this.updates, + error => { + if (error != null) { + throw error + } + return done() + } + ) + }) + return null + }) - ({ - after() { - MockWebApi.getUserInfo.restore(); - return null; - } - }); + ;({ + after() { + MockWebApi.getUserInfo.restore() + return null + } + }) - describe("getting updates up to the limit", function() { - before(function(done) { - TrackChangesClient.getUpdates(this.project_id, { before: this.to + 1, min_count: 3 }, (error, body) => { - if (error != null) { throw error; } - this.updates = body.updates; - return done(); - }); - return null; - }); + describe('getting updates up to the limit', function() { + before(function(done) { + TrackChangesClient.getUpdates( + this.project_id, + { before: this.to + 1, min_count: 3 }, + (error, body) => { + if (error != null) { + throw error + } + this.updates = body.updates + return done() + } + ) + return null + }) - it("should fetch the user details from the web api", function() { - return MockWebApi.getUserInfo - .calledWith(this.user_id) - .should.equal(true); - }); + it('should fetch the user details from the web api', function() { + return MockWebApi.getUserInfo.calledWith(this.user_id).should.equal(true) + }) - return it("should return at least the min_count number of summarized updates", function() { - const docs1 = {}; - docs1[this.doc_id] = {toV: 20, fromV: 19}; - const docs2 = {}; - docs2[this.doc_id] = {toV: 18, fromV: 17}; - const docs3 = {}; - docs3[this.doc_id] = {toV: 16, fromV: 15}; - return expect(this.updates.slice(0,3)).to.deep.equal([{ - docs: docs1, - meta: { - start_ts: this.to - (2 * this.minutes), - end_ts: this.to, - users: [this.user] - } - }, { - docs: docs2, - meta: { - start_ts: this.to - (1 * this.hours) - (2 * this.minutes), - end_ts: this.to - (1 * this.hours), - users: [this.user] - } - }, { - docs: docs3, - meta: { - start_ts: this.to - (2 * this.hours) - (2 * this.minutes), - end_ts: this.to - (2 * this.hours), - users: [this.user] - } - }]); - }); -}); + return it('should return at least the min_count number of summarized updates', function() { + const docs1 = {} + docs1[this.doc_id] = { toV: 20, fromV: 19 } + const docs2 = {} + docs2[this.doc_id] = { toV: 18, fromV: 17 } + const docs3 = {} + docs3[this.doc_id] = { toV: 16, fromV: 15 } + return expect(this.updates.slice(0, 3)).to.deep.equal([ + { + docs: docs1, + meta: { + start_ts: this.to - 2 * this.minutes, + end_ts: this.to, + users: [this.user] + } + }, + { + docs: docs2, + meta: { + start_ts: this.to - 1 * this.hours - 2 * this.minutes, + end_ts: this.to - 1 * this.hours, + users: [this.user] + } + }, + { + docs: docs3, + meta: { + start_ts: this.to - 2 * this.hours - 2 * this.minutes, + end_ts: this.to - 2 * this.hours, + users: [this.user] + } + } + ]) + }) + }) - return describe("getting updates beyond the end of the database", function() { - before(function(done) { - TrackChangesClient.getUpdates(this.project_id, { before: (this.to - (8 * this.hours)) + 1, min_count: 30 }, (error, body) => { - if (error != null) { throw error; } - this.updates = body.updates; - return done(); - }); - return null; - }); + return describe('getting updates beyond the end of the database', function() { + before(function(done) { + TrackChangesClient.getUpdates( + this.project_id, + { before: this.to - 8 * this.hours + 1, min_count: 30 }, + (error, body) => { + if (error != null) { + throw error + } + this.updates = body.updates + return done() + } + ) + return null + }) - return it("should return as many updates as it can", function() { - const docs1 = {}; - docs1[this.doc_id] = {toV: 4, fromV: 3}; - const docs2 = {}; - docs2[this.doc_id] = {toV: 2, fromV: 1}; - return expect(this.updates).to.deep.equal([{ - docs: docs1, - meta: { - start_ts: this.to - (8 * this.hours) - (2 * this.minutes), - end_ts: this.to - (8 * this.hours), - users: [this.user] - } - }, { - docs: docs2, - meta: { - start_ts: this.to - (9 * this.hours) - (2 * this.minutes), - end_ts: this.to - (9 * this.hours), - users: [this.user, null] - } - }]); - }); -}); -}); + return it('should return as many updates as it can', function() { + const docs1 = {} + docs1[this.doc_id] = { toV: 4, fromV: 3 } + const docs2 = {} + docs2[this.doc_id] = { toV: 2, fromV: 1 } + return expect(this.updates).to.deep.equal([ + { + docs: docs1, + meta: { + start_ts: this.to - 8 * this.hours - 2 * this.minutes, + end_ts: this.to - 8 * this.hours, + users: [this.user] + } + }, + { + docs: docs2, + meta: { + start_ts: this.to - 9 * this.hours - 2 * this.minutes, + end_ts: this.to - 9 * this.hours, + users: [this.user, null] + } + } + ]) + }) + }) +}) diff --git a/services/track-changes/test/acceptance/js/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js index 19e0a13914..ad5352ae44 100644 --- a/services/track-changes/test/acceptance/js/LockManagerTests.js +++ b/services/track-changes/test/acceptance/js/LockManagerTests.js @@ -9,52 +9,58 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); -const LockManager = require("../../../app/js/LockManager"); -const rclient = require("redis").createClient(Settings.redis.history); // Only works locally for now -const TrackChangesApp = require("./helpers/TrackChangesApp"); +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') +const LockManager = require('../../../app/js/LockManager') +const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now +const TrackChangesApp = require('./helpers/TrackChangesApp') -describe("Locking document", function() { +describe('Locking document', function() { + before(function(done) { + TrackChangesApp.ensureRunning(done) + return null + }) - before(function(done){ - TrackChangesApp.ensureRunning(done); - return null; - }); - - return describe("when the lock has expired in redis", function() { - before(function(done) { - LockManager.LOCK_TTL = 1; // second - LockManager.runWithLock("doc123", releaseA => { - // we create a lock A and allow it to expire in redis - return setTimeout(() => - // now we create a new lock B and try to release A - LockManager.runWithLock("doc123", releaseB => { - return releaseA(); - } // try to release lock A to see if it wipes out lock B - , (error) => {}) - - // we never release lock B so nothing should happen here - , 1500); - } // enough time to wait until the lock has expired - , error => - // we get here after trying to release lock A - done() - ); - return null; - }); + return describe('when the lock has expired in redis', function() { + before(function(done) { + LockManager.LOCK_TTL = 1 // second + LockManager.runWithLock( + 'doc123', + releaseA => { + // we create a lock A and allow it to expire in redis + return setTimeout( + () => + // now we create a new lock B and try to release A + LockManager.runWithLock( + 'doc123', + releaseB => { + return releaseA() + }, // try to release lock A to see if it wipes out lock B + error => {} + ), - return it("the new lock should not be removed by the expired locker", function(done) { - LockManager.checkLock("doc123", (err, isFree) => { - expect(isFree).to.equal(false); - return done(); - }); - return null; - }); - }); -}); + // we never release lock B so nothing should happen here + 1500 + ) + }, // enough time to wait until the lock has expired + error => + // we get here after trying to release lock A + done() + ) + return null + }) + + return it('the new lock should not be removed by the expired locker', function(done) { + LockManager.checkLock('doc123', (err, isFree) => { + expect(isFree).to.equal(false) + return done() + }) + return null + }) + }) +}) diff --git a/services/track-changes/test/acceptance/js/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js index 3c03848a8b..bfa55e8ba2 100644 --- a/services/track-changes/test/acceptance/js/RestoringVersions.js +++ b/services/track-changes/test/acceptance/js/RestoringVersions.js @@ -9,86 +9,112 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const sinon = require("sinon"); -const chai = require("chai"); -chai.should(); -const { expect } = chai; -const mongojs = require("../../../app/js/mongojs"); -const { db } = mongojs; -const { ObjectId } = mongojs; -const Settings = require("settings-sharelatex"); +const sinon = require('sinon') +const chai = require('chai') +chai.should() +const { expect } = chai +const mongojs = require('../../../app/js/mongojs') +const { db } = mongojs +const { ObjectId } = mongojs +const Settings = require('settings-sharelatex') -const TrackChangesApp = require("./helpers/TrackChangesApp"); -const TrackChangesClient = require("./helpers/TrackChangesClient"); -const MockDocUpdaterApi = require("./helpers/MockDocUpdaterApi"); -const MockWebApi = require("./helpers/MockWebApi"); +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') +const MockDocUpdaterApi = require('./helpers/MockDocUpdaterApi') +const MockWebApi = require('./helpers/MockWebApi') -describe("Restoring a version", function() { - before(function(done) { - sinon.spy(MockDocUpdaterApi, "setDoc"); +describe('Restoring a version', function() { + before(function(done) { + sinon.spy(MockDocUpdaterApi, 'setDoc') - this.now = Date.now(); - this.user_id = ObjectId().toString(); - this.doc_id = ObjectId().toString(); - this.project_id = ObjectId().toString(); - MockWebApi.projects[this.project_id] = {features: {versioning: true}}; - - const minutes = 60 * 1000; + this.now = Date.now() + this.user_id = ObjectId().toString() + this.doc_id = ObjectId().toString() + this.project_id = ObjectId().toString() + MockWebApi.projects[this.project_id] = { features: { versioning: true } } - this.updates = [{ - op: [{ i: "one ", p: 0 }], - meta: { ts: this.now - (6 * minutes), user_id: this.user_id }, - v: 3 - }, { - op: [{ i: "two ", p: 4 }], - meta: { ts: this.now - (4 * minutes), user_id: this.user_id }, - v: 4 - }, { - op: [{ i: "three ", p: 8 }], - meta: { ts: this.now - (2 * minutes), user_id: this.user_id }, - v: 5 - }, { - op: [{ i: "four", p: 14 }], - meta: { ts: this.now, user_id: this.user_id }, - v: 6 - }]; - this.lines = ["one two three four"]; - this.restored_lines = ["one two "]; - this.beforeVersion = 5; + const minutes = 60 * 1000 - MockWebApi.users[this.user_id] = (this.user = { - email: "user@sharelatex.com", - first_name: "Leo", - last_name: "Lion", - id: this.user_id - }); + this.updates = [ + { + op: [{ i: 'one ', p: 0 }], + meta: { ts: this.now - 6 * minutes, user_id: this.user_id }, + v: 3 + }, + { + op: [{ i: 'two ', p: 4 }], + meta: { ts: this.now - 4 * minutes, user_id: this.user_id }, + v: 4 + }, + { + op: [{ i: 'three ', p: 8 }], + meta: { ts: this.now - 2 * minutes, user_id: this.user_id }, + v: 5 + }, + { + op: [{ i: 'four', p: 14 }], + meta: { ts: this.now, user_id: this.user_id }, + v: 6 + } + ] + this.lines = ['one two three four'] + this.restored_lines = ['one two '] + this.beforeVersion = 5 - MockDocUpdaterApi.docs[this.doc_id] = { - lines: this.lines, - version: 7 - }; + MockWebApi.users[this.user_id] = this.user = { + email: 'user@sharelatex.com', + first_name: 'Leo', + last_name: 'Lion', + id: this.user_id + } - TrackChangesApp.ensureRunning(() => { - return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => { - if (error != null) { throw error; } - return TrackChangesClient.restoreDoc(this.project_id, this.doc_id, this.beforeVersion, this.user_id, error => { - if (error != null) { throw error; } - return done(); - }); - }); - }); - return null; - }); + MockDocUpdaterApi.docs[this.doc_id] = { + lines: this.lines, + version: 7 + } - after(function() { - MockDocUpdaterApi.setDoc.restore(); - return null; - }); + TrackChangesApp.ensureRunning(() => { + return TrackChangesClient.pushRawUpdates( + this.project_id, + this.doc_id, + this.updates, + error => { + if (error != null) { + throw error + } + return TrackChangesClient.restoreDoc( + this.project_id, + this.doc_id, + this.beforeVersion, + this.user_id, + error => { + if (error != null) { + throw error + } + return done() + } + ) + } + ) + }) + return null + }) - return it("should set the doc in the doc updater", function() { - MockDocUpdaterApi.setDoc - .calledWith(this.project_id, this.doc_id, this.restored_lines, this.user_id, true) - .should.equal(true); - return null; - }); -}); + after(function() { + MockDocUpdaterApi.setDoc.restore() + return null + }) + + return it('should set the doc in the doc updater', function() { + MockDocUpdaterApi.setDoc + .calledWith( + this.project_id, + this.doc_id, + this.restored_lines, + this.user_id, + true + ) + .should.equal(true) + return null + }) +}) diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js index c55260a007..8ebaf68081 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js @@ -10,40 +10,45 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let MockDocUpdaterApi; -const express = require("express"); -const app = express(); +let MockDocUpdaterApi +const express = require('express') +const app = express() -module.exports = (MockDocUpdaterApi = { - docs: {}, +module.exports = MockDocUpdaterApi = { + docs: {}, - getAllDoc(project_id, callback) { - if (callback == null) { callback = function(error) {}; } - return callback(null, this.docs); - }, + getAllDoc(project_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return callback(null, this.docs) + }, - run() { - app.get("/project/:project_id/doc", (req, res, next) => { - return this.getAllDoc(req.params.project_id, (error, docs) => { - if (error != null) { - res.send(500); - } - if ((docs == null)) { - return res.send(404); - } else { - return res.send(JSON.stringify(docs)); - } - }); - }); + run() { + app.get('/project/:project_id/doc', (req, res, next) => { + return this.getAllDoc(req.params.project_id, (error, docs) => { + if (error != null) { + res.send(500) + } + if (docs == null) { + return res.send(404) + } else { + return res.send(JSON.stringify(docs)) + } + }) + }) - return app.listen(3016, (error) => { - if (error != null) { throw error; } - }).on("error", (error) => { - console.error("error starting MockDocStoreApi:", error.message); - return process.exit(1); - }); - } -}); - -MockDocUpdaterApi.run(); + return app + .listen(3016, error => { + if (error != null) { + throw error + } + }) + .on('error', error => { + console.error('error starting MockDocStoreApi:', error.message) + return process.exit(1) + }) + } +} +MockDocUpdaterApi.run() diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js index 8ba6f2c6b2..38a4136d9f 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js @@ -11,58 +11,81 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let MockDocUpdaterApi; -const express = require("express"); -const app = express(); +let MockDocUpdaterApi +const express = require('express') +const app = express() -module.exports = (MockDocUpdaterApi = { - docs: {}, +module.exports = MockDocUpdaterApi = { + docs: {}, - getDoc(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return callback(null, this.docs[doc_id]); - }, + getDoc(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return callback(null, this.docs[doc_id]) + }, - setDoc(project_id, doc_id, lines, user_id, undoing, callback) { - if (callback == null) { callback = function(error) {}; } - if (!this.docs[doc_id]) { this.docs[doc_id] = {}; } - this.docs[doc_id].lines = lines; - return callback(); - }, + setDoc(project_id, doc_id, lines, user_id, undoing, callback) { + if (callback == null) { + callback = function(error) {} + } + if (!this.docs[doc_id]) { + this.docs[doc_id] = {} + } + this.docs[doc_id].lines = lines + return callback() + }, - run() { - app.get("/project/:project_id/doc/:doc_id", (req, res, next) => { - return this.getDoc(req.params.project_id, req.params.doc_id, (error, doc) => { - if (error != null) { - res.send(500); - } - if ((doc == null)) { - return res.send(404); - } else { - return res.send(JSON.stringify(doc)); - } - }); - }); + run() { + app.get('/project/:project_id/doc/:doc_id', (req, res, next) => { + return this.getDoc( + req.params.project_id, + req.params.doc_id, + (error, doc) => { + if (error != null) { + res.send(500) + } + if (doc == null) { + return res.send(404) + } else { + return res.send(JSON.stringify(doc)) + } + } + ) + }) - app.post("/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) => { - return this.setDoc(req.params.project_id, req.params.doc_id, req.body.lines, req.body.user_id, req.body.undoing, (errr, doc) => { - if (typeof error !== 'undefined' && error !== null) { - return res.send(500); - } else { - return res.send(204); - } - }); - }); + app.post( + '/project/:project_id/doc/:doc_id', + express.bodyParser(), + (req, res, next) => { + return this.setDoc( + req.params.project_id, + req.params.doc_id, + req.body.lines, + req.body.user_id, + req.body.undoing, + (errr, doc) => { + if (typeof error !== 'undefined' && error !== null) { + return res.send(500) + } else { + return res.send(204) + } + } + ) + } + ) - return app.listen(3003, (error) => { - if (error != null) { throw error; } - }).on("error", (error) => { - console.error("error starting MockDocUpdaterApi:", error.message); - return process.exit(1); - }); - } -}); - - -MockDocUpdaterApi.run(); + return app + .listen(3003, error => { + if (error != null) { + throw error + } + }) + .on('error', error => { + console.error('error starting MockDocUpdaterApi:', error.message) + return process.exit(1) + }) + } +} +MockDocUpdaterApi.run() diff --git a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js index cf1350f97d..ebf1b05e18 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js @@ -10,60 +10,67 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let MockWebApi; -const express = require("express"); -const app = express(); +let MockWebApi +const express = require('express') +const app = express() -module.exports = (MockWebApi = { - users: {}, +module.exports = MockWebApi = { + users: {}, - projects: {}, + projects: {}, - getUserInfo(user_id, callback) { - if (callback == null) { callback = function(error) {}; } - return callback(null, this.users[user_id] || null); - }, + getUserInfo(user_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return callback(null, this.users[user_id] || null) + }, - getProjectDetails(project_id, callback) { - if (callback == null) { callback = function(error, project) {}; } - return callback(null, this.projects[project_id]); - }, + getProjectDetails(project_id, callback) { + if (callback == null) { + callback = function(error, project) {} + } + return callback(null, this.projects[project_id]) + }, - run() { - app.get("/user/:user_id/personal_info", (req, res, next) => { - return this.getUserInfo(req.params.user_id, (error, user) => { - if (error != null) { - res.send(500); - } - if ((user == null)) { - return res.send(404); - } else { - return res.send(JSON.stringify(user)); - } - }); - }); + run() { + app.get('/user/:user_id/personal_info', (req, res, next) => { + return this.getUserInfo(req.params.user_id, (error, user) => { + if (error != null) { + res.send(500) + } + if (user == null) { + return res.send(404) + } else { + return res.send(JSON.stringify(user)) + } + }) + }) - app.get("/project/:project_id/details", (req, res, next) => { - return this.getProjectDetails(req.params.project_id, (error, project) => { - if (error != null) { - res.send(500); - } - if ((project == null)) { - return res.send(404); - } else { - return res.send(JSON.stringify(project)); - } - }); - }); + app.get('/project/:project_id/details', (req, res, next) => { + return this.getProjectDetails(req.params.project_id, (error, project) => { + if (error != null) { + res.send(500) + } + if (project == null) { + return res.send(404) + } else { + return res.send(JSON.stringify(project)) + } + }) + }) - return app.listen(3000, (error) => { - if (error != null) { throw error; } - }).on("error", (error) => { - console.error("error starting MockWebApiServer:", error.message); - return process.exit(1); - }); - } -}); - -MockWebApi.run(); + return app + .listen(3000, error => { + if (error != null) { + throw error + } + }) + .on('error', error => { + console.error('error starting MockWebApiServer:', error.message) + return process.exit(1) + }) + } +} +MockWebApi.run() diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js index 464961c689..1ce662548e 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js @@ -12,40 +12,55 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const app = require('../../../../app'); -require("logger-sharelatex"); -const logger = require("logger-sharelatex"); -const Settings = require("settings-sharelatex"); +const app = require('../../../../app') +require('logger-sharelatex') +const logger = require('logger-sharelatex') +const Settings = require('settings-sharelatex') module.exports = { - running: false, - initing: false, - callbacks: [], - ensureRunning(callback) { - if (callback == null) { callback = function(error) {}; } - if (this.running) { - return callback(); - } else if (this.initing) { - return this.callbacks.push(callback); - } else { - this.initing = true; - this.callbacks.push(callback); - return app.listen(__guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x => x.port), "localhost", error => { - if (error != null) { throw error; } - this.running = true; - logger.log("track changes running in dev mode"); + running: false, + initing: false, + callbacks: [], + ensureRunning(callback) { + if (callback == null) { + callback = function(error) {} + } + if (this.running) { + return callback() + } else if (this.initing) { + return this.callbacks.push(callback) + } else { + this.initing = true + this.callbacks.push(callback) + return app.listen( + __guard__( + Settings.internal != null + ? Settings.internal.trackchanges + : undefined, + x => x.port + ), + 'localhost', + error => { + if (error != null) { + throw error + } + this.running = true + logger.log('track changes running in dev mode') - return (() => { - const result = []; - for (callback of Array.from(this.callbacks)) { - result.push(callback()); - } - return result; - })(); - }); - } - } -}; + return (() => { + const result = [] + for (callback of Array.from(this.callbacks)) { + result.push(callback()) + } + return result + })() + } + ) + } + } +} function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index df66425a49..d49980976b 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -12,198 +12,277 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let TrackChangesClient; -const async = require('async'); -const zlib = require('zlib'); -const request = require("request"); -const Settings = require("settings-sharelatex"); -const rclient = require("redis-sharelatex").createClient(Settings.redis.history); // Only works locally for now -const Keys = Settings.redis.history.key_schema; -const {db, ObjectId} = require("../../../../app/js/mongojs"); +let TrackChangesClient +const async = require('async') +const zlib = require('zlib') +const request = require('request') +const Settings = require('settings-sharelatex') +const rclient = require('redis-sharelatex').createClient(Settings.redis.history) // Only works locally for now +const Keys = Settings.redis.history.key_schema +const { db, ObjectId } = require('../../../../app/js/mongojs') -const aws = require("aws-sdk"); +const aws = require('aws-sdk') const s3 = new aws.S3({ - accessKeyId: Settings.trackchanges.s3.key, - secretAccessKey: Settings.trackchanges.s3.secret, - endpoint: Settings.trackchanges.s3.endpoint, - s3ForcePathStyle: Settings.trackchanges.s3.pathStyle -}); -const S3_BUCKET = Settings.trackchanges.stores.doc_history; + accessKeyId: Settings.trackchanges.s3.key, + secretAccessKey: Settings.trackchanges.s3.secret, + endpoint: Settings.trackchanges.s3.endpoint, + s3ForcePathStyle: Settings.trackchanges.s3.pathStyle +}) +const S3_BUCKET = Settings.trackchanges.stores.doc_history -module.exports = (TrackChangesClient = { - flushAndGetCompressedUpdates(project_id, doc_id, callback) { - if (callback == null) { callback = function(error, updates) {}; } - return TrackChangesClient.flushDoc(project_id, doc_id, (error) => { - if (error != null) { return callback(error); } - return TrackChangesClient.getCompressedUpdates(doc_id, callback); - }); - }, +module.exports = TrackChangesClient = { + flushAndGetCompressedUpdates(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error, updates) {} + } + return TrackChangesClient.flushDoc(project_id, doc_id, error => { + if (error != null) { + return callback(error) + } + return TrackChangesClient.getCompressedUpdates(doc_id, callback) + }) + }, - flushDoc(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return request.post({ - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush` - }, (error, response, body) => { - response.statusCode.should.equal(204); - return callback(error); - }); - }, + flushDoc(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return request.post( + { + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush` + }, + (error, response, body) => { + response.statusCode.should.equal(204) + return callback(error) + } + ) + }, - flushProject(project_id, callback) { - if (callback == null) { callback = function(error) {}; } - return request.post({ - url: `http://localhost:3015/project/${project_id}/flush` - }, (error, response, body) => { - response.statusCode.should.equal(204); - return callback(error); - }); - }, + flushProject(project_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return request.post( + { + url: `http://localhost:3015/project/${project_id}/flush` + }, + (error, response, body) => { + response.statusCode.should.equal(204) + return callback(error) + } + ) + }, - getCompressedUpdates(doc_id, callback) { - if (callback == null) { callback = function(error, updates) {}; } - return db.docHistory - .find({doc_id: ObjectId(doc_id)}) - .sort({"meta.end_ts": 1}) - .toArray(callback); - }, + getCompressedUpdates(doc_id, callback) { + if (callback == null) { + callback = function(error, updates) {} + } + return db.docHistory + .find({ doc_id: ObjectId(doc_id) }) + .sort({ 'meta.end_ts': 1 }) + .toArray(callback) + }, - getProjectMetaData(project_id, callback) { - if (callback == null) { callback = function(error, updates) {}; } - return db.projectHistoryMetaData - .find({ - project_id: ObjectId(project_id) - }, - (error, projects) => callback(error, projects[0])); - }, + getProjectMetaData(project_id, callback) { + if (callback == null) { + callback = function(error, updates) {} + } + return db.projectHistoryMetaData.find( + { + project_id: ObjectId(project_id) + }, + (error, projects) => callback(error, projects[0]) + ) + }, - setPreserveHistoryForProject(project_id, callback) { - if (callback == null) { callback = function(error) {}; } - return db.projectHistoryMetaData.update({ - project_id: ObjectId(project_id) - }, { - $set: { preserveHistory: true } - }, { - upsert: true - }, callback); - }, + setPreserveHistoryForProject(project_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return db.projectHistoryMetaData.update( + { + project_id: ObjectId(project_id) + }, + { + $set: { preserveHistory: true } + }, + { + upsert: true + }, + callback + ) + }, - pushRawUpdates(project_id, doc_id, updates, callback) { - if (callback == null) { callback = function(error) {}; } - return rclient.sadd(Keys.docsWithHistoryOps({project_id}), doc_id, (error) => { - if (error != null) { return callback(error); } - return rclient.rpush(Keys.uncompressedHistoryOps({doc_id}), ...Array.from(((Array.from(updates).map((u) => JSON.stringify(u))))), callback); - }); - }, + pushRawUpdates(project_id, doc_id, updates, callback) { + if (callback == null) { + callback = function(error) {} + } + return rclient.sadd( + Keys.docsWithHistoryOps({ project_id }), + doc_id, + error => { + if (error != null) { + return callback(error) + } + return rclient.rpush( + Keys.uncompressedHistoryOps({ doc_id }), + ...Array.from(Array.from(updates).map(u => JSON.stringify(u))), + callback + ) + } + ) + }, - getDiff(project_id, doc_id, from, to, callback) { - if (callback == null) { callback = function(error, diff) {}; } - return request.get({ - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}` - }, (error, response, body) => { - response.statusCode.should.equal(200); - return callback(null, JSON.parse(body)); - }); - }, + getDiff(project_id, doc_id, from, to, callback) { + if (callback == null) { + callback = function(error, diff) {} + } + return request.get( + { + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}` + }, + (error, response, body) => { + response.statusCode.should.equal(200) + return callback(null, JSON.parse(body)) + } + ) + }, - getUpdates(project_id, options, callback) { - if (callback == null) { callback = function(error, body) {}; } - return request.get({ - url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}` - }, (error, response, body) => { - response.statusCode.should.equal(200); - return callback(null, JSON.parse(body)); - }); - }, + getUpdates(project_id, options, callback) { + if (callback == null) { + callback = function(error, body) {} + } + return request.get( + { + url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}` + }, + (error, response, body) => { + response.statusCode.should.equal(200) + return callback(null, JSON.parse(body)) + } + ) + }, - restoreDoc(project_id, doc_id, version, user_id, callback) { - if (callback == null) { callback = function(error) {}; } - return request.post({ - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/version/${version}/restore`, - headers: { - "X-User-Id": user_id - } - }, (error, response, body) => { - response.statusCode.should.equal(204); - return callback(null); - }); - }, + restoreDoc(project_id, doc_id, version, user_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return request.post( + { + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/version/${version}/restore`, + headers: { + 'X-User-Id': user_id + } + }, + (error, response, body) => { + response.statusCode.should.equal(204) + return callback(null) + } + ) + }, - pushDocHistory(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return request.post({ - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push` - }, (error, response, body) => { - response.statusCode.should.equal(204); - return callback(error); - }); - }, + pushDocHistory(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return request.post( + { + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push` + }, + (error, response, body) => { + response.statusCode.should.equal(204) + return callback(error) + } + ) + }, - pullDocHistory(project_id, doc_id, callback) { - if (callback == null) { callback = function(error) {}; } - return request.post({ - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull` - }, (error, response, body) => { - response.statusCode.should.equal(204); - return callback(error); - }); - }, + pullDocHistory(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error) {} + } + return request.post( + { + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull` + }, + (error, response, body) => { + response.statusCode.should.equal(204) + return callback(error) + } + ) + }, - waitForS3(done, retries) { - if (retries == null) { retries = 42; } - if (!Settings.trackchanges.s3.endpoint) { - return done(); - } + waitForS3(done, retries) { + if (retries == null) { + retries = 42 + } + if (!Settings.trackchanges.s3.endpoint) { + return done() + } - return request.get(`${Settings.trackchanges.s3.endpoint}/`, (err, res) => { - if (res && (res.statusCode < 500)) { - return done(); - } + return request.get(`${Settings.trackchanges.s3.endpoint}/`, (err, res) => { + if (res && res.statusCode < 500) { + return done() + } - if (retries === 0) { - return done(err || new Error(`s3 returned ${res.statusCode}`)); - } + if (retries === 0) { + return done(err || new Error(`s3 returned ${res.statusCode}`)) + } - return setTimeout(() => TrackChangesClient.waitForS3(done, --retries) - , 1000); - }); - }, + return setTimeout( + () => TrackChangesClient.waitForS3(done, --retries), + 1000 + ) + }) + }, - getS3Doc(project_id, doc_id, pack_id, callback) { - if (callback == null) { callback = function(error, body) {}; } - const params = { - Bucket: S3_BUCKET, - Key: `${project_id}/changes-${doc_id}/pack-${pack_id}` - }; + getS3Doc(project_id, doc_id, pack_id, callback) { + if (callback == null) { + callback = function(error, body) {} + } + const params = { + Bucket: S3_BUCKET, + Key: `${project_id}/changes-${doc_id}/pack-${pack_id}` + } - return s3.getObject(params, (error, data) => { - if (error != null) { return callback(error); } - const body = data.Body; - if ((body == null)) { return callback(new Error("empty response from s3")); } - return zlib.gunzip(body, (err, result) => { - if (err != null) { return callback(err); } - return callback(null, JSON.parse(result.toString())); - }); - }); - }, + return s3.getObject(params, (error, data) => { + if (error != null) { + return callback(error) + } + const body = data.Body + if (body == null) { + return callback(new Error('empty response from s3')) + } + return zlib.gunzip(body, (err, result) => { + if (err != null) { + return callback(err) + } + return callback(null, JSON.parse(result.toString())) + }) + }) + }, - removeS3Doc(project_id, doc_id, callback) { - if (callback == null) { callback = function(error, res, body) {}; } - let params = { - Bucket: S3_BUCKET, - Prefix: `${project_id}/changes-${doc_id}` - }; + removeS3Doc(project_id, doc_id, callback) { + if (callback == null) { + callback = function(error, res, body) {} + } + let params = { + Bucket: S3_BUCKET, + Prefix: `${project_id}/changes-${doc_id}` + } - return s3.listObjects(params, (error, data) => { - if (error != null) { return callback(error); } + return s3.listObjects(params, (error, data) => { + if (error != null) { + return callback(error) + } - params = { - Bucket: S3_BUCKET, - Delete: { - Objects: data.Contents.map(s3object => ({Key: s3object.Key})) - } - }; + params = { + Bucket: S3_BUCKET, + Delete: { + Objects: data.Contents.map(s3object => ({ Key: s3object.Key })) + } + } - return s3.deleteObjects(params, callback); - }); - } -}); + return s3.deleteObjects(params, callback) + }) + } +} From 0c8873fd2e75c4467fda794841c161a30c8139a9 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:35:59 +0100 Subject: [PATCH 480/549] decaffeinate: rename individual coffee files to js files --- services/track-changes/{app.coffee => app.js} | 0 .../config/{settings.defaults.coffee => settings.defaults.js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename services/track-changes/{app.coffee => app.js} (100%) rename services/track-changes/config/{settings.defaults.coffee => settings.defaults.js} (100%) diff --git a/services/track-changes/app.coffee b/services/track-changes/app.js similarity index 100% rename from services/track-changes/app.coffee rename to services/track-changes/app.js diff --git a/services/track-changes/config/settings.defaults.coffee b/services/track-changes/config/settings.defaults.js similarity index 100% rename from services/track-changes/config/settings.defaults.coffee rename to services/track-changes/config/settings.defaults.js From 0a8e936c47dd8707c1f597be333a961287471218 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:36:01 +0100 Subject: [PATCH 481/549] decaffeinate: convert individual files to js --- services/track-changes/app.js | 163 ++++++++++-------- .../track-changes/config/settings.defaults.js | 104 ++++++----- 2 files changed, 154 insertions(+), 113 deletions(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 972b38b7a7..79d58d0ccf 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -1,98 +1,121 @@ -Metrics = require "metrics-sharelatex" -Metrics.initialize("track-changes") -Settings = require "settings-sharelatex" -logger = require "logger-sharelatex" -TrackChangesLogger = logger.initialize("track-changes").logger +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__ + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +const Metrics = require("metrics-sharelatex"); +Metrics.initialize("track-changes"); +const Settings = require("settings-sharelatex"); +const logger = require("logger-sharelatex"); +const TrackChangesLogger = logger.initialize("track-changes").logger; -if Settings.sentry?.dsn? - logger.initializeErrorReporting(Settings.sentry.dsn) - -# log updates as truncated strings -truncateFn = (updates) -> - JSON.parse( - JSON.stringify updates, (key, value) -> - if typeof value == 'string' && (len = value.length) > 80 - return value.substr(0,32) + "...(message of length #{len} truncated)..." + value.substr(-32) - else - return value - ) - -TrackChangesLogger.addSerializers { - rawUpdate: truncateFn - rawUpdates: truncateFn - newUpdates: truncateFn - lastUpdate: truncateFn +if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) { + logger.initializeErrorReporting(Settings.sentry.dsn); } -Path = require "path" +// log updates as truncated strings +const truncateFn = updates => + JSON.parse( + JSON.stringify(updates, function(key, value) { + let len; + if ((typeof value === 'string') && ((len = value.length) > 80)) { + return value.substr(0,32) + `...(message of length ${len} truncated)...` + value.substr(-32); + } else { + return value; + } + }) + ) + ; -Metrics.memory.monitor(logger) +TrackChangesLogger.addSerializers({ + rawUpdate: truncateFn, + rawUpdates: truncateFn, + newUpdates: truncateFn, + lastUpdate: truncateFn +}); -child_process = require "child_process" +const Path = require("path"); -HttpController = require "./app/js/HttpController" -express = require "express" -app = express() +Metrics.memory.monitor(logger); -app.use Metrics.http.monitor(logger) +const child_process = require("child_process"); -Metrics.injectMetricsRoute(app) +const HttpController = require("./app/js/HttpController"); +const express = require("express"); +const app = express(); -app.post "/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc +app.use(Metrics.http.monitor(logger)); -app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff +Metrics.injectMetricsRoute(app); -app.get "/project/:project_id/doc/:doc_id/check", HttpController.checkDoc +app.post("/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc); -app.get "/project/:project_id/updates", HttpController.getUpdates +app.get("/project/:project_id/doc/:doc_id/diff", HttpController.getDiff); -app.post "/project/:project_id/flush", HttpController.flushProject +app.get("/project/:project_id/doc/:doc_id/check", HttpController.checkDoc); -app.post "/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore +app.get("/project/:project_id/updates", HttpController.getUpdates); -app.post '/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory -app.post '/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory +app.post("/project/:project_id/flush", HttpController.flushProject); -app.post '/flush/all', HttpController.flushAll -app.post '/check/dangling', HttpController.checkDanglingUpdates +app.post("/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore); -packWorker = null # use a single packing worker +app.post('/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory); +app.post('/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory); -app.post "/pack", (req, res, next) -> - if packWorker? - res.send "pack already running" - else - logger.log "running pack" +app.post('/flush/all', HttpController.flushAll); +app.post('/check/dangling', HttpController.checkDanglingUpdates); + +let packWorker = null; // use a single packing worker + +app.post("/pack", function(req, res, next) { + if (packWorker != null) { + return res.send("pack already running"); + } else { + logger.log("running pack"); packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js', - [req.query.limit || 1000, req.query.delay || 1000, req.query.timeout || 30*60*1000]) - packWorker.on 'exit', (code, signal) -> - logger.log {code, signal}, "history auto pack exited" - packWorker = null - res.send "pack started" + [req.query.limit || 1000, req.query.delay || 1000, req.query.timeout || (30*60*1000)]); + packWorker.on('exit', function(code, signal) { + logger.log({code, signal}, "history auto pack exited"); + return packWorker = null; + }); + return res.send("pack started"); + } +}); -app.get "/status", (req, res, next) -> - res.send "track-changes is alive" +app.get("/status", (req, res, next) => res.send("track-changes is alive")); -app.get "/oops", (req, res, next) -> - throw new Error("dummy test error") +app.get("/oops", function(req, res, next) { + throw new Error("dummy test error"); +}); -app.get "/check_lock", HttpController.checkLock +app.get("/check_lock", HttpController.checkLock); -app.get "/health_check", HttpController.healthCheck +app.get("/health_check", HttpController.healthCheck); -app.use (error, req, res, next) -> - logger.error err: error, req: req, "an internal error occured" - res.send 500 +app.use(function(error, req, res, next) { + logger.error({err: error, req}, "an internal error occured"); + return res.send(500); +}); -port = Settings.internal?.trackchanges?.port or 3015 -host = Settings.internal?.trackchanges?.host or "localhost" +const port = __guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x => x.port) || 3015; +const host = __guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x1 => x1.host) || "localhost"; -if !module.parent # Called directly - app.listen port, host, (error) -> - if error? - logger.error err: error, "could not start track-changes server" - else - logger.info "trackchanges starting up, listening on #{host}:#{port}" +if (!module.parent) { // Called directly + app.listen(port, host, function(error) { + if (error != null) { + return logger.error({err: error}, "could not start track-changes server"); + } else { + return logger.info(`trackchanges starting up, listening on ${host}:${port}`); + } + }); +} -module.exports = app +module.exports = app; + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/services/track-changes/config/settings.defaults.js b/services/track-changes/config/settings.defaults.js index 897c5a0536..c26ccaa4f9 100755 --- a/services/track-changes/config/settings.defaults.js +++ b/services/track-changes/config/settings.defaults.js @@ -1,51 +1,69 @@ -Path = require('path') -TMP_DIR = process.env["TMP_PATH"] or Path.resolve(Path.join(__dirname, "../../", "tmp")) +const Path = require('path'); +const TMP_DIR = process.env["TMP_PATH"] || Path.resolve(Path.join(__dirname, "../../", "tmp")); -module.exports = - mongo: - url: process.env['MONGO_CONNECTION_STRING'] or "mongodb://#{process.env["MONGO_HOST"] or "localhost"}/sharelatex" +module.exports = { + mongo: { + url: process.env['MONGO_CONNECTION_STRING'] || `mongodb://${process.env["MONGO_HOST"] || "localhost"}/sharelatex` + }, - internal: - trackchanges: - port: 3015 - host: process.env["LISTEN_ADDRESS"] or "localhost" - apis: - documentupdater: - url: "http://#{process.env["DOCUMENT_UPDATER_HOST"] or process.env["DOCUPDATER_HOST"] or "localhost"}:3003" - docstore: - url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016" - web: - url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}" - user: process.env['WEB_API_USER'] or "sharelatex" - pass: process.env['WEB_API_PASSWORD'] or "password" - redis: - lock: - host: process.env["REDIS_HOST"] or "localhost" - port: process.env['REDIS_PORT'] or 6379 - password: process.env["REDIS_PASSWORD"] or "" - key_schema: - historyLock: ({doc_id}) -> "HistoryLock:{#{doc_id}}" - historyIndexLock: ({project_id}) -> "HistoryIndexLock:{#{project_id}}" - history: - host: process.env["REDIS_HOST"] or "localhost" - port: process.env['REDIS_PORT'] or 6379 - password: process.env["REDIS_PASSWORD"] or "" - key_schema: - uncompressedHistoryOps: ({doc_id}) -> "UncompressedHistoryOps:{#{doc_id}}" - docsWithHistoryOps: ({project_id}) -> "DocsWithHistoryOps:{#{project_id}}" + internal: { + trackchanges: { + port: 3015, + host: process.env["LISTEN_ADDRESS"] || "localhost" + } + }, + apis: { + documentupdater: { + url: `http://${process.env["DOCUMENT_UPDATER_HOST"] || process.env["DOCUPDATER_HOST"] || "localhost"}:3003` + }, + docstore: { + url: `http://${process.env["DOCSTORE_HOST"] || "localhost"}:3016` + }, + web: { + url: `http://${process.env['WEB_API_HOST'] || process.env['WEB_HOST'] || "localhost"}:${process.env['WEB_API_PORT'] || process.env['WEB_PORT'] || 3000}`, + user: process.env['WEB_API_USER'] || "sharelatex", + pass: process.env['WEB_API_PASSWORD'] || "password" + } + }, + redis: { + lock: { + host: process.env["REDIS_HOST"] || "localhost", + port: process.env['REDIS_PORT'] || 6379, + password: process.env["REDIS_PASSWORD"] || "", + key_schema: { + historyLock({doc_id}) { return `HistoryLock:{${doc_id}}`; }, + historyIndexLock({project_id}) { return `HistoryIndexLock:{${project_id}}`; } + } + }, + history: { + host: process.env["REDIS_HOST"] || "localhost", + port: process.env['REDIS_PORT'] || 6379, + password: process.env["REDIS_PASSWORD"] || "", + key_schema: { + uncompressedHistoryOps({doc_id}) { return `UncompressedHistoryOps:{${doc_id}}`; }, + docsWithHistoryOps({project_id}) { return `DocsWithHistoryOps:{${project_id}}`; } + } + } + }, - trackchanges: - s3: - key: process.env['AWS_ACCESS_KEY_ID'] - secret: process.env['AWS_SECRET_ACCESS_KEY'] - endpoint: process.env['AWS_S3_ENDPOINT'] - pathStyle: process.env['AWS_S3_PATH_STYLE'] == 'true' - stores: + trackchanges: { + s3: { + key: process.env['AWS_ACCESS_KEY_ID'], + secret: process.env['AWS_SECRET_ACCESS_KEY'], + endpoint: process.env['AWS_S3_ENDPOINT'], + pathStyle: process.env['AWS_S3_PATH_STYLE'] === 'true' + }, + stores: { doc_history: process.env['AWS_BUCKET'] - continueOnError: process.env['TRACK_CHANGES_CONTINUE_ON_ERROR'] or false + }, + continueOnError: process.env['TRACK_CHANGES_CONTINUE_ON_ERROR'] || false + }, - path: + path: { dumpFolder: Path.join(TMP_DIR, "dumpFolder") + }, - sentry: + sentry: { dsn: process.env.SENTRY_DSN + } +}; From 572446956e506e339f266fe40f8ac271370dd44a Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:36:02 +0100 Subject: [PATCH 482/549] prettier: convert individual decaffeinated files to Prettier format --- services/track-changes/app.js | 181 ++++++++++-------- .../track-changes/config/settings.defaults.js | 145 +++++++------- 2 files changed, 183 insertions(+), 143 deletions(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 79d58d0ccf..a69cdabc88 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -5,117 +5,140 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const Metrics = require("metrics-sharelatex"); -Metrics.initialize("track-changes"); -const Settings = require("settings-sharelatex"); -const logger = require("logger-sharelatex"); -const TrackChangesLogger = logger.initialize("track-changes").logger; +const Metrics = require('metrics-sharelatex') +Metrics.initialize('track-changes') +const Settings = require('settings-sharelatex') +const logger = require('logger-sharelatex') +const TrackChangesLogger = logger.initialize('track-changes').logger if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) { - logger.initializeErrorReporting(Settings.sentry.dsn); + logger.initializeErrorReporting(Settings.sentry.dsn) } // log updates as truncated strings const truncateFn = updates => - JSON.parse( - JSON.stringify(updates, function(key, value) { - let len; - if ((typeof value === 'string') && ((len = value.length) > 80)) { - return value.substr(0,32) + `...(message of length ${len} truncated)...` + value.substr(-32); - } else { - return value; - } - }) - ) - ; - + JSON.parse( + JSON.stringify(updates, function(key, value) { + let len + if (typeof value === 'string' && (len = value.length) > 80) { + return ( + value.substr(0, 32) + + `...(message of length ${len} truncated)...` + + value.substr(-32) + ) + } else { + return value + } + }) + ) TrackChangesLogger.addSerializers({ - rawUpdate: truncateFn, - rawUpdates: truncateFn, - newUpdates: truncateFn, - lastUpdate: truncateFn -}); + rawUpdate: truncateFn, + rawUpdates: truncateFn, + newUpdates: truncateFn, + lastUpdate: truncateFn +}) -const Path = require("path"); +const Path = require('path') -Metrics.memory.monitor(logger); +Metrics.memory.monitor(logger) -const child_process = require("child_process"); +const child_process = require('child_process') -const HttpController = require("./app/js/HttpController"); -const express = require("express"); -const app = express(); +const HttpController = require('./app/js/HttpController') +const express = require('express') +const app = express() -app.use(Metrics.http.monitor(logger)); +app.use(Metrics.http.monitor(logger)) -Metrics.injectMetricsRoute(app); +Metrics.injectMetricsRoute(app) -app.post("/project/:project_id/doc/:doc_id/flush", HttpController.flushDoc); +app.post('/project/:project_id/doc/:doc_id/flush', HttpController.flushDoc) -app.get("/project/:project_id/doc/:doc_id/diff", HttpController.getDiff); +app.get('/project/:project_id/doc/:doc_id/diff', HttpController.getDiff) -app.get("/project/:project_id/doc/:doc_id/check", HttpController.checkDoc); +app.get('/project/:project_id/doc/:doc_id/check', HttpController.checkDoc) -app.get("/project/:project_id/updates", HttpController.getUpdates); +app.get('/project/:project_id/updates', HttpController.getUpdates) -app.post("/project/:project_id/flush", HttpController.flushProject); +app.post('/project/:project_id/flush', HttpController.flushProject) -app.post("/project/:project_id/doc/:doc_id/version/:version/restore", HttpController.restore); +app.post( + '/project/:project_id/doc/:doc_id/version/:version/restore', + HttpController.restore +) -app.post('/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory); -app.post('/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory); +app.post('/project/:project_id/doc/:doc_id/push', HttpController.pushDocHistory) +app.post('/project/:project_id/doc/:doc_id/pull', HttpController.pullDocHistory) -app.post('/flush/all', HttpController.flushAll); -app.post('/check/dangling', HttpController.checkDanglingUpdates); +app.post('/flush/all', HttpController.flushAll) +app.post('/check/dangling', HttpController.checkDanglingUpdates) -let packWorker = null; // use a single packing worker +let packWorker = null // use a single packing worker -app.post("/pack", function(req, res, next) { - if (packWorker != null) { - return res.send("pack already running"); - } else { - logger.log("running pack"); - packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js', - [req.query.limit || 1000, req.query.delay || 1000, req.query.timeout || (30*60*1000)]); - packWorker.on('exit', function(code, signal) { - logger.log({code, signal}, "history auto pack exited"); - return packWorker = null; - }); - return res.send("pack started"); - } -}); +app.post('/pack', function(req, res, next) { + if (packWorker != null) { + return res.send('pack already running') + } else { + logger.log('running pack') + packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js', [ + req.query.limit || 1000, + req.query.delay || 1000, + req.query.timeout || 30 * 60 * 1000 + ]) + packWorker.on('exit', function(code, signal) { + logger.log({ code, signal }, 'history auto pack exited') + return (packWorker = null) + }) + return res.send('pack started') + } +}) -app.get("/status", (req, res, next) => res.send("track-changes is alive")); +app.get('/status', (req, res, next) => res.send('track-changes is alive')) -app.get("/oops", function(req, res, next) { - throw new Error("dummy test error"); -}); +app.get('/oops', function(req, res, next) { + throw new Error('dummy test error') +}) -app.get("/check_lock", HttpController.checkLock); +app.get('/check_lock', HttpController.checkLock) -app.get("/health_check", HttpController.healthCheck); +app.get('/health_check', HttpController.healthCheck) app.use(function(error, req, res, next) { - logger.error({err: error, req}, "an internal error occured"); - return res.send(500); -}); + logger.error({ err: error, req }, 'an internal error occured') + return res.send(500) +}) -const port = __guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x => x.port) || 3015; -const host = __guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x1 => x1.host) || "localhost"; +const port = + __guard__( + Settings.internal != null ? Settings.internal.trackchanges : undefined, + x => x.port + ) || 3015 +const host = + __guard__( + Settings.internal != null ? Settings.internal.trackchanges : undefined, + x1 => x1.host + ) || 'localhost' -if (!module.parent) { // Called directly - app.listen(port, host, function(error) { - if (error != null) { - return logger.error({err: error}, "could not start track-changes server"); - } else { - return logger.info(`trackchanges starting up, listening on ${host}:${port}`); - } - }); +if (!module.parent) { + // Called directly + app.listen(port, host, function(error) { + if (error != null) { + return logger.error( + { err: error }, + 'could not start track-changes server' + ) + } else { + return logger.info( + `trackchanges starting up, listening on ${host}:${port}` + ) + } + }) } -module.exports = app; - +module.exports = app function __guard__(value, transform) { - return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; -} \ No newline at end of file + return typeof value !== 'undefined' && value !== null + ? transform(value) + : undefined +} diff --git a/services/track-changes/config/settings.defaults.js b/services/track-changes/config/settings.defaults.js index c26ccaa4f9..543feea917 100755 --- a/services/track-changes/config/settings.defaults.js +++ b/services/track-changes/config/settings.defaults.js @@ -1,69 +1,86 @@ -const Path = require('path'); -const TMP_DIR = process.env["TMP_PATH"] || Path.resolve(Path.join(__dirname, "../../", "tmp")); +const Path = require('path') +const TMP_DIR = + process.env.TMP_PATH || Path.resolve(Path.join(__dirname, '../../', 'tmp')) module.exports = { - mongo: { - url: process.env['MONGO_CONNECTION_STRING'] || `mongodb://${process.env["MONGO_HOST"] || "localhost"}/sharelatex` - }, + mongo: { + url: + process.env.MONGO_CONNECTION_STRING || + `mongodb://${process.env.MONGO_HOST || 'localhost'}/sharelatex` + }, - internal: { - trackchanges: { - port: 3015, - host: process.env["LISTEN_ADDRESS"] || "localhost" - } - }, - apis: { - documentupdater: { - url: `http://${process.env["DOCUMENT_UPDATER_HOST"] || process.env["DOCUPDATER_HOST"] || "localhost"}:3003` - }, - docstore: { - url: `http://${process.env["DOCSTORE_HOST"] || "localhost"}:3016` - }, - web: { - url: `http://${process.env['WEB_API_HOST'] || process.env['WEB_HOST'] || "localhost"}:${process.env['WEB_API_PORT'] || process.env['WEB_PORT'] || 3000}`, - user: process.env['WEB_API_USER'] || "sharelatex", - pass: process.env['WEB_API_PASSWORD'] || "password" - } - }, - redis: { - lock: { - host: process.env["REDIS_HOST"] || "localhost", - port: process.env['REDIS_PORT'] || 6379, - password: process.env["REDIS_PASSWORD"] || "", - key_schema: { - historyLock({doc_id}) { return `HistoryLock:{${doc_id}}`; }, - historyIndexLock({project_id}) { return `HistoryIndexLock:{${project_id}}`; } - } - }, - history: { - host: process.env["REDIS_HOST"] || "localhost", - port: process.env['REDIS_PORT'] || 6379, - password: process.env["REDIS_PASSWORD"] || "", - key_schema: { - uncompressedHistoryOps({doc_id}) { return `UncompressedHistoryOps:{${doc_id}}`; }, - docsWithHistoryOps({project_id}) { return `DocsWithHistoryOps:{${project_id}}`; } - } - } - }, + internal: { + trackchanges: { + port: 3015, + host: process.env.LISTEN_ADDRESS || 'localhost' + } + }, + apis: { + documentupdater: { + url: `http://${process.env.DOCUMENT_UPDATER_HOST || + process.env.DOCUPDATER_HOST || + 'localhost'}:3003` + }, + docstore: { + url: `http://${process.env.DOCSTORE_HOST || 'localhost'}:3016` + }, + web: { + url: `http://${process.env.WEB_API_HOST || + process.env.WEB_HOST || + 'localhost'}:${process.env.WEB_API_PORT || + process.env.WEB_PORT || + 3000}`, + user: process.env.WEB_API_USER || 'sharelatex', + pass: process.env.WEB_API_PASSWORD || 'password' + } + }, + redis: { + lock: { + host: process.env.REDIS_HOST || 'localhost', + port: process.env.REDIS_PORT || 6379, + password: process.env.REDIS_PASSWORD || '', + key_schema: { + historyLock({ doc_id }) { + return `HistoryLock:{${doc_id}}` + }, + historyIndexLock({ project_id }) { + return `HistoryIndexLock:{${project_id}}` + } + } + }, + history: { + host: process.env.REDIS_HOST || 'localhost', + port: process.env.REDIS_PORT || 6379, + password: process.env.REDIS_PASSWORD || '', + key_schema: { + uncompressedHistoryOps({ doc_id }) { + return `UncompressedHistoryOps:{${doc_id}}` + }, + docsWithHistoryOps({ project_id }) { + return `DocsWithHistoryOps:{${project_id}}` + } + } + } + }, - trackchanges: { - s3: { - key: process.env['AWS_ACCESS_KEY_ID'], - secret: process.env['AWS_SECRET_ACCESS_KEY'], - endpoint: process.env['AWS_S3_ENDPOINT'], - pathStyle: process.env['AWS_S3_PATH_STYLE'] === 'true' - }, - stores: { - doc_history: process.env['AWS_BUCKET'] - }, - continueOnError: process.env['TRACK_CHANGES_CONTINUE_ON_ERROR'] || false - }, - - path: { - dumpFolder: Path.join(TMP_DIR, "dumpFolder") - }, + trackchanges: { + s3: { + key: process.env.AWS_ACCESS_KEY_ID, + secret: process.env.AWS_SECRET_ACCESS_KEY, + endpoint: process.env.AWS_S3_ENDPOINT, + pathStyle: process.env.AWS_S3_PATH_STYLE === 'true' + }, + stores: { + doc_history: process.env.AWS_BUCKET + }, + continueOnError: process.env.TRACK_CHANGES_CONTINUE_ON_ERROR || false + }, - sentry: { - dsn: process.env.SENTRY_DSN - } -}; + path: { + dumpFolder: Path.join(TMP_DIR, 'dumpFolder') + }, + + sentry: { + dsn: process.env.SENTRY_DSN + } +} From 61fa73b1474dfe6fc75ef32471d04e06f55d68ad Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 17 Feb 2020 18:36:21 +0100 Subject: [PATCH 483/549] Uninstall coffee-script --- services/track-changes/package-lock.json | 6 ------ services/track-changes/package.json | 1 - 2 files changed, 7 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 0825432037..898cc6ffff 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1080,12 +1080,6 @@ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz", "integrity": "sha1-1d7/KlIHF7yYMTl5tocwmy02jik=" }, - "coffee-script": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.4.tgz", - "integrity": "sha1-/hvO2X/h+zknuZjytFYW4GWL4f8=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 56007ef786..b11785b87c 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -43,7 +43,6 @@ "babel-eslint": "^10.0.3", "bunyan": "~2.0.2", "chai": "~4.1.1", - "coffee-script": "^1.7.1", "eslint": "^6.6.0", "eslint-config-prettier": "^6.10.0", "eslint-config-standard": "^14.1.0", From 705ee285c11153afeaafb2d68f0c4f370486953b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2020 11:38:33 +0000 Subject: [PATCH 484/549] Bump request from 2.33.0 to 2.88.2 Bumps [request](https://github.com/request/request) from 2.33.0 to 2.88.2. - [Release notes](https://github.com/request/request/releases) - [Changelog](https://github.com/request/request/blob/master/CHANGELOG.md) - [Commits](https://github.com/request/request/commits) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 201 ++++++++--------------- services/track-changes/package.json | 2 +- 2 files changed, 72 insertions(+), 131 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 898cc6ffff..365bb38a6f 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -693,17 +693,10 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", "integrity": "sha1-yWVekzHgq81YjSp8rX6ZVvZnAfo=" }, - "asn1": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", - "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", - "optional": true - }, "assert-plus": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", - "optional": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assertion-error": { "version": "1.1.0", @@ -759,10 +752,9 @@ } }, "aws-sign2": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", - "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", - "optional": true + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", @@ -851,15 +843,6 @@ "integrity": "sha1-tcCeF8rNET0Rt7s+04TMASmU2Gs=", "dev": true }, - "boom": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", - "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", - "optional": true, - "requires": { - "hoek": "0.9.x" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1096,12 +1079,11 @@ "dev": true }, "combined-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", - "optional": true, + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { - "delayed-stream": "0.0.5" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -1196,21 +1178,6 @@ "which": "^1.2.9" } }, - "cryptiles": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", - "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", - "optional": true, - "requires": { - "boom": "0.4.x" - } - }, - "ctype": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", - "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", - "optional": true - }, "damerau-levenshtein": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", @@ -1276,10 +1243,9 @@ "integrity": "sha1-7+6/uPVFV5yzlrOnIkQ+yW0UxQ4=" }, "delayed-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", - "optional": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "denque": { "version": "1.4.1", @@ -2144,27 +2110,18 @@ } }, "forever-agent": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", - "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", - "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", - "optional": true, + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { - "async": "~0.9.0", - "combined-stream": "~0.0.4", - "mime": "~1.2.11" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "optional": true - } + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" } }, "formatio": { @@ -2399,18 +2356,6 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, - "hawk": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", - "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", - "optional": true, - "requires": { - "boom": "0.4.x", - "cryptiles": "0.2.x", - "hoek": "0.9.x", - "sntp": "0.2.x" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -2427,12 +2372,6 @@ "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz", "integrity": "sha1-jhzkvvNqdPfVcjw/swkMKGAHczg=" }, - "hoek": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", - "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", - "optional": true - }, "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", @@ -2440,14 +2379,13 @@ "dev": true }, "http-signature": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", - "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", - "optional": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "asn1": "0.1.11", - "assert-plus": "^0.1.5", - "ctype": "0.5.3" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "https-proxy-agent": { @@ -3540,10 +3478,9 @@ } }, "oauth-sign": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", - "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", - "optional": true + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", @@ -4721,28 +4658,41 @@ "dev": true }, "request": { - "version": "2.33.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.33.0.tgz", - "integrity": "sha1-UWeHgTFyYHDsYzdS6iMKI3ncZf8=", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { - "aws-sign2": "~0.5.0", - "forever-agent": "~0.5.0", - "form-data": "~0.1.0", - "hawk": "~1.0.0", - "http-signature": "~0.10.0", - "json-stringify-safe": "~5.0.0", - "mime": "~1.2.9", - "node-uuid": "~1.4.0", - "oauth-sign": "~0.3.0", - "qs": "~0.6.0", - "tough-cookie": ">=0.12.0", - "tunnel-agent": "~0.3.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "dependencies": { - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -5187,15 +5137,6 @@ } } }, - "sntp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", - "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", - "optional": true, - "requires": { - "hoek": "0.9.x" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5546,8 +5487,7 @@ "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha1-zZ+yoKodWhK0c72fuW+j3P9lreI=", - "optional": true, + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -5556,8 +5496,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", - "optional": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } }, @@ -5568,10 +5507,12 @@ "dev": true }, "tunnel-agent": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", - "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=", - "optional": true + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } }, "tweetnacl": { "version": "0.14.5", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b11785b87c..9c893297e4 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -33,7 +33,7 @@ "mongojs": "2.4.0", "redis": "~0.10.1", "redis-sharelatex": "^1.0.8", - "request": "~2.33.0", + "request": "~2.88.2", "requestretry": "^1.12.0", "s3-streams": "^0.3.0", "settings-sharelatex": "^1.1.0", From 622ab898d92416c00239d8cec57d65a55051c9eb Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Wed, 26 Feb 2020 12:25:13 +0000 Subject: [PATCH 485/549] upgrade requestretry to 4.1 --- services/track-changes/package-lock.json | 130 +---------------------- services/track-changes/package.json | 2 +- 2 files changed, 6 insertions(+), 126 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 365bb38a6f..f840b4a9e6 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -4697,133 +4697,13 @@ } }, "requestretry": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "integrity": "sha1-IT7BAG7rdQ6LjOVBdig9FajVXZQ=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-4.1.0.tgz", + "integrity": "sha512-q3IT2vz5vkcMT6xgwB/BWzsmnu7N/27l9fW86U48gt9Mwrce5rSEyFvpAW7Il1/B78/NBUlYBvcCY1RzWUWy7w==", "requires": { - "extend": "^3.0.0", - "lodash": "^4.15.0", - "request": "^2.74.0", + "extend": "^3.0.2", + "lodash": "^4.17.10", "when": "^3.7.7" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha1-LR0kMXr7ir6V1tLAsHtXgTU52Cg=", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" - } } }, "require-directory": { diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 9c893297e4..6dd106be61 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -34,7 +34,7 @@ "redis": "~0.10.1", "redis-sharelatex": "^1.0.8", "request": "~2.88.2", - "requestretry": "^1.12.0", + "requestretry": "^4.1.0", "s3-streams": "^0.3.0", "settings-sharelatex": "^1.1.0", "underscore": "~1.7.0" From 3d10196ebd19cb276c886142d5ebc40f77137ae6 Mon Sep 17 00:00:00 2001 From: mserranom Date: Fri, 20 Mar 2020 17:31:01 +0100 Subject: [PATCH 486/549] updated build-scripts and sharelatex-xxx modules --- services/track-changes/.eslintrc | 1 - services/track-changes/.prettierrc | 1 - services/track-changes/Dockerfile | 1 - services/track-changes/Makefile | 1 - services/track-changes/buildscript.txt | 10 +- services/track-changes/docker-compose.ci.yml | 1 - services/track-changes/docker-compose.yml | 1 - services/track-changes/package-lock.json | 1566 ++++++++++++++---- services/track-changes/package.json | 14 +- 9 files changed, 1210 insertions(+), 386 deletions(-) diff --git a/services/track-changes/.eslintrc b/services/track-changes/.eslintrc index 42a4b5cace..2e945d6ffb 100644 --- a/services/track-changes/.eslintrc +++ b/services/track-changes/.eslintrc @@ -1,7 +1,6 @@ // this file was auto-generated, do not edit it directly. // instead run bin/update_build_scripts from // https://github.com/sharelatex/sharelatex-dev-environment -// Version: 1.3.5 { "extends": [ "standard", diff --git a/services/track-changes/.prettierrc b/services/track-changes/.prettierrc index 5845b82113..24f9ec526f 100644 --- a/services/track-changes/.prettierrc +++ b/services/track-changes/.prettierrc @@ -1,7 +1,6 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.5 { "semi": false, "singleQuote": true diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 2d8b097e26..c014691791 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -1,7 +1,6 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.5 FROM node:10.19.0 as base diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index 73702fc5b1..e1b4843fea 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -1,7 +1,6 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.5 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index d68e80b307..977522bfba 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,10 +1,10 @@ track-changes ---public-repo=True ---language=es ---env-add=AWS_BUCKET=bucket ---node-version=10.19.0 --acceptance-creds=None --dependencies=mongo,redis,s3 --docker-repos=gcr.io/overleaf-ops +--env-add=AWS_BUCKET=bucket --env-pass-through= ---script-version=1.3.5 +--language=es +--node-version=10.19.0 +--public-repo=True +--script-version=2.0.0 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index e9c3f7ecbf..4e41056770 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -1,7 +1,6 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.5 version: "2.3" diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 300d7b45ec..88bd224842 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -1,7 +1,6 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.3.5 version: "2.3" diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index f840b4a9e6..cad1751959 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -150,27 +150,25 @@ } }, "@google-cloud/common": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", - "integrity": "sha1-ajLDQBcs6j22Z00ODjTnh0CgBz8=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz", + "integrity": "sha512-zWFjBS35eI9leAHhjfeOYlK5Plcuj/77EzstnrJIZbKgF/nkqjcQuGiMCpzCwOfPyUbz8ZaEOYgbHa759AKbjg==", "requires": { - "@google-cloud/projectify": "^0.3.3", - "@google-cloud/promisify": "^0.4.0", - "@types/request": "^2.48.1", + "@google-cloud/projectify": "^1.0.0", + "@google-cloud/promisify": "^1.0.0", "arrify": "^2.0.0", "duplexify": "^3.6.0", "ent": "^2.2.0", "extend": "^3.0.2", - "google-auth-library": "^3.1.1", - "pify": "^4.0.1", + "google-auth-library": "^5.5.0", "retry-request": "^4.0.0", - "teeny-request": "^3.11.3" + "teeny-request": "^6.0.0" } }, "@google-cloud/debug-agent": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.2.0.tgz", - "integrity": "sha1-2qdjWhaYpWY31dxXzhED536uKdM=", + "integrity": "sha512-fP87kYbS6aeDna08BivwQ1J260mwJGchRi99XdWCgqbRwuFac8ul0OT5i2wEeDSc5QaDX8ZuWQQ0igZvh1rTyQ==", "requires": { "@google-cloud/common": "^0.32.0", "@sindresorhus/is": "^0.15.0", @@ -188,22 +186,242 @@ "split": "^1.0.0" }, "dependencies": { + "@google-cloud/common": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", + "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", + "requires": { + "@google-cloud/projectify": "^0.3.3", + "@google-cloud/promisify": "^0.4.0", + "@types/request": "^2.48.1", + "arrify": "^2.0.0", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^3.1.1", + "pify": "^4.0.1", + "retry-request": "^4.0.0", + "teeny-request": "^3.11.3" + } + }, + "@google-cloud/projectify": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", + "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" + }, + "@google-cloud/promisify": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", + "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, "coffeescript": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.4.1.tgz", - "integrity": "sha1-gV/TN98KNNSedKmKbr6pw+eTD3A=" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz", + "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==" + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "gaxios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", + "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", + "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", + "requires": { + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" + } + }, + "google-auth-library": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", + "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", + "requires": { + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^1.2.1", + "gcp-metadata": "^1.0.0", + "gtoken": "^2.3.2", + "https-proxy-agent": "^2.2.1", + "jws": "^3.1.5", + "lru-cache": "^5.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "google-p12-pem": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", + "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", + "requires": { + "node-forge": "^0.8.0", + "pify": "^4.0.0" + } + }, + "gtoken": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", + "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", + "requires": { + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "node-forge": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", + "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" }, "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha1-U/U9qbMLIQPNTxXqs6GOy8shDJs=" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "teeny-request": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", + "requires": { + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0", + "uuid": "^3.3.2" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, + "@google-cloud/logging": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-7.3.0.tgz", + "integrity": "sha512-xTW1V4MKpYC0mjSugyuiyUoZ9g6A42IhrrO3z7Tt3SmAb2IRj2Gf4RLoguKKncs340ooZFXrrVN/++t2Aj5zgg==", + "requires": { + "@google-cloud/common": "^2.2.2", + "@google-cloud/paginator": "^2.0.0", + "@google-cloud/projectify": "^1.0.0", + "@google-cloud/promisify": "^1.0.0", + "@opencensus/propagation-stackdriver": "0.0.20", + "arrify": "^2.0.0", + "dot-prop": "^5.1.0", + "eventid": "^1.0.0", + "extend": "^3.0.2", + "gcp-metadata": "^3.1.0", + "google-auth-library": "^5.2.2", + "google-gax": "^1.11.0", + "is": "^3.3.0", + "on-finished": "^2.3.0", + "pumpify": "^2.0.0", + "snakecase-keys": "^3.0.0", + "stream-events": "^1.0.4", + "through2": "^3.0.0", + "type-fest": "^0.12.0" + }, + "dependencies": { + "type-fest": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", + "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==" + } + } + }, + "@google-cloud/logging-bunyan": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/logging-bunyan/-/logging-bunyan-2.0.3.tgz", + "integrity": "sha512-8n9MwsCRd4v8WZg17+d3m7qInud7lYTm5rpwXHY0/lzWEJYjeiztT09BiCYh56EEhHr+ynymJnzUDZKazkywlg==", + "requires": { + "@google-cloud/logging": "^7.0.0", + "google-auth-library": "^5.0.0" + } + }, + "@google-cloud/paginator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.3.tgz", + "integrity": "sha512-kp/pkb2p/p0d8/SKUu4mOq8+HGwF8NPzHWkj+VKrIPQPyMRw8deZtrO/OcSiy9C/7bpfU5Txah5ltUNfPkgEXg==", + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, "@google-cloud/profiler": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-0.2.3.tgz", - "integrity": "sha1-Fj3738Mwuug1X+RuHlvgZTV7H1w=", + "integrity": "sha512-rNvtrFtIebIxZEJ/O0t8n7HciZGIXBo8DvHxWqAmsCaeLvkTtsaL6HmPkwxrNQ1IhbYWAxF+E/DwCiHyhKmgTg==", "requires": { "@google-cloud/common": "^0.26.0", "@types/console-log-level": "^1.4.0", @@ -225,7 +443,7 @@ "@google-cloud/common": { "version": "0.26.2", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz", - "integrity": "sha1-nFTiRxqEqgMelaJIJJduCA8lVkU=", + "integrity": "sha512-xJ2M/q3MrUbnYZuFlpF01caAlEhAUoRn0NXp93Hn3pkFpfSOG8YfbKbpBAHvcKVbBOAKVIwPsleNtuyuabUwLQ==", "requires": { "@google-cloud/projectify": "^0.3.2", "@google-cloud/promisify": "^0.3.0", @@ -241,20 +459,52 @@ "through2": "^3.0.0" } }, + "@google-cloud/projectify": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", + "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" + }, "@google-cloud/promisify": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz", - "integrity": "sha1-9kHm2USo4KBe4MsQkd+mAIm+zbo=" + "integrity": "sha512-QzB0/IMvB0eFxFK7Eqh+bfC8NLv3E9ScjWQrPOk6GgfNroxcVITdTlT8NRsRrcp5+QQJVPLkRqKG0PUdaWXmHw==" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "gaxios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", + "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" + } + }, "gcp-metadata": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", - "integrity": "sha1-H510lfdGChRSZIHynhFZbdVj3SY=", + "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", "requires": { "gaxios": "^1.0.2", "json-bigint": "^0.3.0" @@ -263,7 +513,7 @@ "google-auth-library": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", - "integrity": "sha1-ejFdIDZ0Svavyth7IQ7mY4tA9Xs=", + "integrity": "sha512-FURxmo1hBVmcfLauuMRKOPYAPKht3dGuI2wjeJFalDUThO0HoYVjr4yxt5cgYSFm1dgUpmN9G/poa7ceTFAIiA==", "requires": { "axios": "^0.18.0", "gcp-metadata": "^0.7.0", @@ -277,7 +527,7 @@ "gcp-metadata": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", - "integrity": "sha1-bDXbtSvaMqQnu5yY9UI33dG1QG8=", + "integrity": "sha512-ffjC09amcDWjh3VZdkDngIo7WoluyC5Ag9PAYxZbmQLOLNI8lvPtoKTSCyU54j2gwy5roZh6sSMTfkY2ct7K3g==", "requires": { "axios": "^0.18.0", "extend": "^3.0.1", @@ -286,38 +536,104 @@ } } }, + "google-p12-pem": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", + "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", + "requires": { + "node-forge": "^0.8.0", + "pify": "^4.0.0" + } + }, + "gtoken": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", + "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", + "requires": { + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { "yallist": "^3.0.2" } }, - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "node-forge": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", + "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" + }, + "teeny-request": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", "requires": { - "readable-stream": "2 || 3" + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0", + "uuid": "^3.3.2" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, "@google-cloud/projectify": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", - "integrity": "sha1-vekQPVCyCj6jM334xng6dm5w1B0=" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", + "integrity": "sha512-ZdzQUN02eRsmTKfBj9FDL0KNDIFNjBn/d6tHQmA/+FImH5DO6ZV8E7FzxMgAUiVAUq41RFAkb25p1oHOZ8psfg==" }, "@google-cloud/promisify": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", - "integrity": "sha1-T7/PTYW7ai5MzwWqY9KxDWyarZs=" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", + "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==" }, "@google-cloud/trace-agent": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.6.1.tgz", - "integrity": "sha1-W+dEE5TQ6ldY8o25IqUAT/PwO+w=", + "integrity": "sha512-KDo85aPN4gSxJ7oEIOlKd7aGENZFXAM1kbIn1Ds+61gh/K1CQWSyepgJo3nUpAwH6D1ezDWV7Iaf8ueoITc8Uw==", "requires": { "@google-cloud/common": "^0.32.1", "builtin-modules": "^3.0.0", @@ -334,18 +650,259 @@ "uuid": "^3.0.1" }, "dependencies": { + "@google-cloud/common": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", + "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", + "requires": { + "@google-cloud/projectify": "^0.3.3", + "@google-cloud/promisify": "^0.4.0", + "@types/request": "^2.48.1", + "arrify": "^2.0.0", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^3.1.1", + "pify": "^4.0.1", + "retry-request": "^4.0.0", + "teeny-request": "^3.11.3" + } + }, + "@google-cloud/projectify": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", + "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" + }, + "@google-cloud/promisify": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", + "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "gaxios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", + "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", + "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", + "requires": { + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" + } + }, + "google-auth-library": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", + "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", + "requires": { + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^1.2.1", + "gcp-metadata": "^1.0.0", + "gtoken": "^2.3.2", + "https-proxy-agent": "^2.2.1", + "jws": "^3.1.5", + "lru-cache": "^5.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "google-p12-pem": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", + "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", + "requires": { + "node-forge": "^0.8.0", + "pify": "^4.0.0" + } + }, + "gtoken": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", + "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", + "requires": { + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" + } + }, + "https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "node-forge": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", + "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" + }, "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha1-U/U9qbMLIQPNTxXqs6GOy8shDJs=" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "teeny-request": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", + "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", + "requires": { + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } } } }, + "@grpc/grpc-js": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.6.18.tgz", + "integrity": "sha512-uAzv/tM8qpbf1vpx1xPMfcUMzbfdqJtdCYAqY/LsLeQQlnTb4vApylojr+wlCyr7bZeg3AFfHvtihnNOQQt/nA==", + "requires": { + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@grpc/proto-loader": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.3.tgz", + "integrity": "sha512-8qvUtGg77G2ZT2HqdqYoM/OY97gQd/0crSG34xNmZ4ZOsv3aQT/FQV9QfZPazTGna6MIoyUd+u6AxsoZjJ/VMQ==", + "requires": { + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" + } + }, + "@opencensus/core": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.20.tgz", + "integrity": "sha512-vqOuTd2yuMpKohp8TNNGUAPjWEGjlnGfB9Rh5e3DKqeyR94YgierNs4LbMqxKtsnwB8Dm2yoEtRuUgoe5vD9DA==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^6.0.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "@opencensus/propagation-stackdriver": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.20.tgz", + "integrity": "sha512-P8yuHSLtce+yb+2EZjtTVqG7DQ48laC+IuOWi3X9q78s1Gni5F9+hmbmyP6Nb61jb5BEvXQX1s2rtRI6bayUWA==", + "requires": { + "@opencensus/core": "^0.0.20", + "hex2dec": "^1.0.1", + "uuid": "^3.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "@overleaf/o-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-2.1.0.tgz", + "integrity": "sha512-Zd9sks9LrLw8ErHt/cXeWIkyxWAqNAvNGn7wIjLQJH6TTEEW835PWOhpch+hQwwWsTxWIx/JDj+IpZ3ouw925g==" + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -354,12 +911,12 @@ "@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU=" + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" }, "@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs=" + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" }, "@protobufjs/eventemitter": { "version": "1.1.0", @@ -403,7 +960,7 @@ "@sindresorhus/is": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.15.0.tgz", - "integrity": "sha1-lpFbqgXmpqHRN7rfSYTT/AWCC7Y=" + "integrity": "sha512-lu8BpxjAtRCAo5ifytTpCPCj99LF7o/2Myn+NXyNCBqvPYn7Pjd76AMmUB5l7XF1U6t0hcWrlEM5ESufW7wAeA==" }, "@sinonjs/commons": { "version": "1.4.0", @@ -441,11 +998,22 @@ "integrity": "sha1-jaXGUwkVZT86Hzj9XxAdjD+AecU=", "dev": true }, + "@tootallnate/once": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", + "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==" + }, "@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/console-log-level": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz", @@ -465,10 +1033,10 @@ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, - "@types/form-data": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", - "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "@types/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", "requires": { "@types/node": "*" } @@ -480,24 +1048,36 @@ "dev": true }, "@types/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", - "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "12.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz", - "integrity": "sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==" + "version": "10.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz", + "integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q==" }, "@types/request": { - "version": "2.48.1", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", - "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", + "version": "2.48.4", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.4.tgz", + "integrity": "sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==", "requires": { "@types/caseless": "*", - "@types/form-data": "*", "@types/node": "*", - "@types/tough-cookie": "*" + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } } }, "@types/semver": { @@ -506,9 +1086,9 @@ "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" }, "@types/tough-cookie": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", - "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==" + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.6.tgz", + "integrity": "sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ==" }, "@typescript-eslint/experimental-utils": { "version": "1.13.0", @@ -575,7 +1155,7 @@ "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha1-6vVNU7YrrkE46AnKIlyEOabvs5I=", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "requires": { "event-target-shim": "^5.0.0" } @@ -592,11 +1172,11 @@ "dev": true }, "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha1-gWXwHENgCbzK0LHRIvBe13Dvxu4=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", + "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", "requires": { - "es6-promisify": "^5.0.0" + "debug": "4" } }, "ajv": { @@ -611,12 +1191,20 @@ } }, "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } } }, "ansi-regex": { @@ -724,7 +1312,7 @@ "async-listener": { "version": "0.6.10", "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", - "integrity": "sha1-p8l6vlcLpgLXgic8DeYKUePhfLw=", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", "requires": { "semver": "^5.3.0", "shimmer": "^1.1.0" @@ -764,7 +1352,7 @@ "axios": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha1-/z8N4ue10YDnV62YAA8Qgbh7zqM=", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", "requires": { "follow-redirects": "1.5.10", "is-buffer": "^2.0.2" @@ -822,12 +1410,12 @@ "bignumber.js": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", - "integrity": "sha1-gMBIdZ2CaACAfEv9Uh5Q7bulel8=" + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "requires": { "file-uri-to-path": "1.0.0" } @@ -837,6 +1425,11 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "boolify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz", @@ -891,7 +1484,7 @@ "builtin-modules": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha1-qtl8FRMet2tltQ7yCOdYTNdqdIQ=" + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==" }, "bunyan": { "version": "2.0.2", @@ -1059,9 +1652,14 @@ } }, "cluster-key-slot": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz", - "integrity": "sha1-1d7/KlIHF7yYMTl5tocwmy02jik=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "coffee-script": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" }, "color-convert": { "version": "1.9.3", @@ -1127,7 +1725,7 @@ "console-log-level": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", - "integrity": "sha1-nFprue8e9lsFq6gwKLD/iUzfYwo=" + "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==" }, "contains-path": { "version": "0.1.0", @@ -1138,7 +1736,7 @@ "continuation-local-storage": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", - "integrity": "sha1-EfYT906RT+mzTJKtLSj+auHbf/s=", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", "requires": { "async-listener": "^0.6.0", "emitter-listener": "^1.1.1" @@ -1178,6 +1776,11 @@ "which": "^1.2.9" } }, + "d64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d64/-/d64-1.0.0.tgz", + "integrity": "sha1-QAKofoUMv8n52XBrYPymE6MzbpA=" + }, "damerau-levenshtein": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", @@ -1240,7 +1843,7 @@ "delay": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz", - "integrity": "sha1-7+6/uPVFV5yzlrOnIkQ+yW0UxQ4=" + "integrity": "sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA==" }, "delayed-stream": { "version": "1.0.0", @@ -1250,7 +1853,7 @@ "denque": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha1-Z0T/dkHBSMP4ppwwflEjXB9KN88=" + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" }, "diff": { "version": "3.3.1", @@ -1273,6 +1876,14 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "requires": { + "is-obj": "^2.0.0" + } + }, "dtrace-provider": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", @@ -1285,7 +1896,7 @@ "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -1310,15 +1921,20 @@ "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha1-rg8PothQRe8UqBfao86azQSJ5b8=", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "requires": { "safe-buffer": "^5.0.1" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, "emitter-listener": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", - "integrity": "sha1-VrFA6PaZI3Wz18ssqxzHQy2WMug=", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", "requires": { "shimmer": "^1.2.0" } @@ -1330,9 +1946,9 @@ "dev": true }, "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "requires": { "once": "^1.4.0" } @@ -1384,7 +2000,7 @@ "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=" + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "es6-promisify": { "version": "5.0.0", @@ -1401,9 +2017,9 @@ "dev": true }, "eslint": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.6.0.tgz", - "integrity": "sha512-PpEBq7b6qY/qrOmpYQ/jTMDYfuQMELR4g4WI1M/NaSDDD/bdcMb+dj4Hgks7p41kW2caXsPsEZAEAyAgjVVC0g==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -1421,7 +2037,7 @@ "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -1434,7 +2050,7 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", @@ -1446,9 +2062,9 @@ }, "dependencies": { "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -1463,6 +2079,15 @@ "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -1478,13 +2103,19 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "semver": { @@ -1718,12 +2349,24 @@ } }, "eslint-plugin-mocha": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.2.tgz", - "integrity": "sha512-oNhPzfkT6Q6CJ0HMVJ2KLxEWG97VWGTmuHOoRcDLE0U88ugUyFNV9wrT2XIt5cGtqc5W9k38m4xTN34L09KhBA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz", + "integrity": "sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==", "dev": true, "requires": { - "ramda": "^0.26.1" + "eslint-utils": "^2.0.0", + "ramda": "^0.27.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + } } }, "eslint-plugin-node": { @@ -1857,20 +2500,26 @@ "dev": true }, "espree": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", - "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", "dev": true, "requires": { - "acorn": "^7.1.0", - "acorn-jsx": "^5.1.0", + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", "eslint-visitor-keys": "^1.1.0" }, "dependencies": { "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "dev": true } } @@ -1914,7 +2563,16 @@ "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha1-XU0+vflYPWOlMzzi3rdICrKwV4k=" + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "eventid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventid/-/eventid-1.0.0.tgz", + "integrity": "sha512-4upSDsvpxhWPsmw4fsJCp0zj8S7I0qh1lCDTmZXP8V3TtryQKDI8CgQPN+e5JakbWwzaAX3lrdp2b3KSoMSUpw==", + "requires": { + "d64": "^1.0.0", + "uuid": "^3.0.1" + } }, "events": { "version": "1.1.1", @@ -1994,9 +2652,9 @@ "dev": true }, "fast-text-encoding": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", - "integrity": "sha1-PlzoKTQJz6pxd6cbnKhOGx5vJe8=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.1.tgz", + "integrity": "sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ==" }, "figures": { "version": "3.2.0", @@ -2019,7 +2677,7 @@ "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=" + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "find-up": { "version": "2.1.0", @@ -2089,7 +2747,7 @@ "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { "debug": "=3.1.0" }, @@ -2097,7 +2755,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" } @@ -2162,22 +2820,23 @@ "dev": true }, "gaxios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", - "integrity": "sha1-4Iw0/pPAqbZ6Ure556ZOZDX5ozk=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.2.tgz", + "integrity": "sha512-K/+py7UvKRDaEwEKlLiRKrFr+wjGjsMz5qH7Vs549QJS7cpSCOT/BbWL7pzqECflc46FcNPipjSfB+V1m8PAhw==", "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", - "https-proxy-agent": "^2.2.1", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", "node-fetch": "^2.3.0" } }, "gcp-metadata": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", - "integrity": "sha1-UhJEAin6CZ/C98KlzcuVV16bLKY=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz", + "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==", "requires": { - "gaxios": "^1.0.2", + "gaxios": "^2.1.0", "json-bigint": "^0.3.0" } }, @@ -2239,38 +2898,66 @@ "dev": true }, "google-auth-library": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", - "integrity": "sha1-/y+IzVzSEYpXvT1a08CTyIN/w1A=", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", "requires": { + "arrify": "^2.0.0", "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", "fast-text-encoding": "^1.0.0", - "gaxios": "^1.2.1", - "gcp-metadata": "^1.0.0", - "gtoken": "^2.3.2", - "https-proxy-agent": "^2.2.1", - "jws": "^3.1.5", - "lru-cache": "^5.0.0", - "semver": "^5.5.0" + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" }, "dependencies": { "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { "yallist": "^3.0.2" } } } }, - "google-p12-pem": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", - "integrity": "sha1-t3+4M6Lrn388aJ4uVPCVJ293dgU=", + "google-gax": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.1.tgz", + "integrity": "sha512-1T1PwSZWnbdRusA+NCZMSe56iU6swGvuZuy54eYl9vEHiRXTLYbQmUkWY2CqgYD9Fd/T4WBkUl22+rZG80unyw==", "requires": { - "node-forge": "^0.8.0", - "pify": "^4.0.0" + "@grpc/grpc-js": "^0.6.18", + "@grpc/proto-loader": "^0.5.1", + "@types/fs-extra": "^8.0.1", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^3.6.0", + "google-auth-library": "^5.0.0", + "is-stream-ended": "^0.1.4", + "lodash.at": "^4.6.0", + "lodash.has": "^4.5.2", + "node-fetch": "^2.6.0", + "protobufjs": "^6.8.9", + "retry-request": "^4.0.0", + "semver": "^6.0.0", + "walkdir": "^0.4.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "google-p12-pem": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz", + "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==", + "requires": { + "node-forge": "^0.9.0" } }, "graceful-fs": { @@ -2286,21 +2973,20 @@ "dev": true }, "gtoken": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "integrity": "sha1-in/hVcXODEtxyIbPsoKpBg2UpkE=", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", + "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", "requires": { - "gaxios": "^1.0.4", - "google-p12-pem": "^1.0.0", - "jws": "^3.1.5", - "mime": "^2.2.0", - "pify": "^4.0.0" + "gaxios": "^2.1.0", + "google-p12-pem": "^2.0.0", + "jws": "^4.0.0", + "mime": "^2.2.0" }, "dependencies": { "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha1-vXuRE1/GsBzePpuuM9ZZtj2IV+U=" + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" } } }, @@ -2370,7 +3056,7 @@ "hex2dec": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/hex2dec/-/hex2dec-1.1.2.tgz", - "integrity": "sha1-jhzkvvNqdPfVcjw/swkMKGAHczg=" + "integrity": "sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA==" }, "hosted-git-info": { "version": "2.8.5", @@ -2378,6 +3064,16 @@ "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", "dev": true }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -2389,22 +3085,12 @@ } }, "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha1-UVUpcPoE1yPgTFbQQXjD+SWSu8A=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", - "requires": { - "ms": "^2.1.1" - } - } + "agent-base": "6", + "debug": "4" } }, "iconv-lite": { @@ -2472,31 +3158,99 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "inquirer": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", - "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", - "chalk": "^2.4.2", + "chalk": "^3.0.0", "cli-cursor": "^3.1.0", "cli-width": "^2.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.15", "mute-stream": "0.0.8", - "run-async": "^2.2.0", + "run-async": "^2.4.0", "rxjs": "^6.5.3", "string-width": "^4.1.0", - "strip-ansi": "^5.1.0", + "strip-ansi": "^6.0.0", "through": "^2.3.6" }, "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true + }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, @@ -2512,35 +3266,25 @@ } }, "ioredis": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.9.5.tgz", - "integrity": "sha1-C7ugqfquk0hdMjHhuBnS1OIycdk=", + "version": "4.14.4", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.14.4.tgz", + "integrity": "sha512-9JAQi9Z0OioGNFKIgDKv6CpjNHaUX3CbkF02jDpPMe3+6v+TI47Ky4XvByeDV0wb7yrZyKCilW/6STIZs8GX3Q==", "requires": { - "cluster-key-slot": "^1.0.6", - "debug": "^3.1.0", + "cluster-key-slot": "^1.1.0", + "debug": "^4.1.1", "denque": "^1.1.0", "lodash.defaults": "^4.2.0", "lodash.flatten": "^4.4.0", - "redis-commands": "1.4.0", + "redis-commands": "1.5.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.0.1" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", - "requires": { - "ms": "^2.1.1" - } - } } }, "is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha1-Yc/23TxBk9uUo9YlggcrROVkXXk=" + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==" }, "is-arrayish": { "version": "0.2.1", @@ -2549,9 +3293,9 @@ "dev": true }, "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha1-Ts8/z3ScvR5HJonhCaxmJhol5yU=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, "is-callable": { "version": "1.1.5", @@ -2586,6 +3330,11 @@ "is-extglob": "^2.1.1" } }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -2601,6 +3350,16 @@ "has": "^1.0.3" } }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==" + }, "is-string": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", @@ -2738,9 +3497,9 @@ "dev": true }, "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha1-dDwymFy56YZVUw1TZBtmyGRbA5o=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -2748,11 +3507,11 @@ } }, "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha1-ABCZ82OUaMlBQADpmZX6UvtHgwQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "requires": { - "jwa": "^1.4.1", + "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, @@ -2811,6 +3570,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" }, + "lodash.at": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", + "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -2821,6 +3590,11 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, + "lodash.has": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", + "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -2844,26 +3618,24 @@ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", "dev": true }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" + }, "logger-sharelatex": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.7.0.tgz", - "integrity": "sha1-XuMje84im1rITZ7SLoXa6eI3/HQ=", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.9.0.tgz", + "integrity": "sha512-yVTuha82047IiMOQLgQHCZGKkJo6I2+2KtiFKpgkIooR2yZaoTEvAeoMwBesSDSpGUpvUJ/+9UI+PmRyc+PQKQ==", "requires": { + "@google-cloud/logging-bunyan": "^2.0.0", + "@overleaf/o-error": "^2.0.0", "bunyan": "1.8.12", "raven": "1.1.3", - "request": "2.88.0" + "request": "2.88.0", + "yn": "^3.1.1" }, "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, "bunyan": { "version": "1.8.12", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", @@ -2875,49 +3647,6 @@ "safe-json-stringify": "~1" } }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -2926,12 +3655,12 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -2958,24 +3687,16 @@ "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha1-U/Nto/R3g7CSWvoG/587FlKA94E=", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -3046,7 +3767,7 @@ "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=" + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "loose-envify": { "version": "1.4.0", @@ -3097,8 +3818,7 @@ "map-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", - "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", - "dev": true + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==" }, "memorystream": { "version": "0.3.1", @@ -3140,9 +3860,9 @@ "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=" }, "metrics-sharelatex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.2.0.tgz", - "integrity": "sha1-RM9oy9FuUQYgfrZ+PvkAhaQWwqk=", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.6.1.tgz", + "integrity": "sha512-Fkzl3MDjhsFuCl2i3f0bAqyC9lp4MAt6/hF9HxI6CV5r42kCsbqkE81G61HySpna7hMXZ0cE8T+w+91nAMwCQQ==", "requires": { "@google-cloud/debug-agent": "^3.0.0", "@google-cloud/profiler": "^0.2.3", @@ -3150,14 +3870,10 @@ "coffee-script": "1.6.0", "lynx": "~0.1.1", "prom-client": "^11.1.3", - "underscore": "~1.6.0" + "underscore": "~1.6.0", + "yn": "^3.1.1" }, "dependencies": { - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" - }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", @@ -3458,12 +4174,12 @@ "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha1-5jNFY4bUqlWGP2dqerDaqP3ssP0=" + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, "node-forge": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.4.tgz", - "integrity": "sha1-1nOGYrZhvhnicR7wGqOxghLxMDA=" + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", + "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" }, "normalize-package-data": { "version": "2.5.0", @@ -3548,6 +4264,14 @@ "has": "^1.0.3" } }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3634,9 +4358,9 @@ } }, "parse-duration": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.1.tgz", - "integrity": "sha1-ExFN3JiRwezSgANiRFVN5DZHoiY=" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.2.tgz", + "integrity": "sha512-0qfMZyjOUFBeEIvJ5EayfXJqaEXxQ+Oj2b7tWJM3hvEXvXsYCk05EDVI23oYnEw2NaFYUWdABEVPBvBMh8L/pA==" }, "parse-json": { "version": "2.2.0", @@ -3655,7 +4379,7 @@ "parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha1-NIVlp1PUOR+lJAKZVrFyy3dTCX0=" + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" }, "path-exists": { "version": "3.0.0", @@ -3738,7 +4462,7 @@ "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=" + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, "pkg-dir": { "version": "2.0.0", @@ -4397,7 +5121,7 @@ "pretty-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz", - "integrity": "sha1-Mbr0G5T9AiJwmKqgO9YmCOsNbpI=", + "integrity": "sha512-qG66ahoLCwpLXD09ZPHSCbUWYTqdosB7SMP4OffgTgL2PBKXMuUsrk5Bwg8q4qPkjTXsKBMr+YK3Ltd/6F9s/Q==", "requires": { "parse-ms": "^2.0.0" } @@ -4414,9 +5138,9 @@ "dev": true }, "prom-client": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.1.tgz", - "integrity": "sha1-FcZsrN7EUwELz68EEJvMNOa92pw=", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz", + "integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==", "requires": { "tdigest": "^0.1.1" } @@ -4433,9 +5157,9 @@ } }, "protobufjs": { - "version": "6.8.8", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha1-yLTxKC/XqQ5vWxCe0RyEr4KQjnw=", + "version": "6.8.9", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.9.tgz", + "integrity": "sha512-j2JlRdUeL/f4Z6x4aU4gj9I2LECglC+5qR2TrWb193Tla1qfdaNQTZ8I27Pt7K0Ajmvjjpft7O3KWTGciz4gpw==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -4450,13 +5174,6 @@ "@types/long": "^4.0.0", "@types/node": "^10.1.0", "long": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "10.14.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz", - "integrity": "sha1-Lo1ngDnSeUPOU6GRM4YTMif9kGY=" - } } }, "psl": { @@ -4464,6 +5181,48 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", "integrity": "sha1-6aqG0BAbWxBcvpOsa3hM1UcnYYQ=" }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "requires": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + }, + "dependencies": { + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -4491,9 +5250,9 @@ "dev": true }, "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz", + "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==", "dev": true }, "range-parser": { @@ -4572,9 +5331,9 @@ "integrity": "sha1-iSf+IRDuOWF7zz/Te4nY4SORG7Y=" }, "redis-commands": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", - "integrity": "sha1-UvnPmRU+/M5WqPhq+Ya9BOmIYC8=" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" }, "redis-errors": { "version": "1.2.0", @@ -4606,23 +5365,23 @@ } }, "redis-sharelatex": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.8.tgz", - "integrity": "sha1-6cDkGpNCNcsWLjQMC9IxCbZIe70=", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.11.tgz", + "integrity": "sha512-rKXPVLmFC9ycpRc5e4rULOwi9DB0LqRcWEiUxQuJNSVgcqCxpGqVw+zwivo+grk3G2tGpduh3/8y+4KVHWOntw==", "requires": { "async": "^2.5.0", "coffee-script": "1.8.0", - "ioredis": "~4.9.1", + "ioredis": "~4.14.1", "redis-sentinel": "0.1.1", "underscore": "1.7.0" }, "dependencies": { "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha1-GDMOp+bjE4h/XS8qkEusb+TdU4E=", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "requires": { - "lodash": "^4.17.11" + "lodash": "^4.17.14" } }, "coffee-script": { @@ -4632,6 +5391,11 @@ "requires": { "mkdirp": "~0.3.5" } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" } } }, @@ -4713,13 +5477,23 @@ "dev": true }, "require-in-the-middle": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.0.tgz", - "integrity": "sha1-PHUoik7EgM30S8d950T4q+WFQFs=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.1.tgz", + "integrity": "sha512-EfkM2zANyGkrfIExsECMeNn/uzjvHrE9h36yLXSavmrDiH4tgDNvltAmEKnt4PNLbqKPHZz+uszW2wTKrLUX0w==", "requires": { "debug": "^4.1.1", "module-details-from-path": "^1.0.3", - "resolve": "^1.10.0" + "resolve": "^1.12.0" + }, + "dependencies": { + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "requires": { + "path-parse": "^1.0.6" + } + } } }, "require-like": { @@ -4753,6 +5527,7 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", "integrity": "sha1-QBSHC6KWF2uGND1Qtg87UGCc4jI=", + "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -4775,14 +5550,15 @@ "retry-axios": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", - "integrity": "sha1-V1fID1hbTMTEmGqi/9R6YMbTXhM=" + "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==" }, "retry-request": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", - "integrity": "sha1-XDZhZiebPhDp16oTJ0RnoFy2kpA=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz", + "integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==", "requires": { - "through2": "^2.0.0" + "debug": "^4.1.1", + "through2": "^3.0.1" } }, "rimraf": { @@ -4837,24 +5613,24 @@ } }, "s3-streams": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.3.0.tgz", - "integrity": "sha1-Y/Ax6+FRg/CfdlZMYW8cCc7PMFA=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.4.0.tgz", + "integrity": "sha512-DtZ7w3A0EorzHdhh00U3p7O2c2hv2w/i+A1JATAJZubp+fnwlU8MiejJibAzMLhhCRv+UsfimSGoivWt2Y4JsQ==", "requires": { - "bluebird": "^2.9.27", - "lodash": "^3.9.3", - "readable-stream": "^2.0.0" + "bluebird": "^3.5.3", + "lodash": "^4.17.11", + "readable-stream": "^3.1.1" }, "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } } } }, @@ -4958,7 +5734,7 @@ "shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha1-YQhZ994ye1h+/r9QH7QxF/mv8zc=" + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, "side-channel": { "version": "1.0.2", @@ -5017,10 +5793,19 @@ } } }, + "snakecase-keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.1.2.tgz", + "integrity": "sha512-NrzHj8ctStnd1LYx3+L4buS7yildFum7WAbQQxkhPCNi3Qeqv7hoBne2c9n++HWxDG9Nv23pNEyyLCITZTv24Q==", + "requires": { + "map-obj": "^4.0.0", + "to-snake-case": "^1.0.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "spdx-correct": { "version": "3.1.0", @@ -5057,7 +5842,7 @@ "split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha1-YFvZvjA6pZ+zX5Ip++oN3snqB9k=", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "requires": { "through": "2" } @@ -5107,17 +5892,25 @@ "standard-as-callback": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", - "integrity": "sha1-7YuyVkjhWDF1m2Ajvbh+a2CzgSY=" + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" }, "statsd-parser": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" }, + "stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "requires": { + "stubs": "^3.0.0" + } + }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, "string-width": { "version": "4.2.0", @@ -5212,6 +6005,11 @@ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "dev": true }, + "stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" + }, "supports-color": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", @@ -5291,19 +6089,21 @@ } }, "teeny-request": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", - "integrity": "sha1-M1xin3ZF5dZZk2LfLzIwxMvCOlU=", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-6.0.3.tgz", + "integrity": "sha512-TZG/dfd2r6yeji19es1cUIwAlVD8y+/svB1kAC2Y0bjEyysrfbO8EZvJBRwIE6WkwmUoB7uvWLwTIhJbMXZ1Dw==", "requires": { - "https-proxy-agent": "^2.2.1", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", "node-fetch": "^2.2.0", - "uuid": "^3.3.2" + "stream-events": "^1.0.5", + "uuid": "^7.0.0" }, "dependencies": { "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE=" + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", + "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==" } } }, @@ -5325,12 +6125,11 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2 || 3" } }, "thunky": { @@ -5364,6 +6163,27 @@ "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", "integrity": "sha1-NZbsdhOsmtO5ioncua77pWnNJ+s=" }, + "to-no-case": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + }, + "to-snake-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", + "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=", + "requires": { + "to-space-case": "^1.0.0" + } + }, + "to-space-case": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "requires": { + "to-no-case": "^1.0.0" + } + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -5571,6 +6391,11 @@ } } }, + "walkdir": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", + "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==" + }, "when": { "version": "3.7.8", "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", @@ -5684,9 +6509,9 @@ "dev": true }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha1-tLBJ4xS+VF486AIjbWzSLNkcPek=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { "version": "13.3.0", @@ -5768,6 +6593,11 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" } } } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 6dd106be61..211e53f4d4 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -14,8 +14,8 @@ "test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP", "nodemon": "nodemon --config nodemon.json", "lint": "node_modules/.bin/eslint .", - "format": "node_modules/.bin/prettier-eslint '**/*.js' --list-different", - "format:fix": "node_modules/.bin/prettier-eslint '**/*.js' --write" + "format": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --list-different", + "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" }, "dependencies": { "JSONStream": "^1.0.4", @@ -27,12 +27,12 @@ "express": "3.3.5", "heap": "^0.2.6", "line-reader": "^0.2.4", - "logger-sharelatex": "^1.7.0", - "metrics-sharelatex": "^2.2.0", + "logger-sharelatex": "^1.9.0", + "metrics-sharelatex": "^2.6.1", "mongo-uri": "^0.1.2", "mongojs": "2.4.0", "redis": "~0.10.1", - "redis-sharelatex": "^1.0.8", + "redis-sharelatex": "^1.0.11", "request": "~2.88.2", "requestretry": "^4.1.0", "s3-streams": "^0.3.0", @@ -43,7 +43,7 @@ "babel-eslint": "^10.0.3", "bunyan": "~2.0.2", "chai": "~4.1.1", - "eslint": "^6.6.0", + "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", "eslint-config-standard": "^14.1.0", "eslint-config-standard-jsx": "^8.1.0", @@ -52,7 +52,7 @@ "eslint-plugin-chai-friendly": "^0.5.0", "eslint-plugin-import": "^2.20.1", "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-mocha": "^6.2.2", + "eslint-plugin-mocha": "^6.3.0", "eslint-plugin-node": "^11.0.0", "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-promise": "^4.2.1", From bcc1c9d4f9f3c1e8a2cb78c48c207ea71827e43e Mon Sep 17 00:00:00 2001 From: mserranom Date: Fri, 20 Mar 2020 17:32:23 +0100 Subject: [PATCH 487/549] npm audit fix --- services/track-changes/package-lock.json | 18 +++++++++--------- services/track-changes/package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index cad1751959..0b2faa9493 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1161,9 +1161,9 @@ } }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha1-fSWuBbuK0fm2mRCOEJTs14hK3B8=" + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" }, "acorn-jsx": { "version": "5.1.0", @@ -3566,9 +3566,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.at": { "version": "4.6.0", @@ -3807,9 +3807,9 @@ }, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true, "optional": true } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 211e53f4d4..57748923f4 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -35,7 +35,7 @@ "redis-sharelatex": "^1.0.11", "request": "~2.88.2", "requestretry": "^4.1.0", - "s3-streams": "^0.3.0", + "s3-streams": "^0.4.0", "settings-sharelatex": "^1.1.0", "underscore": "~1.7.0" }, From 592cae3698d75365a0e8218a4bb742f3e989fb85 Mon Sep 17 00:00:00 2001 From: mserranom Date: Fri, 20 Mar 2020 17:33:45 +0100 Subject: [PATCH 488/549] updated minor/patch dependencies --- services/track-changes/package-lock.json | 172 +++++++++++++---------- services/track-changes/package.json | 10 +- 2 files changed, 106 insertions(+), 76 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 0b2faa9493..6a9a990a99 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -14,23 +14,17 @@ } }, "@babel/generator": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", - "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.0.tgz", + "integrity": "sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw==", "dev": true, "requires": { - "@babel/types": "^7.8.3", + "@babel/types": "^7.9.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -68,6 +62,12 @@ "@babel/types": "^7.8.3" } }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "dev": true + }, "@babel/highlight": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", @@ -80,9 +80,9 @@ } }, "@babel/parser": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", - "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.0.tgz", + "integrity": "sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg==", "dev": true }, "@babel/runtime": { @@ -94,59 +94,61 @@ "regenerator-runtime": "^0.13.2" } }, + "@babel/runtime-corejs3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.0.tgz", + "integrity": "sha512-Fe3z3yVZNCUTaOFBAofwkEtFiYi7a7Gg2F5S1QX+mqP403i2iKJtyHJYEp/PV2ijUheT0PiKWbmXcqtwLhmBzg==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + } + } + }, "@babel/template": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", - "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", - "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", + "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", "dev": true, "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", + "@babel/generator": "^7.9.0", "@babel/helper-function-name": "^7.8.3", "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.4", - "@babel/types": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } } }, "@babel/types": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", - "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", "dev": true, "requires": { - "esutils": "^2.0.2", + "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } } }, "@google-cloud/common": { @@ -1289,7 +1291,7 @@ "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs=", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "ast-types-flow": { @@ -1365,15 +1367,15 @@ "dev": true }, "babel-eslint": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", - "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", "eslint-visitor-keys": "^1.0.0", "resolve": "^1.12.0" }, @@ -1538,17 +1540,17 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" + "pathval": "^1.1.0", + "type-detect": "^4.0.5" } }, "chalk": { @@ -1758,6 +1760,12 @@ "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", "dev": true }, + "core-js-pure": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz", + "integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1819,7 +1827,7 @@ "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -2431,9 +2439,9 @@ "dev": true }, "eslint-plugin-react": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.3.tgz", - "integrity": "sha512-Bt56LNHAQCoou88s8ViKRjMB2+36XRejCQ1VoLj716KI1MoE99HpTVvIThJ0rvFmG4E4Gsq+UgToEjn+j044Bg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz", + "integrity": "sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==", "dev": true, "requires": { "array-includes": "^3.1.1", @@ -2444,8 +2452,10 @@ "object.fromentries": "^2.0.2", "object.values": "^1.1.1", "prop-types": "^15.7.2", - "resolve": "^1.14.2", - "string.prototype.matchall": "^4.0.2" + "resolve": "^1.15.1", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.2", + "xregexp": "^4.3.0" }, "dependencies": { "doctrine": { @@ -2465,6 +2475,12 @@ "requires": { "path-parse": "^1.0.6" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -5285,9 +5301,9 @@ } }, "react-is": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", - "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, "read-pkg": { @@ -5396,6 +5412,11 @@ "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" } } }, @@ -6252,9 +6273,9 @@ "integrity": "sha1-EH+xVcgsETZiB5ftTIjPKwj2qrg=" }, "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", + "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==" }, "uri-js": { "version": "4.2.2", @@ -6497,6 +6518,15 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, + "xregexp": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz", + "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==", + "dev": true, + "requires": { + "@babel/runtime-corejs3": "^7.8.3" + } + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 57748923f4..ee69618a54 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -18,7 +18,7 @@ "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" }, "dependencies": { - "JSONStream": "^1.0.4", + "JSONStream": "^1.3.5", "async": "~0.2.10", "aws-sdk": "^2.102.0", "bson": "^0.4.20", @@ -37,12 +37,12 @@ "requestretry": "^4.1.0", "s3-streams": "^0.4.0", "settings-sharelatex": "^1.1.0", - "underscore": "~1.7.0" + "underscore": "~1.9.2" }, "devDependencies": { - "babel-eslint": "^10.0.3", + "babel-eslint": "^10.1.0", "bunyan": "~2.0.2", - "chai": "~4.1.1", + "chai": "~4.2.0", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", "eslint-config-standard": "^14.1.0", @@ -56,7 +56,7 @@ "eslint-plugin-node": "^11.0.0", "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-react": "^7.18.3", + "eslint-plugin-react": "^7.19.0", "eslint-plugin-standard": "^4.0.1", "memorystream": "0.3.1", "mocha": "^4.0.1", From 3f2bb309801839be575b1275c108ae4a75f40f90 Mon Sep 17 00:00:00 2001 From: mserranom Date: Fri, 20 Mar 2020 17:39:23 +0100 Subject: [PATCH 489/549] updated aws-sdk and mongojs --- services/track-changes/package-lock.json | 930 ++++++++++++++++++----- services/track-changes/package.json | 4 +- 2 files changed, 736 insertions(+), 198 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 6a9a990a99..57ce2c70d6 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -8,7 +8,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, "requires": { "@babel/highlight": "^7.8.3" } @@ -17,7 +16,6 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.0.tgz", "integrity": "sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw==", - "dev": true, "requires": { "@babel/types": "^7.9.0", "jsesc": "^2.5.1", @@ -28,8 +26,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -37,7 +34,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.8.3", "@babel/template": "^7.8.3", @@ -48,7 +44,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, "requires": { "@babel/types": "^7.8.3" } @@ -57,7 +52,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, "requires": { "@babel/types": "^7.8.3" } @@ -65,14 +59,12 @@ "@babel/helper-validator-identifier": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", - "dev": true + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==" }, "@babel/highlight": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", - "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -82,8 +74,7 @@ "@babel/parser": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg==", - "dev": true + "integrity": "sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg==" }, "@babel/runtime": { "version": "7.8.4", @@ -116,7 +107,6 @@ "version": "7.8.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, "requires": { "@babel/code-frame": "^7.8.3", "@babel/parser": "^7.8.6", @@ -127,7 +117,6 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", - "dev": true, "requires": { "@babel/code-frame": "^7.8.3", "@babel/generator": "^7.9.0", @@ -144,7 +133,6 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", @@ -1219,16 +1207,27 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -1326,19 +1325,26 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sdk": { - "version": "2.384.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.384.0.tgz", - "integrity": "sha1-yP3OfHLwaPbYsZi1PPM7bzXz6BE=", + "version": "2.642.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.642.0.tgz", + "integrity": "sha512-0ZNgL1HBXRVobFD9Z64RyQk50cNABDMU1GV4lYIAvao4urYqYJi2MEVQmq+7WyXyzkBWu3lAPNDiJ8WW7emTzg==", "requires": { "buffer": "4.9.1", "events": "1.1.1", - "ieee754": "1.1.8", + "ieee754": "1.1.13", "jmespath": "0.15.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", - "uuid": "3.1.0", + "uuid": "3.3.2", "xml2js": "0.4.19" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } } }, "aws-sign2": { @@ -1427,6 +1433,15 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" }, + "bl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", + "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1478,11 +1493,6 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" - }, "builtin-modules": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", @@ -1511,6 +1521,17 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", "integrity": "sha1-qtM+wU49wsp06OfUUfm6BTrU96A=" }, + "caching-transform": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", + "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "requires": { + "hasha": "^3.0.0", + "make-dir": "^2.0.0", + "package-hash": "^3.0.0", + "write-file-atomic": "^2.4.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1520,8 +1541,7 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "camelcase-keys": { "version": "6.1.2", @@ -1557,7 +1577,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1567,14 +1586,12 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -1621,7 +1638,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, "requires": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -1631,20 +1647,17 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -1667,7 +1680,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1675,8 +1687,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "combined-stream": { "version": "1.0.8", @@ -1700,6 +1711,11 @@ "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1744,6 +1760,14 @@ "emitter-listener": "^1.1.1" } }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, "cookie": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", @@ -1771,6 +1795,18 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cp-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", + "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^2.0.0", + "nested-error-stacks": "^2.0.0", + "pify": "^4.0.1", + "safe-buffer": "^5.0.1" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -1821,8 +1857,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "deep-eql": { "version": "3.0.1", @@ -1839,6 +1874,14 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "requires": { + "strip-bom": "^3.0.0" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1970,7 +2013,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -2005,6 +2047,11 @@ "is-symbol": "^1.0.2" } }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -2021,8 +2068,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "6.8.0", @@ -2543,8 +2589,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.1.0", @@ -2573,8 +2618,7 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "event-target-shim": { "version": "5.0.1", @@ -2695,6 +2739,51 @@ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + } + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -2783,6 +2872,40 @@ } } }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -2820,8 +2943,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", @@ -2859,8 +2981,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { "version": "2.0.0", @@ -2910,8 +3031,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "google-auth-library": { "version": "5.10.1", @@ -2979,8 +3099,7 @@ "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "growl": { "version": "1.10.3", @@ -3058,6 +3177,21 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "hasha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", + "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "requires": { + "is-stream": "^1.0.1" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + } + } + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -3077,8 +3211,12 @@ "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", - "dev": true + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" + }, + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==" }, "http-proxy-agent": { "version": "4.0.1", @@ -3119,9 +3257,9 @@ } }, "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "ignore": { "version": "4.0.6", @@ -3150,8 +3288,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "indent-string": { "version": "4.0.0", @@ -3305,8 +3442,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-buffer": { "version": "2.0.4", @@ -3404,14 +3540,123 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" + }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "istanbul-reports": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", + "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", + "requires": { + "html-escaper": "^2.0.0" + } + }, "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", @@ -3420,14 +3665,12 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3441,8 +3684,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "json-bigint": { "version": "0.3.0", @@ -3452,6 +3694,11 @@ "bignumber.js": "^7.0.0" } }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -3606,6 +3853,11 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" + }, "lodash.has": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", @@ -3813,6 +4065,15 @@ "statsd-parser": "~0.0.4" } }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "make-plural": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", @@ -3836,12 +4097,26 @@ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "dev": true }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "requires": { + "source-map": "^0.6.1" + } + }, "mersenne": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", @@ -4030,78 +4305,50 @@ "integrity": "sha1-FzrwFAMzkALgq9C01nWYfTzc+Z4=" }, "mongodb": { - "version": "2.2.36", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.36.tgz", - "integrity": "sha1-HFc2gLKEn7D0esu6PcX6Io3pdfU=", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.5.tgz", + "integrity": "sha512-GCjDxR3UOltDq00Zcpzql6dQo1sVry60OXJY3TDmFc2SWFY6c8Gn1Ardidc5jDirvJrx2GC3knGOImKphbSL3A==", "requires": { - "es6-promise": "3.2.1", - "mongodb-core": "2.1.20", - "readable-stream": "2.2.7" - }, - "dependencies": { - "es6-promise": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", - "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "readable-stream": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", - "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", - "requires": { - "buffer-shims": "~1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~1.0.0", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "mongodb-core": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz", - "integrity": "sha1-/s6N12tZ7n1/LTE7ZTIsFgSS2PE=", - "requires": { - "bson": "~1.0.4", - "require_optional": "~1.0.0" + "bl": "^2.2.0", + "bson": "^1.1.1", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" }, "dependencies": { "bson": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", - "integrity": "sha1-EjGfgyOxJUc5t8a++NPomuBaL1c=" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", + "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" } } }, "mongojs": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.4.0.tgz", - "integrity": "sha1-8of7/UV/7fWItakBHmhRPZ3TK/s=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-3.1.0.tgz", + "integrity": "sha512-aXJ4xfXwx9s1cqtKTZ24PypXiWhIgvgENObQzCGbV4QBxEVedy3yuErhx6znk959cF2dOzL2ClgXJvIhfgkpIQ==", "requires": { "each-series": "^1.0.0", - "mongodb": "^2.0.45", - "once": "^1.3.2", - "parse-mongo-url": "^1.1.0", - "readable-stream": "^2.0.2", - "thunky": "^0.1.0", - "to-mongodb-core": "^2.0.0", - "xtend": "^4.0.0" + "mongodb": "^3.3.2", + "nyc": "^14.1.1", + "once": "^1.4.0", + "parse-mongo-url": "^1.1.1", + "readable-stream": "^3.4.0", + "thunky": "^1.1.0", + "to-mongodb-core": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "ms": { @@ -4160,6 +4407,11 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -4201,7 +4453,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -4209,6 +4460,104 @@ "validate-npm-package-license": "^3.0.1" } }, + "nyc": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", + "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "requires": { + "archy": "^1.0.0", + "caching-transform": "^3.0.2", + "convert-source-map": "^1.6.0", + "cp-file": "^6.2.0", + "find-cache-dir": "^2.1.0", + "find-up": "^3.0.0", + "foreground-child": "^1.5.6", + "glob": "^7.1.3", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.4", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", + "merge-source-map": "^1.1.0", + "resolve-from": "^4.0.0", + "rimraf": "^2.6.3", + "signal-exit": "^3.0.2", + "spawn-wrap": "^1.4.2", + "test-exclude": "^5.2.3", + "uuid": "^3.3.2", + "yargs": "^13.2.2", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -4319,6 +4668,11 @@ "word-wrap": "~1.2.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -4364,6 +4718,17 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=" }, + "package-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", + "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^3.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4400,8 +4765,7 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", @@ -5192,6 +5556,11 @@ "long": "^4.0.0" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -5442,6 +5811,14 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "requires": { + "es6-error": "^4.0.1" + } + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -5494,8 +5871,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-in-the-middle": { "version": "4.0.1", @@ -5526,8 +5902,7 @@ "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "require-relative": { "version": "0.8.7", @@ -5538,7 +5913,7 @@ "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha1-TPNaQkf2TKPfjC7yCMxJSxyo/C4=", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", "requires": { "resolve-from": "^2.0.0", "semver": "^5.1.0" @@ -5548,7 +5923,6 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", "integrity": "sha1-QBSHC6KWF2uGND1Qtg87UGCc4jI=", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -5695,6 +6069,15 @@ } } }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", @@ -5719,8 +6102,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "settings-sharelatex": { "version": "1.1.0", @@ -5775,8 +6157,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sinon": { "version": "3.2.1", @@ -5828,11 +6209,76 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "spawn-wrap": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", + "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "requires": { + "minimist": "^1.2.5" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -5841,14 +6287,12 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -5857,8 +6301,7 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, "split": { "version": "1.0.1", @@ -5871,8 +6314,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.0", @@ -6001,7 +6443,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" }, @@ -6009,16 +6450,14 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" } } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" }, "strip-json-comments": { "version": "3.0.1", @@ -6128,6 +6567,117 @@ } } }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, "text-encoding": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", @@ -6154,9 +6704,9 @@ } }, "thunky": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", - "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, "timekeeper": { "version": "0.0.4", @@ -6176,8 +6726,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-mongodb-core": { "version": "2.0.0", @@ -6321,7 +6870,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -6426,7 +6974,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -6434,8 +6981,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "word-wrap": { "version": "1.2.3", @@ -6447,7 +6993,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -6457,20 +7002,17 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -6504,10 +7046,20 @@ } } }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha1-aGwg8hMgnpSr8NG88e+qKRx4J6c=", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "requires": { "sax": ">=0.6.0", "xmlbuilder": "~9.0.1" @@ -6527,16 +7079,10 @@ "@babel/runtime-corejs3": "^7.8.3" } }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { "version": "3.1.1", @@ -6547,7 +7093,6 @@ "version": "13.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, "requires": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -6564,14 +7109,12 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, "requires": { "locate-path": "^3.0.0" } @@ -6579,14 +7122,12 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -6596,7 +7137,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, "requires": { "p-limit": "^2.0.0" } @@ -6605,7 +7145,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -6618,7 +7157,6 @@ "version": "13.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index ee69618a54..900e625789 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -20,7 +20,7 @@ "dependencies": { "JSONStream": "^1.3.5", "async": "~0.2.10", - "aws-sdk": "^2.102.0", + "aws-sdk": "^2.642.0", "bson": "^0.4.20", "byline": "^4.2.1", "cli": "^0.6.6", @@ -30,7 +30,7 @@ "logger-sharelatex": "^1.9.0", "metrics-sharelatex": "^2.6.1", "mongo-uri": "^0.1.2", - "mongojs": "2.4.0", + "mongojs": "3.1.0", "redis": "~0.10.1", "redis-sharelatex": "^1.0.11", "request": "~2.88.2", From 3464088c9411e3dd346c82414e231f034975b44d Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 10:21:46 +0100 Subject: [PATCH 490/549] updated prettier and other minor dependencies --- services/track-changes/package-lock.json | 30 ++++++++++++++---------- services/track-changes/package.json | 8 +++---- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 57ce2c70d6..63e6031637 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1325,9 +1325,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sdk": { - "version": "2.642.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.642.0.tgz", - "integrity": "sha512-0ZNgL1HBXRVobFD9Z64RyQk50cNABDMU1GV4lYIAvao4urYqYJi2MEVQmq+7WyXyzkBWu3lAPNDiJ8WW7emTzg==", + "version": "2.643.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.643.0.tgz", + "integrity": "sha512-4r7VGQFqshrhXnOCVQdlatAWiK/8kmmtAtY9gbITPNpY5Is+SfIy6k/1BgrnL5H/2sYd27H+Xp8itXZoCnQeTw==", "requires": { "buffer": "4.9.1", "events": "1.1.1", @@ -2181,18 +2181,18 @@ } }, "eslint-config-prettier": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", - "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz", + "integrity": "sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ==", "dev": true, "requires": { "get-stdin": "^6.0.0" } }, "eslint-config-standard": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz", - "integrity": "sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", + "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", "dev": true }, "eslint-config-standard-jsx": { @@ -4860,9 +4860,9 @@ "dev": true }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.1.tgz", + "integrity": "sha512-piXGBcY1zoFOG0MvHpNE5reAGseLmaCRifQ/fmfF49BcYkInEs/naD/unxGNAeOKFA5+JxVrPyMvMlpzcd20UA==", "dev": true }, "prettier-eslint": { @@ -5102,6 +5102,12 @@ "mimic-fn": "^1.0.0" } }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 900e625789..f615fa6ba6 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -20,7 +20,7 @@ "dependencies": { "JSONStream": "^1.3.5", "async": "~0.2.10", - "aws-sdk": "^2.642.0", + "aws-sdk": "^2.643.0", "bson": "^0.4.20", "byline": "^4.2.1", "cli": "^0.6.6", @@ -44,8 +44,8 @@ "bunyan": "~2.0.2", "chai": "~4.2.0", "eslint": "^6.8.0", - "eslint-config-prettier": "^6.10.0", - "eslint-config-standard": "^14.1.0", + "eslint-config-prettier": "^6.10.1", + "eslint-config-standard": "^14.1.1", "eslint-config-standard-jsx": "^8.1.0", "eslint-config-standard-react": "^9.2.0", "eslint-plugin-chai-expect": "^2.1.0", @@ -60,7 +60,7 @@ "eslint-plugin-standard": "^4.0.1", "memorystream": "0.3.1", "mocha": "^4.0.1", - "prettier": "^1.19.1", + "prettier": "^2.0.1", "prettier-eslint-cli": "^5.0.0", "sandboxed-module": "~0.3.0", "sinon": "~3.2.1", From ddcdbb6448846172692b61ad1363a1613fb5c2d9 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 10:35:53 +0100 Subject: [PATCH 491/549] updated mocha and sinon, fixed test --- services/track-changes/package-lock.json | 555 +++++++++++++----- services/track-changes/package.json | 4 +- .../unit/js/DiffManager/DiffManagerTests.js | 12 +- 3 files changed, 432 insertions(+), 139 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 63e6031637..a45f1e2141 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -953,39 +953,48 @@ "integrity": "sha512-lu8BpxjAtRCAo5ifytTpCPCj99LF7o/2Myn+NXyNCBqvPYn7Pjd76AMmUB5l7XF1U6t0hcWrlEM5ESufW7wAeA==" }, "@sinonjs/commons": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", - "integrity": "sha1-ez7C2Wr0gdegMhJS57HJRyTsWng=", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", + "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==", "dev": true, "requires": { "type-detect": "4.0.8" } }, + "@sinonjs/fake-timers": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.0.tgz", + "integrity": "sha512-atR1J/jRXvQAb47gfzSK8zavXy7BcpnYq21ALon0U99etu99vsir0trzIO3wpeLtW+LLVY6X7EkfVTbjGSH8Ww==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, "@sinonjs/formatio": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", - "integrity": "sha1-UjEPL5vLxnvawYyUrUkBuV/eJn4=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", + "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", "dev": true, "requires": { "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@sinonjs/samsam": "^5.0.2" } }, "@sinonjs/samsam": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.2.tgz", - "integrity": "sha1-Y5QuPV6wt59t4775q/rRX7S2QBs=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz", + "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==", "dev": true, "requires": { - "@sinonjs/commons": "^1.0.2", - "array-from": "^2.1.1", - "lodash": "^4.17.11" + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, "@sinonjs/text-encoding": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha1-jaXGUwkVZT86Hzj9XxAdjD+AecU=", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, "@tootallnate/once": { @@ -1180,6 +1189,12 @@ "uri-js": "^4.2.2" } }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -1211,6 +1226,16 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "append-transform": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", @@ -1250,12 +1275,6 @@ } } }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, "array-includes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", @@ -1420,6 +1439,12 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -1462,10 +1487,19 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "bson": { @@ -1610,6 +1644,22 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } + }, "cli": { "version": "0.6.6", "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", @@ -1907,9 +1957,9 @@ "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" }, "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "dlv": { @@ -2739,6 +2789,15 @@ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", @@ -2798,6 +2857,15 @@ "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -2921,15 +2989,6 @@ "mime-types": "^2.1.12" } }, - "formatio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", - "dev": true, - "requires": { - "samsam": "1.x" - } - }, "formidable": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", @@ -2945,6 +3004,13 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3102,9 +3168,9 @@ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "gtoken": { @@ -3166,9 +3232,9 @@ } }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "has-symbols": { @@ -3193,9 +3259,9 @@ } }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "heap": { @@ -3444,6 +3510,15 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", @@ -3482,6 +3557,12 @@ "is-extglob": "^2.1.1" } }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -3754,9 +3835,9 @@ } }, "just-extend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha1-8/R/ffyg+YnFVBCn68iFSwcQivw=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", + "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", "dev": true }, "jwa": { @@ -3858,6 +3939,12 @@ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.has": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", @@ -3891,6 +3978,15 @@ "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + } + }, "logger-sharelatex": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.9.0.tgz", @@ -4026,12 +4122,6 @@ } } }, - "lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha1-ETAB1Wv8fgLVbjYpHMXEE9GqBzM=", - "dev": true - }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", @@ -4216,42 +4306,65 @@ "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" }, "mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", + "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", "dev": true, "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.3", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" }, "dependencies": { - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", - "dev": true - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" } }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -4262,29 +4375,99 @@ "path-is-absolute": "^1.0.0" } }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -4389,12 +4572,6 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", "integrity": "sha1-exqhk+mqhgV+PHu9CsRI53CSVVI=" }, - "native-promise-only": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4419,22 +4596,32 @@ "dev": true }, "nise": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.0.tgz", - "integrity": "sha1-0D6g5sG3XGOAFao1he3cEylJpQ0=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz", + "integrity": "sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg==", "dev": true, "requires": { - "@sinonjs/formatio": "^3.1.0", + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^4.1.0", "path-to-regexp": "^1.7.0" + } + }, + "node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" }, "dependencies": { - "lolex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.1.0.tgz", - "integrity": "sha1-7N17hlOTkdgjeUejQZqorJdfD+E=", + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -4460,6 +4647,12 @@ "validate-npm-package-license": "^3.0.1" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -4617,6 +4810,16 @@ "has": "^1.0.3" } }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "object.values": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", @@ -4790,9 +4993,9 @@ "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=" }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -4839,6 +5042,12 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -5716,6 +5925,15 @@ "util-deprecate": "~1.0.1" } }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, "redis": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz", @@ -6051,12 +6269,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha1-jR2TUOJWItow3j5EumkrUiGrfFA=", - "dev": true - }, "sandboxed-module": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", @@ -6166,20 +6378,41 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sinon": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", - "integrity": "sha1-2K2r2QBzD9SXeIoCcEnGSwi+kcI=", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.1.tgz", + "integrity": "sha512-iTTyiQo5T94jrOx7X7QLBZyucUJ2WvL9J13+96HMfm2CGoJYbIPqRfl6wgNcqmzk0DI28jeGx5bUTXizkrqBmg==", "dev": true, "requires": { - "diff": "^3.1.0", - "formatio": "1.2.0", - "lolex": "^2.1.2", - "native-promise-only": "^0.8.1", - "nise": "^1.0.1", - "path-to-regexp": "^1.7.0", - "samsam": "^1.1.3", - "text-encoding": "0.6.4", - "type-detect": "^4.0.0" + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/formatio": "^5.0.1", + "@sinonjs/samsam": "^5.0.3", + "diff": "^4.0.2", + "nise": "^4.0.1", + "supports-color": "^7.1.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "slice-ansi": { @@ -6477,12 +6710,12 @@ "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { - "has-flag": "^2.0.0" + "has-flag": "^3.0.0" } }, "table": { @@ -6684,12 +6917,6 @@ } } }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6744,6 +6971,15 @@ "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "to-snake-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", @@ -6989,6 +7225,48 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -7168,6 +7446,17 @@ "decamelize": "^1.2.0" } }, + "yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + } + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index f615fa6ba6..26ff71842c 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -59,11 +59,11 @@ "eslint-plugin-react": "^7.19.0", "eslint-plugin-standard": "^4.0.1", "memorystream": "0.3.1", - "mocha": "^4.0.1", + "mocha": "^7.1.1", "prettier": "^2.0.1", "prettier-eslint-cli": "^5.0.0", "sandboxed-module": "~0.3.0", - "sinon": "~3.2.1", + "sinon": "~9.0.1", "timekeeper": "0.0.4" } } diff --git a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js index f6768fa197..bee618badf 100644 --- a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js @@ -181,7 +181,7 @@ describe('DiffManager', function() { }) }) - return describe('when the updates are inconsistent', function() { + describe('when the updates are inconsistent', function() { beforeEach(function() { this.DiffManager.getLatestDocAndUpdates = sinon .stub() @@ -189,7 +189,9 @@ describe('DiffManager', function() { this.DiffGenerator.buildDiff = sinon .stub() .throws((this.error = new Error('inconsistent!'))) - return this.DiffManager.getDiff( + this.DiffGenerator.rewindUpdates = sinon + .stub() + this.DiffManager.getDiff( this.project_id, this.doc_id, this.fromVersion, @@ -198,8 +200,10 @@ describe('DiffManager', function() { ) }) - return it('should call the callback with an error', function() { - return this.callback.calledWith(this.error).should.equal(true) + it('should call the callback with an error', function() { + this.callback.calledWith(sinon.match(Error)).should.equal(true) + const errorObj = this.callback.args[0][0] + expect(errorObj.message).to.include('inconsistent!') }) }) }) From 79f6b9a87796ed82f36225060b79aee0390f731b Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 11:10:24 +0100 Subject: [PATCH 492/549] updated express to v4 --- services/track-changes/app.js | 4 + .../track-changes/app/js/HttpController.js | 20 +- services/track-changes/package-lock.json | 461 ++++++++++++++---- services/track-changes/package.json | 3 +- .../js/helpers/MockDocUpdaterApi.js | 3 +- 5 files changed, 382 insertions(+), 109 deletions(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index a69cdabc88..ed49af171e 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -46,8 +46,12 @@ const child_process = require('child_process') const HttpController = require('./app/js/HttpController') const express = require('express') +const bodyParser = require('body-parser'); + const app = express() +app.use(bodyParser.json()) + app.use(Metrics.http.monitor(logger)) Metrics.injectMetricsRoute(app) diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js index 87f0003606..662ca49f0d 100644 --- a/services/track-changes/app/js/HttpController.js +++ b/services/track-changes/app/js/HttpController.js @@ -36,7 +36,7 @@ module.exports = HttpController = { if (error != null) { return next(error) } - return res.send(204) + return res.sendStatus(204) } ) }, @@ -53,7 +53,7 @@ module.exports = HttpController = { if (error != null) { return next(error) } - return res.send(204) + return res.sendStatus(204) } ) }, @@ -134,7 +134,7 @@ module.exports = HttpController = { if (broken.length > 0) { return res.send(broken) } else { - return res.send(204) + return res.sendStatus(204) } }) }, @@ -215,7 +215,7 @@ module.exports = HttpController = { if (error != null) { return next(error) } - return res.send(204) + return res.sendStatus(204) } ) }, @@ -231,7 +231,7 @@ module.exports = HttpController = { if (error != null) { return next(error) } - return res.send(204) + return res.sendStatus(204) }) }, @@ -246,7 +246,7 @@ module.exports = HttpController = { if (error != null) { return next(error) } - return res.send(204) + return res.sendStatus(204) }) }, @@ -254,9 +254,9 @@ module.exports = HttpController = { return HealthChecker.check(function(err) { if (err != null) { logger.err({ err }, 'error performing health check') - return res.send(500) + return res.sendStatus(500) } else { - return res.send(200) + return res.sendStatus(200) } }) }, @@ -265,9 +265,9 @@ module.exports = HttpController = { return HealthChecker.checkLock(function(err) { if (err != null) { logger.err({ err }, 'error performing lock check') - return res.send(500) + return res.sendStatus(500) } else { - return res.send(200) + return res.sendStatus(200) } }) } diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index a45f1e2141..64f1203fd3 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1159,6 +1159,30 @@ "event-target-shim": "^5.0.0" } }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "dependencies": { + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + } + } + }, "acorn": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", @@ -1275,6 +1299,11 @@ } } }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, "array-includes": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", @@ -1472,6 +1501,38 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "boolify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz", @@ -1517,11 +1578,6 @@ "isarray": "^1.0.0" } }, - "buffer-crc32": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", - "integrity": "sha1-vj5TgvwCttYySVasGvmKqYsIU0w=" - }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -1551,9 +1607,9 @@ "integrity": "sha1-wgOpilsCkIIqk4anjtosvVvNsy8=" }, "bytes": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz", - "integrity": "sha1-qtM+wU49wsp06OfUUfm6BTrU96A=" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, "caching-transform": { "version": "3.0.2", @@ -1747,14 +1803,6 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-1.2.0.tgz", - "integrity": "sha1-/VcTv6FTx9bMWZN4patMRcU1Ap4=", - "requires": { - "keypress": "0.1.x" - } - }, "common-tags": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", @@ -1771,25 +1819,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "connect": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.5.tgz", - "integrity": "sha1-IFcgd7ofYm/bdAsK1waPkTDXAbg=", - "requires": { - "buffer-crc32": "0.2.1", - "bytes": "0.2.0", - "cookie": "0.1.0", - "cookie-signature": "1.0.1", - "debug": "*", - "formidable": "1.0.14", - "fresh": "0.2.0", - "methods": "0.0.1", - "pause": "0.0.1", - "qs": "0.6.5", - "send": "0.1.4", - "uid2": "0.0.2" - } - }, "console-log-level": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", @@ -1801,6 +1830,19 @@ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "dev": true }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, "continuation-local-storage": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", @@ -1819,14 +1861,14 @@ } }, "cookie": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", - "integrity": "sha1-kOtGndzpBchm3mh+/EMTHYgB+dA=" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, "cookie-signature": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz", - "integrity": "sha1-ROByFIrwHm6OJK+/EmkNaK5pjss=" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "core-js": { "version": "3.6.4", @@ -1956,6 +1998,16 @@ "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -2046,6 +2098,11 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -2115,6 +2172,11 @@ "es6-promise": "^4.0.3" } }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -2670,6 +2732,11 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -2701,21 +2768,60 @@ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" }, "express": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/express/-/express-3.3.5.tgz", - "integrity": "sha1-P9B3ZgycyuRxD8+zJikKAdHnJWY=", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "requires": { - "buffer-crc32": "0.2.1", - "commander": "1.2.0", - "connect": "2.8.5", - "cookie": "0.1.0", - "cookie-signature": "1.0.1", - "debug": "*", - "fresh": "0.2.0", - "methods": "0.0.1", - "mkdirp": "0.3.5", - "range-parser": "0.0.4", - "send": "0.1.4" + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + } } }, "extend": { @@ -2798,6 +2904,35 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", @@ -2989,15 +3124,15 @@ "mime-types": "^2.1.12" } }, - "formidable": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", - "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=" + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fresh": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", - "integrity": "sha1-v9lALPPfEsSkwxDHn5mj3eE9NKc=" + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs.realpath": { "version": "1.0.0", @@ -3284,6 +3419,18 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==" }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, "http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -3317,7 +3464,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -3500,6 +3646,11 @@ "standard-as-callback": "^2.0.1" } }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, "is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", @@ -3859,11 +4010,6 @@ "safe-buffer": "^5.0.1" } }, - "keypress": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", - "integrity": "sha1-SjGI1CkbZrT2XtuZ+AaqmuKTWSo=" - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -4187,6 +4333,11 @@ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==" }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -4199,6 +4350,11 @@ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "dev": true }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, "merge-source-map": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", @@ -4236,9 +4392,9 @@ "dev": true }, "methods": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", - "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "metrics-sharelatex": { "version": "2.6.1", @@ -4263,9 +4419,9 @@ } }, "mime": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", - "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { "version": "1.37.0", @@ -4584,6 +4740,11 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, "nested-error-stacks": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", @@ -4965,6 +5126,11 @@ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==" }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -5032,11 +5198,6 @@ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, - "pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -5771,6 +5932,15 @@ "long": "^4.0.0" } }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -5834,9 +6004,9 @@ "integrity": "sha1-I8BsRsgTKGFqrhaNPuI6Vr1D2vY=" }, "qs": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz", - "integrity": "sha1-KUsmjksNQlD23eGbO4s0k13/FO8=" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "querystring": { "version": "0.2.0", @@ -5856,9 +6026,9 @@ "dev": true }, "range-parser": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", - "integrity": "sha1-wEJ//vUcEKy6B4KkbJYC50T/Ygs=" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raven": { "version": "1.1.3", @@ -5884,6 +6054,17 @@ } } }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -6307,14 +6488,51 @@ "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=" }, "send": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", - "integrity": "sha1-vnDY0b4B3mGCGvE3gLUDRaT3Gr0=", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "requires": { - "debug": "*", - "fresh": "0.2.0", - "mime": "~1.2.9", - "range-parser": "0.0.4" + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" } }, "set-blocking": { @@ -6322,6 +6540,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, "settings-sharelatex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", @@ -6601,6 +6824,11 @@ "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, "stream-events": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", @@ -6996,6 +7224,11 @@ "to-no-case": "^1.0.0" } }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -7052,22 +7285,46 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "dependencies": { + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + } + } + }, "typescript": { "version": "3.7.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", "dev": true }, - "uid2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz", - "integrity": "sha1-EH+xVcgsETZiB5ftTIjPKwj2qrg=" - }, "underscore": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==" }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -7097,6 +7354,11 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", @@ -7117,6 +7379,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 26ff71842c..982f178dc2 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -21,10 +21,11 @@ "JSONStream": "^1.3.5", "async": "~0.2.10", "aws-sdk": "^2.643.0", + "body-parser": "^1.19.0", "bson": "^0.4.20", "byline": "^4.2.1", "cli": "^0.6.6", - "express": "3.3.5", + "express": "4.17.1", "heap": "^0.2.6", "line-reader": "^0.2.4", "logger-sharelatex": "^1.9.0", diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js index 38a4136d9f..a30bcc2e31 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js @@ -13,7 +13,9 @@ */ let MockDocUpdaterApi const express = require('express') +const bodyParser = require('body-parser') const app = express() +app.use(bodyParser.json()) module.exports = MockDocUpdaterApi = { docs: {}, @@ -56,7 +58,6 @@ module.exports = MockDocUpdaterApi = { app.post( '/project/:project_id/doc/:doc_id', - express.bodyParser(), (req, res, next) => { return this.setDoc( req.params.project_id, From dd3673d5c0e6e561e59ebee54945a17aece4a256 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 11:17:32 +0100 Subject: [PATCH 493/549] updated mock methods to express v4 signatures --- .../test/acceptance/js/helpers/MockDocStoreApi.js | 4 ++-- .../test/acceptance/js/helpers/MockDocUpdaterApi.js | 8 ++++---- .../test/acceptance/js/helpers/MockWebApi.js | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js index 8ebaf68081..ef912948eb 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js @@ -28,10 +28,10 @@ module.exports = MockDocUpdaterApi = { app.get('/project/:project_id/doc', (req, res, next) => { return this.getAllDoc(req.params.project_id, (error, docs) => { if (error != null) { - res.send(500) + res.sendStatus(500) } if (docs == null) { - return res.send(404) + return res.sendStatus(404) } else { return res.send(JSON.stringify(docs)) } diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js index a30bcc2e31..449bf1e217 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js @@ -45,10 +45,10 @@ module.exports = MockDocUpdaterApi = { req.params.doc_id, (error, doc) => { if (error != null) { - res.send(500) + res.sendStatus(500) } if (doc == null) { - return res.send(404) + return res.sendStatus(404) } else { return res.send(JSON.stringify(doc)) } @@ -67,9 +67,9 @@ module.exports = MockDocUpdaterApi = { req.body.undoing, (errr, doc) => { if (typeof error !== 'undefined' && error !== null) { - return res.send(500) + return res.sendStatus(500) } else { - return res.send(204) + return res.sendStatus(204) } } ) diff --git a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js index ebf1b05e18..45e94db8f8 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js @@ -37,10 +37,10 @@ module.exports = MockWebApi = { app.get('/user/:user_id/personal_info', (req, res, next) => { return this.getUserInfo(req.params.user_id, (error, user) => { if (error != null) { - res.send(500) + res.sendStatus(500) } if (user == null) { - return res.send(404) + return res.sendStatus(404) } else { return res.send(JSON.stringify(user)) } @@ -50,10 +50,10 @@ module.exports = MockWebApi = { app.get('/project/:project_id/details', (req, res, next) => { return this.getProjectDetails(req.params.project_id, (error, project) => { if (error != null) { - res.send(500) + res.sendStatus(500) } if (project == null) { - return res.send(404) + return res.sendStatus(404) } else { return res.send(JSON.stringify(project)) } From f321144d8cb1c652b5ab336da12aa30aad363dd4 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 11:25:28 +0100 Subject: [PATCH 494/549] fixed unit test using legacy express methods --- .../unit/js/HttpController/HttpControllerTests.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js index 68bab28da3..93fb2b0789 100644 --- a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js @@ -44,7 +44,7 @@ describe('HttpController', function() { project_id: this.project_id } } - this.res = { send: sinon.stub() } + this.res = { sendStatus: sinon.stub() } this.UpdatesManager.processUncompressedUpdatesWithLock = sinon .stub() .callsArg(2) @@ -58,7 +58,7 @@ describe('HttpController', function() { }) return it('should return a success code', function() { - return this.res.send.calledWith(204).should.equal(true) + return this.res.sendStatus.calledWith(204).should.equal(true) }) }) @@ -69,7 +69,7 @@ describe('HttpController', function() { project_id: this.project_id } } - this.res = { send: sinon.stub() } + this.res = { sendStatus: sinon.stub() } this.UpdatesManager.processUncompressedUpdatesForProject = sinon .stub() .callsArg(1) @@ -83,7 +83,7 @@ describe('HttpController', function() { }) return it('should return a success code', function() { - return this.res.send.calledWith(204).should.equal(true) + return this.res.sendStatus.calledWith(204).should.equal(true) }) }) @@ -177,7 +177,7 @@ describe('HttpController', function() { 'x-user-id': this.user_id } } - this.res = { send: sinon.stub() } + this.res = { sendStatus: sinon.stub() } this.RestoreManager.restoreToBeforeVersion = sinon.stub().callsArg(4) return this.HttpController.restore(this.req, this.res, this.next) @@ -195,7 +195,7 @@ describe('HttpController', function() { }) return it('should return a success code', function() { - return this.res.send.calledWith(204).should.equal(true) + return this.res.sendStatus.calledWith(204).should.equal(true) }) }) }) From cd0e7b3afd4bc0e416e3a7c5d80fe8139a41f9c8 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 11:28:26 +0100 Subject: [PATCH 495/549] updated async, byline, line-reader and cli --- services/track-changes/package-lock.json | 72 ++++++++++++------------ services/track-changes/package.json | 10 ++-- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 64f1203fd3..cd382b2bae 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1354,9 +1354,12 @@ "dev": true }, "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } }, "async-listener": { "version": "0.6.10", @@ -1602,9 +1605,9 @@ } }, "byline": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/byline/-/byline-4.2.2.tgz", - "integrity": "sha1-wgOpilsCkIIqk4anjtosvVvNsy8=" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=" }, "bytes": { "version": "3.1.0", @@ -1717,12 +1720,13 @@ } }, "cli": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", - "integrity": "sha1-Aq1Eo4Cr8nraxebwzdewQ9dMU+M=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, "requires": { "exit": "0.1.2", - "glob": "~ 3.2.1" + "glob": "^7.1.1" } }, "cli-cursor": { @@ -2765,7 +2769,8 @@ "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true }, "express": { "version": "4.17.1", @@ -3212,12 +3217,17 @@ } }, "glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", "inherits": "2", - "minimatch": "0.3" + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-parent": { @@ -4021,9 +4031,9 @@ } }, "line-reader": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.2.4.tgz", - "integrity": "sha1-xDkrWH3qOFgMlnhXDm6OSfzlJiI=" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.4.0.tgz", + "integrity": "sha1-F+RIGNoKwzVnW6MAlU+U72cOZv0=" }, "load-json-file": { "version": "2.0.0", @@ -4282,11 +4292,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" - }, "lsmod": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", @@ -4443,12 +4448,12 @@ "dev": true }, "minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -6590,11 +6595,6 @@ "object-inspect": "^1.7.0" } }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -7170,9 +7170,9 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, "timekeeper": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", - "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.2.0.tgz", + "integrity": "sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==", "dev": true }, "tmp": { diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 982f178dc2..ee599e827a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -19,15 +19,14 @@ }, "dependencies": { "JSONStream": "^1.3.5", - "async": "~0.2.10", + "async": "^2.6.3", "aws-sdk": "^2.643.0", "body-parser": "^1.19.0", "bson": "^0.4.20", - "byline": "^4.2.1", - "cli": "^0.6.6", + "byline": "^5.0.0", "express": "4.17.1", "heap": "^0.2.6", - "line-reader": "^0.2.4", + "line-reader": "^0.4.0", "logger-sharelatex": "^1.9.0", "metrics-sharelatex": "^2.6.1", "mongo-uri": "^0.1.2", @@ -44,6 +43,7 @@ "babel-eslint": "^10.1.0", "bunyan": "~2.0.2", "chai": "~4.2.0", + "cli": "^1.0.1", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.1", "eslint-config-standard": "^14.1.1", @@ -65,6 +65,6 @@ "prettier-eslint-cli": "^5.0.0", "sandboxed-module": "~0.3.0", "sinon": "~9.0.1", - "timekeeper": "0.0.4" + "timekeeper": "2.2.0" } } From 87737a403d3ba72a5e76fc06f08e23e997693013 Mon Sep 17 00:00:00 2001 From: mserranom Date: Mon, 23 Mar 2020 11:34:57 +0100 Subject: [PATCH 496/549] updated sandboxed-module and tests --- services/track-changes/package-lock.json | 16 ++++------------ services/track-changes/package.json | 2 +- .../test/unit/js/DocArchive/MongoAWS.js | 1 + .../js/HttpController/HttpControllerTests.js | 1 + .../js/UpdatesManager/UpdatesManagerTests.js | 1 + 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index cd382b2bae..3b2a271f27 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -6456,21 +6456,13 @@ "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, "sandboxed-module": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", - "integrity": "sha1-8fvvvYCaT2kHO9B8rm/H2y6vX2o=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz", + "integrity": "sha1-x+VFkzm7y6KMUwPusz9ug4e/upY=", "dev": true, "requires": { "require-like": "0.1.2", - "stack-trace": "0.0.6" - }, - "dependencies": { - "stack-trace": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", - "integrity": "sha1-HnGb1qJin/CcGJ4Xqe+QKpT8XbA=", - "dev": true - } + "stack-trace": "0.0.9" } }, "saslprep": { diff --git a/services/track-changes/package.json b/services/track-changes/package.json index ee599e827a..0bcb27b3c3 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -63,7 +63,7 @@ "mocha": "^7.1.1", "prettier": "^2.0.1", "prettier-eslint-cli": "^5.0.0", - "sandboxed-module": "~0.3.0", + "sandboxed-module": "~2.0.3", "sinon": "~9.0.1", "timekeeper": "2.2.0" } diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index 27e18b1d6b..ad0e33ca54 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -21,6 +21,7 @@ const zlib = require('zlib') describe('MongoAWS', function() { beforeEach(function() { this.MongoAWS = SandboxedModule.require(modulePath, { + singleOnly: true, requires: { 'settings-sharelatex': (this.settings = { trackchanges: { diff --git a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js index 93fb2b0789..52beb5a198 100644 --- a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js @@ -19,6 +19,7 @@ const SandboxedModule = require('sandboxed-module') describe('HttpController', function() { beforeEach(function() { this.HttpController = SandboxedModule.require(modulePath, { + singleOnly: true, requires: { 'logger-sharelatex': { log: sinon.stub() }, './UpdatesManager': (this.UpdatesManager = {}), diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index 2891d1d4ee..788821aea1 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -23,6 +23,7 @@ const SandboxedModule = require('sandboxed-module') describe('UpdatesManager', function() { beforeEach(function() { this.UpdatesManager = SandboxedModule.require(modulePath, { + singleOnly: true, requires: { './UpdateCompressor': (this.UpdateCompressor = {}), './MongoManager': (this.MongoManager = {}), From 42049d5e0d7d9eeb5a1b6052cd758f915ed29d22 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Wed, 25 Mar 2020 10:13:07 -0400 Subject: [PATCH 497/549] Upgrade metrics-sharelatex to 2.6.2 This version fixes the HTTP request timing metrics, which were not reporting correctly. --- services/track-changes/package-lock.json | 659 +++++++++++------------ services/track-changes/package.json | 2 +- 2 files changed, 320 insertions(+), 341 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 3b2a271f27..477d0d80cb 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -26,7 +26,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" } } }, @@ -317,14 +317,6 @@ "safe-buffer": "^5.0.1" } }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", @@ -470,7 +462,7 @@ "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" }, "debug": { "version": "3.2.6", @@ -575,14 +567,6 @@ "safe-buffer": "^5.0.1" } }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", @@ -776,19 +760,6 @@ "safe-buffer": "^5.0.1" } }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", @@ -896,7 +867,7 @@ "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "@protobufjs/base64": { "version": "1.1.2", @@ -911,12 +882,12 @@ "@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "requires": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -925,27 +896,27 @@ "@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "@sindresorhus/is": { "version": "0.15.0", @@ -1145,7 +1116,7 @@ "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "requires": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -1205,7 +1176,7 @@ "ajv": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", - "integrity": "sha1-ys7M9HS/P8POOxR0Q3EaJAY8ww0=", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -1271,7 +1242,7 @@ "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" }, "argparse": { "version": "1.0.10", @@ -1284,7 +1255,7 @@ "aria-query": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==", "dev": true, "requires": { "ast-types-flow": "0.0.7", @@ -1302,7 +1273,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { "version": "3.1.1", @@ -1328,12 +1299,12 @@ "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha1-yWVekzHgq81YjSp8rX6ZVvZnAfo=" + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" }, "assertion-error": { "version": "1.1.0", @@ -1344,7 +1315,7 @@ "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, "astral-regex": { @@ -1373,7 +1344,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "aws-sdk": { "version": "2.643.0", @@ -1401,12 +1372,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "axios": { "version": "0.18.1", @@ -1451,17 +1422,17 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" }, "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM=" + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "requires": { "tweetnacl": "^0.14.3" } @@ -1488,7 +1459,7 @@ "bintrees": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", - "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + "integrity": "sha512-tbaUB1QpTIj4cKY8c1rvNAvEQXA+ekzHmbe4jzNfW3QWsF9GnnP/BRWyl6/qqS53heoYJ93naaFcm/jooONH8g==" }, "bl": { "version": "2.2.0", @@ -1532,20 +1503,20 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "boolify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz", - "integrity": "sha1-tcCeF8rNET0Rt7s+04TMASmU2Gs=", + "integrity": "sha512-ma2q0Tc760dW54CdOyJjhrg/a54317o1zYADQJFgperNGKIKgAUGIcKnuMiff8z57+yGlrGNEt4lPgZfCgTJgA==", "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1569,12 +1540,12 @@ "bson": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz", - "integrity": "sha1-5louPHUH/63kEJvHV1p25Q+NqRU=" + "integrity": "sha512-xMUimhLm6y4t9BTW6BQGRHs9PODB9082EUX/Gkx6M9T2ktuJ5LvMxY/20ukuk0Uc+WPL37pbMIy731XF7eTxjg==" }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "integrity": "sha512-DNK4ruAqtyHaN8Zne7PkBTO+dD1Lr0YfTduMqlIyjvQIoztBkUxrvL+hKeLW8NXFKHOq/2upkxuoS9znQ9bW9A==", "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", @@ -1584,7 +1555,7 @@ "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "builtin-modules": { "version": "3.1.0", @@ -1594,7 +1565,7 @@ "bunyan": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", - "integrity": "sha1-jbsP6xoyC5JVvEK6LURgrWwEUCg=", + "integrity": "sha512-9/tdl5YpJ2FR1ldac8y+hAC3rLaVzXnrd1ZATS+ehg5VCpD+5vltW9VVWbMF8o5qiw4uQO0nEFK/wL7B2SDvpg==", "dev": true, "requires": { "dtrace-provider": "~0.8", @@ -1607,7 +1578,7 @@ "byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=" + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==" }, "bytes": { "version": "3.1.0", @@ -1650,7 +1621,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, "chai": { "version": "4.2.0", @@ -1679,7 +1650,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "supports-color": { "version": "5.5.0", @@ -1700,7 +1671,7 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true }, "chokidar": { @@ -1722,7 +1693,7 @@ "cli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", - "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", "dev": true, "requires": { "exit": "0.1.2", @@ -1741,7 +1712,7 @@ "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "integrity": "sha512-EJLbKSuvHTrVRynOXCYFTbQKZOFXWNe3/6DN1yrEH3TuuZT1x4dMQnCHnfCrBUUiGjO63enEIfaB17VaRl2d4A==", "dev": true }, "cliui": { @@ -1762,7 +1733,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" }, "string-width": { "version": "3.1.0", @@ -1784,7 +1755,7 @@ "coffee-script": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + "integrity": "sha512-Tx8itEfCsQp8RbLDFt7qwjqXycAx2g6SI7//4PPUR2j6meLmNifYm6zKrNDcU1+Q/GWRhjhEZk7DaLG1TfIzGA==" }, "color-convert": { "version": "1.9.3", @@ -1797,7 +1768,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "combined-stream": { "version": "1.0.8", @@ -1816,12 +1787,12 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "console-log-level": { "version": "1.4.1", @@ -1831,7 +1802,7 @@ "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==", "dev": true }, "content-disposition": { @@ -1872,7 +1843,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "core-js": { "version": "3.6.4", @@ -1889,7 +1860,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "cp-file": { "version": "6.2.0", @@ -1919,7 +1890,7 @@ "d64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d64/-/d64-1.0.0.tgz", - "integrity": "sha1-QAKofoUMv8n52XBrYPymE6MzbpA=" + "integrity": "sha512-5eNy3WZziVYnrogqgXhcdEmqcDB2IHurTqLcrgssJsfkMVCUoUaZpK6cJjxxvLV2dUm5SuJMNcYfVGoin9UIRw==" }, "damerau-levenshtein": { "version": "1.0.6", @@ -1930,7 +1901,7 @@ "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "requires": { "assert-plus": "^1.0.0" }, @@ -1938,14 +1909,14 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" } } }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { "ms": "^2.1.1" } @@ -1953,7 +1924,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" }, "deep-eql": { "version": "3.0.1", @@ -1967,13 +1938,13 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "integrity": "sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==", "dev": true }, "default-require-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", "requires": { "strip-bom": "^3.0.0" } @@ -1995,7 +1966,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "denque": { "version": "1.4.1", @@ -2005,12 +1976,12 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" }, "diff": { "version": "3.5.0", @@ -2044,7 +2015,7 @@ "dtrace-provider": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", - "integrity": "sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ=", + "integrity": "sha512-V+HIGbAdxCIxddHNDwzXi6cx8Cz5RRlQOVcsryHfsyVVebpBEnDwHSgqxpgKzqeU/6/0DWqRLAGUwkbg2ecN1Q==", "optional": true, "requires": { "nan": "^2.10.0" @@ -2064,12 +2035,12 @@ "each-series": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", - "integrity": "sha1-+Ibmxm39sl7x/nNWQUbuXLR4r8s=" + "integrity": "sha512-4MQloCGGCmT5GJZK5ibgJSvTK1c1QSrNlDvLk6fEyRxjZnXjl+NNFfzhfXpmnWh33Owc9D9klrdzCUi7yc9r4Q==" }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -2086,7 +2057,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "emitter-listener": { "version": "1.1.2", @@ -2105,7 +2076,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "end-of-stream": { "version": "1.4.4", @@ -2118,7 +2089,7 @@ "ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==" }, "error-ex": { "version": "1.3.2", @@ -2171,7 +2142,7 @@ "es6-promisify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", "requires": { "es6-promise": "^4.0.3" } @@ -2179,12 +2150,12 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "eslint": { "version": "6.8.0", @@ -2348,7 +2319,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "resolve": { @@ -2384,7 +2355,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -2460,7 +2431,7 @@ "doctrine": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -2479,7 +2450,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "resolve": { @@ -2739,7 +2710,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "event-target-shim": { "version": "5.0.1", @@ -2758,18 +2729,18 @@ "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" }, "exeunt": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", - "integrity": "sha1-r3Lbb5Szy3XpIa7jddUTBJhD0oQ=", + "integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==", "dev": true }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, "express": { @@ -2820,19 +2791,19 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" } } }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "external-editor": { "version": "3.1.0", @@ -2848,12 +2819,12 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" }, "fast-diff": { "version": "1.2.0", @@ -2864,12 +2835,12 @@ "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "integrity": "sha512-eIgZvM9C3P05kg0qxfqaVU6Tma4QedCPIByQOcemV0vju8ot3cS2DpHi4m2G2JvbSMI152rjfLX0p1pkSdyPlQ==" }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fast-text-encoding": { @@ -2934,7 +2905,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -2986,7 +2957,7 @@ "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "requires": { "locate-path": "^2.0.0" @@ -2995,7 +2966,7 @@ "findit2": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", - "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" + "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==" }, "flat": { "version": "4.1.0", @@ -3076,14 +3047,14 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "foreground-child": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "integrity": "sha512-3TOY+4TKV0Ml83PXJQY+JFQaHNV38lzQDIzzXYg1kWdBLenGgoZhAs0CKgzI31vi2pWEpQMq/Yi4bpKwCPkw7g==", "requires": { "cross-spawn": "^4", "signal-exit": "^3.0.0" @@ -3092,7 +3063,7 @@ "cross-spawn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", "requires": { "lru-cache": "^4.0.1", "which": "^1.2.9" @@ -3110,14 +3081,14 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" } } }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" }, "form-data": { "version": "2.3.3", @@ -3132,17 +3103,17 @@ "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "integrity": "sha512-Ua9xNhH0b8pwE3yRbFfXJvfdWF0UHNCdeyb2sbi9Ul/M+r3PTdrz7Cv4SCfZRMjmzEM9PhraqfZFbGTIg3OMyA==" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.1.2", @@ -3160,7 +3131,7 @@ "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, "gaxios": { @@ -3192,7 +3163,7 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "get-stdin": { @@ -3204,7 +3175,7 @@ "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "requires": { "assert-plus": "^1.0.0" }, @@ -3212,7 +3183,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" } } }, @@ -3339,12 +3310,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -3362,7 +3333,7 @@ "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -3371,7 +3342,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true } } @@ -3379,7 +3350,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "has-symbols": { @@ -3391,7 +3362,7 @@ "hasha": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "integrity": "sha512-w0Kz8lJFBoyaurBiNrIvxPqr/gJ6fOfSkpAPOepN3oECqGJag37xPbOv57izi/KP8auHgNYxn5fXtAb+1LsJ6w==", "requires": { "is-stream": "^1.0.1" }, @@ -3399,7 +3370,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" } } }, @@ -3412,7 +3383,7 @@ "heap": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", - "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=" + "integrity": "sha512-MzzWcnfB1e4EG2vHi3dXHoBupmuXNZzx6pY6HldVS55JKKBoq3xOyzfSaZRkJp37HIhEYC78knabHff3zc4dQQ==" }, "hex2dec": { "version": "1.1.2", @@ -3454,7 +3425,7 @@ "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -3510,7 +3481,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" }, "indent-string": { "version": "4.0.0", @@ -3521,7 +3492,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -3530,7 +3501,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" }, "inquirer": { "version": "7.1.0", @@ -3669,7 +3640,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-binary-path": { "version": "2.1.0", @@ -3700,7 +3671,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -3732,7 +3703,7 @@ "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "integrity": "sha512-NECAi6wp6CgMesHuVUEK8JwjCvm/tvnn5pCbB42JOHp3mgUizN0nagXu4HEqQZBkieGEQ+jVcMKWqoVd6CDbLQ==", "dev": true }, "is-regex": { @@ -3772,22 +3743,22 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "istanbul-lib-coverage": { "version": "2.0.5", @@ -3836,7 +3807,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "supports-color": { "version": "6.1.0", @@ -3902,7 +3873,7 @@ "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==" }, "js-tokens": { "version": "4.0.0", @@ -3921,7 +3892,7 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, "jsesc": { "version": "2.5.2", @@ -3931,7 +3902,7 @@ "json-bigint": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", - "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "integrity": "sha512-u+c/u/F+JNPUekHCFyGVycRPyh9UHD5iUhSyIAn10kxbDTJxijwAbT6XHaONEOXuGGfmWUSroheXgHcml4gLgg==", "requires": { "bignumber.js": "^7.0.0" } @@ -3944,33 +3915,33 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha512-a3xHnILGMtk+hDOqNwHzF6e2fNbiMrXZvxKQiEv2MlgQP+pjIOzqAmKYD2mDpXYE/44M7g+n9p2bKkYWDUcXCQ==" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==" }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "integrity": "sha512-4Dj8Rf+fQ+/Pn7C5qeEX02op1WfOss3PKTE9Nsop3Dx+6UPxlm1dr/og7o2cRa5hNN07CACr4NFzRLtj/rjWog==", "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -3981,7 +3952,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" } } }, @@ -4023,7 +3994,7 @@ "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, "requires": { "prelude-ls": "~1.1.2", @@ -4033,12 +4004,12 @@ "line-reader": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/line-reader/-/line-reader-0.4.0.tgz", - "integrity": "sha1-F+RIGNoKwzVnW6MAlU+U72cOZv0=" + "integrity": "sha512-AYJ8g+eE7v+Ba4s/cuYqzuNulH/WbjdKQ55fvx8fNVn8WQzTpioY6vI1MoxTuMgcHYX3VlmZWbVvnkIqkyJbCA==" }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -4050,7 +4021,7 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true } } @@ -4058,7 +4029,7 @@ "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "requires": { "p-locate": "^2.0.0", @@ -4073,43 +4044,43 @@ "lodash.at": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", - "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=" + "integrity": "sha512-GOTh0SEp+Yosnlpjic+8cl2WM9MykorogkGA9xyIFkkObQ3H3kNZqZ+ohuq4K3FrSVo7hMcZBMataJemrxC3BA==" }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==" }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, "lodash.has": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", - "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=" + "integrity": "sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==" }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, "lodash.merge": { @@ -4121,12 +4092,12 @@ "lodash.pickby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=" + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==" }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "integrity": "sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg==", "dev": true }, "log-driver": { @@ -4159,7 +4130,7 @@ "bunyan": { "version": "1.8.12", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", - "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "integrity": "sha512-dmDUbGHeGcvCDLRFOscZkwx1ZO/aFz3bJOCi5nCgzdhFGPxwK+y5AcDBnqagNGlJZ7lje/l6JUEz9mQcutttdg==", "requires": { "dtrace-provider": "~0.8", "moment": "^2.10.6", @@ -4170,7 +4141,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "qs": { "version": "6.5.2", @@ -4229,7 +4200,7 @@ "loglevel-colored-level-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", "dev": true, "requires": { "chalk": "^1.1.3", @@ -4239,19 +4210,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -4264,7 +4235,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "requires": { "ansi-regex": "^2.0.0" @@ -4273,7 +4244,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true } } @@ -4292,15 +4263,23 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, "lsmod": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", - "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" + "integrity": "sha512-Y+6V75r+mGWzWEPr9h6PFmStielICu5JBHLUg18jCsD2VFmEfgHbq/EgnY4inElsUD9eKL9id1qp34w46rSIKQ==" }, "lynx": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", - "integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", + "integrity": "sha512-JI52N0NwK2b/Md0TFPdPtUBI46kjyJXF7+q08l2yvQ56q6QA8s7ZjZQQRoxFpS2jDXNf/B0p8ID+OIKcTsZwzw==", "requires": { "mersenne": "~0.0.3", "statsd-parser": "~0.0.4" @@ -4341,7 +4320,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memory-pager": { "version": "1.5.0", @@ -4352,13 +4331,13 @@ "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "merge-source-map": { "version": "1.1.0", @@ -4371,7 +4350,7 @@ "mersenne": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", - "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" + "integrity": "sha512-XoSUL+nF8hMTKGQxUs8r3Btdsf1yuKKBdCCGbh3YXgCXuVKishpZv1CNc385w9s8t4Ynwc5h61BwW/FCVulkbg==" }, "messageformat": { "version": "2.3.0", @@ -4399,12 +4378,12 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "metrics-sharelatex": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.6.1.tgz", - "integrity": "sha512-Fkzl3MDjhsFuCl2i3f0bAqyC9lp4MAt6/hF9HxI6CV5r42kCsbqkE81G61HySpna7hMXZ0cE8T+w+91nAMwCQQ==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.6.2.tgz", + "integrity": "sha512-bOLfkSCexiPgB96hdXhoOWyvvrwscgjeZPEqdcJ7BTGxY59anzvymNf5hTGJ1RtS4sblDKxITw3L5a+gYKhRYQ==", "requires": { "@google-cloud/debug-agent": "^3.0.0", "@google-cloud/profiler": "^0.2.3", @@ -4419,7 +4398,7 @@ "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + "integrity": "sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ==" } } }, @@ -4431,12 +4410,12 @@ "mime-db": { "version": "1.37.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha1-C2oM5v2+lXbiXx8tL96IMNwK0Ng=" + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" }, "mime-types": { "version": "2.1.21", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha1-KJlaoey3cHQv5q5+WPkYHHRLP5Y=", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", "requires": { "mime-db": "~1.37.0" } @@ -4459,12 +4438,12 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" }, "mkdirp": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + "integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==" }, "mocha": { "version": "7.1.1", @@ -4539,7 +4518,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "locate-path": { @@ -4599,7 +4578,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true }, "yargs": { @@ -4635,18 +4614,18 @@ "module-details-from-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s=", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", "optional": true }, "mongo-uri": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/mongo-uri/-/mongo-uri-0.1.2.tgz", - "integrity": "sha1-FzrwFAMzkALgq9C01nWYfTzc+Z4=" + "integrity": "sha512-FehPVi2Dv7VPvAkLnN9haM1aarj1E9w08rkn2MAbbQJF5EbcOckdOHRAD9T35yUkfLVcs0YzYluNX4/+G8HaIw==" }, "mongodb": { "version": "3.5.5", @@ -4698,7 +4677,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "mute-stream": { "version": "0.0.8", @@ -4709,7 +4688,7 @@ "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", "optional": true, "requires": { "mkdirp": "~0.5.1", @@ -4720,7 +4699,7 @@ "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", "optional": true, "requires": { "minimist": "0.0.8" @@ -4731,18 +4710,18 @@ "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha1-exqhk+mqhgV+PHu9CsRI53CSVVI=" + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "optional": true }, "negotiator": { @@ -4925,7 +4904,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-inspect": { @@ -5001,7 +4980,7 @@ "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "requires": { "ee-first": "1.1.1" } @@ -5009,7 +4988,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -5040,18 +5019,18 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==" }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, "p-limit": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha1-QXyZQeYCepq8ulCS3SkE4lW1+8I=", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "requires": { "p-try": "^2.0.0" } @@ -5059,7 +5038,7 @@ "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "requires": { "p-limit": "^1.1.0" @@ -5077,7 +5056,7 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true } } @@ -5085,7 +5064,7 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=" + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-hash": { "version": "3.0.0", @@ -5115,7 +5094,7 @@ "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", "dev": true, "requires": { "error-ex": "^1.2.0" @@ -5124,7 +5103,7 @@ "parse-mongo-url": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", - "integrity": "sha1-ZiON9fjnwMjKTNlw1KtqE3PrdbU=" + "integrity": "sha512-7bZUusQIrFLwvsLHBnCz2WKYQ5LKO/LwKPnvQxbMIh9gDx8H5ZsknRmLjZdn6GVdrgVOwqDrZKsY0qDLNmRgcw==" }, "parse-ms": { "version": "2.1.0", @@ -5139,29 +5118,29 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "1.8.0", @@ -5175,7 +5154,7 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", "dev": true } } @@ -5183,7 +5162,7 @@ "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", "dev": true, "requires": { "pify": "^2.0.0" @@ -5192,7 +5171,7 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true } } @@ -5200,13 +5179,13 @@ "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "integrity": "sha512-qZ181q3ICkag/+lv1X6frDUF84pqCm30qild3LGbD84n0AC75CYwnWsQRDlpz7zDkU5NVcmhHh4LjXK0goLYZA==", "dev": true }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "picomatch": { "version": "2.2.2", @@ -5222,7 +5201,7 @@ "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", "dev": true, "requires": { "find-up": "^2.1.0" @@ -5231,7 +5210,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true }, "prettier": { @@ -5282,13 +5261,13 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", "dev": true }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, "requires": { "restore-cursor": "^2.0.0" @@ -5368,7 +5347,7 @@ "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -5435,7 +5414,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "mimic-fn": { @@ -5456,7 +5435,7 @@ "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", "dev": true, "requires": { "minimist": "0.0.8" @@ -5465,13 +5444,13 @@ "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", "dev": true }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, "requires": { "mimic-fn": "^1.0.0" @@ -5486,7 +5465,7 @@ "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "requires": { "onetime": "^2.0.0", @@ -5506,7 +5485,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -5515,7 +5494,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true } } @@ -5566,13 +5545,13 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", "dev": true }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", "dev": true, "requires": { "restore-cursor": "^2.0.0" @@ -5660,7 +5639,7 @@ "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -5749,7 +5728,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "locate-path": { @@ -5779,7 +5758,7 @@ "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", "dev": true, "requires": { "minimist": "0.0.8" @@ -5788,13 +5767,13 @@ "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", "dev": true }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, "requires": { "mimic-fn": "^1.0.0" @@ -5818,7 +5797,7 @@ "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "requires": { "onetime": "^2.0.0", @@ -5838,7 +5817,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -5847,7 +5826,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true } } @@ -5874,7 +5853,7 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", "dev": true } } @@ -5890,7 +5869,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=" + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "progress": { "version": "2.0.3", @@ -5949,12 +5928,12 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha1-6aqG0BAbWxBcvpOsa3hM1UcnYYQ=" + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" }, "pump": { "version": "3.0.0", @@ -6001,12 +5980,12 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, "q": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", - "integrity": "sha1-I8BsRsgTKGFqrhaNPuI6Vr1D2vY=" + "integrity": "sha512-ZOxMuWPMJnsUdYhuQ9glpZwKhB4cm8ubYFy1nNCY8TkSAuZun5fd8jCDTlf2ykWnK8x9HGn1stNtLeG179DebQ==" }, "qs": { "version": "6.7.0", @@ -6016,7 +5995,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, "quick-lru": { "version": "4.0.1", @@ -6038,7 +6017,7 @@ "raven": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/raven/-/raven-1.1.3.tgz", - "integrity": "sha1-QnPBrm005CMPUbLAEEGjK5Iygio=", + "integrity": "sha512-RYov4wAaflZasWiCrZuizd3jNXxCOkW1WrXgWsGVb8kRpdHNZ+vPY27R6RhVtqzWp+DG9a5l6iP0QUPK4EgzaQ==", "requires": { "cookie": "0.3.1", "json-stringify-safe": "5.0.1", @@ -6050,12 +6029,12 @@ "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==" }, "uuid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", - "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" + "integrity": "sha512-rqE1LoOVLv3QrZMjb4NkF5UWlkurCfPyItVnFPNKDDGkHw4dQUdE4zMcLqx28+0Kcf3+bnUk4PisaiRJT4aiaQ==" } } }, @@ -6079,7 +6058,7 @@ "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", "dev": true, "requires": { "load-json-file": "^2.0.0", @@ -6090,7 +6069,7 @@ "read-pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", "dev": true, "requires": { "find-up": "^2.0.0", @@ -6100,7 +6079,7 @@ "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6123,7 +6102,7 @@ "redis": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/redis/-/redis-0.10.3.tgz", - "integrity": "sha1-iSf+IRDuOWF7zz/Te4nY4SORG7Y=" + "integrity": "sha512-oVblZDvWa0J03abo+nB4X7XlIlrfp7UF57fQmHe9X7aO5y/aUuxxXOyq8ZdjhMTnto3n9fQXX2flhXU7DP6RYQ==" }, "redis-commands": { "version": "1.5.0", @@ -6133,12 +6112,12 @@ "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" }, "redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "requires": { "redis-errors": "^1.0.0" } @@ -6146,7 +6125,7 @@ "redis-sentinel": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", - "integrity": "sha1-Vj3TQduZMgMfSX+v3Td+hkj/s+U=", + "integrity": "sha512-cKtLSUzDsKmsB50J1eIV/SH11DSMiHgsm/gDPRCU5lXz5OyTSuLKWg9oc8d5n74kZwtAyRkfJP0x8vYXvlPjFQ==", "requires": { "q": "0.9.2", "redis": "0.11.x" @@ -6155,7 +6134,7 @@ "redis": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", - "integrity": "sha1-/cAdSrTL5LO7LLKByP5WnDhX9XE=" + "integrity": "sha512-wkgzIZ9HuxJ6Sul1IW/6FG13Ecv6q8kmdHb5xo09Hu6bgWzz5qsnM06SVMpDxFNbyApaRjy8CwnmVaRMMhAMWg==" } } }, @@ -6182,7 +6161,7 @@ "coffee-script": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", - "integrity": "sha1-nJ8dK0pSoADe0Vtll5FwNkgmPB0=", + "integrity": "sha512-EvLTMcu9vR6G1yfnz75yrISvhq1eBPC+pZbQhHzTiC5vXgpYIrArxQc5tB+SYfBi3souVdSZ4AZzYxI72oLXUw==", "requires": { "mkdirp": "~0.3.5" } @@ -6195,7 +6174,7 @@ "underscore": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" + "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" } } }, @@ -6224,7 +6203,7 @@ "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "requires": { "es6-error": "^4.0.1" } @@ -6281,7 +6260,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "require-in-the-middle": { "version": "4.0.1", @@ -6306,7 +6285,7 @@ "require-like": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", "dev": true }, "require-main-filename": { @@ -6317,7 +6296,7 @@ "require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", "dev": true }, "require_optional": { @@ -6332,7 +6311,7 @@ "resolve": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha1-QBSHC6KWF2uGND1Qtg87UGCc4jI=", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", "requires": { "path-parse": "^1.0.6" } @@ -6340,7 +6319,7 @@ "resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==" }, "restore-cursor": { "version": "3.1.0", @@ -6369,7 +6348,7 @@ "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", "optional": true, "requires": { "glob": "^6.0.1" @@ -6378,7 +6357,7 @@ "glob": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", "optional": true, "requires": { "inflight": "^1.0.4", @@ -6391,7 +6370,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "optional": true, "requires": { "brace-expansion": "^1.1.7" @@ -6402,7 +6381,7 @@ "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "integrity": "sha512-Fx+QT3fGtS0jk8OvKyKgAB2YHPsrmqBRcMeTC5AZ+lp4vzXKPPrFSY3iLdgvjA3HVBkIvJeM6J80LRjx8bQwhA==", "dev": true, "requires": { "is-promise": "^2.1.0" @@ -6442,23 +6421,23 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-json-stringify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "integrity": "sha1-NW5EvJjx+TzkXfFLzXwBzahuCv0=", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sandboxed-module": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz", - "integrity": "sha1-x+VFkzm7y6KMUwPusz9ug4e/upY=", + "integrity": "sha512-wXiA6ULoGjCDwjn6evQF/Qi+oe77P+aCxizUktLBBKdqNbTxwec4GySJcS+O7iZFhme2ex04m+14KgknKKqFsw==", "dev": true, "requires": { "require-like": "0.1.2", @@ -6477,12 +6456,12 @@ "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=" + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "send": { "version": "0.17.1", @@ -6515,7 +6494,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } } @@ -6535,7 +6514,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "setprototypeof": { "version": "1.1.1", @@ -6545,7 +6524,7 @@ "settings-sharelatex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", - "integrity": "sha1-Tv4vUpPbjxwVlnEEx5BfqHD/mS0=", + "integrity": "sha512-f7D+0lnlohoteSn6IKTH72NE+JnAdMWTKwQglAuimZWTID2FRRItZSGeYMTRpvEnaQApkoVwRp//WRMsiddnqw==", "requires": { "coffee-script": "1.6.0" }, @@ -6553,14 +6532,14 @@ "coffee-script": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + "integrity": "sha512-Tx8itEfCsQp8RbLDFt7qwjqXycAx2g6SI7//4PPUR2j6meLmNifYm6zKrNDcU1+Q/GWRhjhEZk7DaLG1TfIzGA==" } } }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -6569,7 +6548,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true }, "shimmer": { @@ -6590,7 +6569,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==" }, "sinon": { "version": "9.0.1", @@ -6644,7 +6623,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true } } @@ -6666,7 +6645,7 @@ "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", "optional": true, "requires": { "memory-pager": "^1.0.2" @@ -6768,12 +6747,12 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "sshpk": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", - "integrity": "sha1-HUljovv/5YBQqpCEyiC+gXQcB94=", + "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -6789,7 +6768,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { "safer-buffer": "~2.1.0" } @@ -6797,14 +6776,14 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" } } }, "stack-trace": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==" }, "standard-as-callback": { "version": "2.0.1", @@ -6814,12 +6793,12 @@ "statsd-parser": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", - "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" + "integrity": "sha512-7XO+ur89EalMXXFQaydsczB8sclr5nDsNIoUu0IzJx1pIbHUhO3LtpSzBwetIuU9DyTLMiVaJBMtWS/Nb2KR4g==" }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" }, "stream-events": { "version": "1.0.5", @@ -6893,7 +6872,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" } @@ -6916,7 +6895,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" }, "strip-json-comments": { "version": "3.0.1", @@ -6927,7 +6906,7 @@ "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" }, "supports-color": { "version": "6.0.0", @@ -6977,7 +6956,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "lodash": { @@ -7002,7 +6981,7 @@ "tdigest": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", - "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", + "integrity": "sha512-CXcDY/NIgIbKZPx5H4JJNpq6JwJhU5Z4+yWj4ZghDc7/9nVajiRlPPyMXRePPPlBfcayUqtoCXjo7/Hm82ecUA==", "requires": { "bintrees": "1.0.1" } @@ -7061,7 +7040,7 @@ "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "requires": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -7097,7 +7076,7 @@ "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "requires": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -7114,12 +7093,12 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "requires": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -7140,13 +7119,13 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, "through2": { "version": "3.0.1", @@ -7179,17 +7158,17 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-mongodb-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", - "integrity": "sha1-NZbsdhOsmtO5ioncua77pWnNJ+s=" + "integrity": "sha512-vfXXcGYFP8+0L5IPOtUzzVIvPE/G3GN0TKa/PRBlzPqYyhm+UxhPmvv634EQgO4Ot8dHbBFihOslMJQclY8Z9A==" }, "to-no-case": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" + "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==" }, "to-regex-range": { "version": "5.0.1", @@ -7203,7 +7182,7 @@ "to-snake-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", - "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=", + "integrity": "sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==", "requires": { "to-space-case": "^1.0.0" } @@ -7211,7 +7190,7 @@ "to-space-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", + "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", "requires": { "to-no-case": "^1.0.0" } @@ -7246,7 +7225,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "requires": { "safe-buffer": "^5.0.1" } @@ -7254,12 +7233,12 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -7268,7 +7247,7 @@ "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "type-fest": { @@ -7315,12 +7294,12 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { "punycode": "^2.1.0" }, @@ -7328,14 +7307,14 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } }, "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -7344,17 +7323,17 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "v8-compile-cache": { "version": "2.1.0", @@ -7374,12 +7353,12 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -7389,7 +7368,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" } } }, @@ -7416,7 +7395,7 @@ "acorn-jsx": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "integrity": "sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ==", "dev": true, "requires": { "acorn": "^3.0.4" @@ -7425,7 +7404,7 @@ "acorn": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==", "dev": true } } @@ -7469,7 +7448,7 @@ "when": { "version": "3.7.8", "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=" + "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==" }, "which": { "version": "1.3.1", @@ -7482,7 +7461,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, "wide-align": { "version": "1.1.3", @@ -7496,13 +7475,13 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, "string-width": { @@ -7518,7 +7497,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -7550,7 +7529,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" }, "string-width": { "version": "3.1.0", @@ -7567,7 +7546,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write": { "version": "1.0.3", @@ -7581,7 +7560,7 @@ "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", "dev": true, "requires": { "minimist": "0.0.8" @@ -7611,7 +7590,7 @@ "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, "xregexp": { "version": "4.3.0", @@ -7665,7 +7644,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" }, "locate-path": { "version": "3.0.0", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 0bcb27b3c3..e2d2cf87fa 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -28,7 +28,7 @@ "heap": "^0.2.6", "line-reader": "^0.4.0", "logger-sharelatex": "^1.9.0", - "metrics-sharelatex": "^2.6.1", + "metrics-sharelatex": "^2.6.2", "mongo-uri": "^0.1.2", "mongojs": "3.1.0", "redis": "~0.10.1", From 5cb369cba8650a1ee6d148ad29dd6eeee71e12d5 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 31 Mar 2020 13:22:38 +0100 Subject: [PATCH 498/549] bump redis to 1.0.12 --- services/track-changes/package-lock.json | 27 ++++++------------------ services/track-changes/package.json | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 477d0d80cb..39ef59a0b9 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -3612,9 +3612,9 @@ } }, "ioredis": { - "version": "4.14.4", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.14.4.tgz", - "integrity": "sha512-9JAQi9Z0OioGNFKIgDKv6CpjNHaUX3CbkF02jDpPMe3+6v+TI47Ky4XvByeDV0wb7yrZyKCilW/6STIZs8GX3Q==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.16.1.tgz", + "integrity": "sha512-g76Mm9dE7BLuewncu1MimGZw5gDDjDwjoRony/VoSxSJEKAhuYncDEwYKYjtHi2NWsTNIB6XXRjE64uVa/wpKQ==", "requires": { "cluster-key-slot": "^1.1.0", "debug": "^4.1.1", @@ -6139,25 +6139,17 @@ } }, "redis-sharelatex": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.11.tgz", - "integrity": "sha512-rKXPVLmFC9ycpRc5e4rULOwi9DB0LqRcWEiUxQuJNSVgcqCxpGqVw+zwivo+grk3G2tGpduh3/8y+4KVHWOntw==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.12.tgz", + "integrity": "sha512-Z+LDGaRNgZ+NiDaCC/R0N3Uy6SCtbKXqiXlvCwAbIQRSZUc69OVx/cQ3i5qDF7zeERhh+pnTd+zGs8nVfa5p+Q==", "requires": { "async": "^2.5.0", "coffee-script": "1.8.0", - "ioredis": "~4.14.1", + "ioredis": "~4.16.1", "redis-sentinel": "0.1.1", "underscore": "1.7.0" }, "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, "coffee-script": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", @@ -6166,11 +6158,6 @@ "mkdirp": "~0.3.5" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, "underscore": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e2d2cf87fa..a87cb01e17 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -32,7 +32,7 @@ "mongo-uri": "^0.1.2", "mongojs": "3.1.0", "redis": "~0.10.1", - "redis-sharelatex": "^1.0.11", + "redis-sharelatex": "^1.0.12", "request": "~2.88.2", "requestretry": "^4.1.0", "s3-streams": "^0.4.0", From 98c1ffeffff7eeb066c08979e487210a826c00bf Mon Sep 17 00:00:00 2001 From: Ersun Warncke Date: Wed, 18 Mar 2020 11:35:47 -0400 Subject: [PATCH 499/549] [misc] fix express deprecations Co-Authored-By: Jakob Ackermann --- services/track-changes/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index ed49af171e..e2a37b79d1 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -109,7 +109,7 @@ app.get('/health_check', HttpController.healthCheck) app.use(function(error, req, res, next) { logger.error({ err: error, req }, 'an internal error occured') - return res.send(500) + return res.sendStatus(500) }) const port = From 6ee56438aaceedf16b4884864fc530c8beca1bca Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 Jun 2020 10:27:34 +0100 Subject: [PATCH 500/549] update to node 10.21.0 --- services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 2 +- services/track-changes/docker-compose.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index 5b7269c0a9..b61c07ffdd 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -10.19.0 +10.21.0 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index c014691791..7b47fb9ee2 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -2,7 +2,7 @@ # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -FROM node:10.19.0 as base +FROM node:10.21.0 as base WORKDIR /app diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 88bd224842..27652dedab 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.3" services: test_unit: - image: node:10.19.0 + image: node:10.21.0 volumes: - .:/app working_dir: /app @@ -17,7 +17,7 @@ services: user: node test_acceptance: - image: node:10.19.0 + image: node:10.21.0 volumes: - .:/app working_dir: /app From bb3e562d8d4c0bb3f8cc5a1f99c62365e5825b62 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 3 Jun 2020 11:12:38 +0100 Subject: [PATCH 501/549] update buildscript.txt to node 10.21.0 --- services/track-changes/buildscript.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 977522bfba..1551e8fbf3 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -5,6 +5,6 @@ track-changes --env-add=AWS_BUCKET=bucket --env-pass-through= --language=es ---node-version=10.19.0 +--node-version=10.21.0 --public-repo=True --script-version=2.0.0 From 9baf54ac00fd581c20bf6e9770fc2d6ed92afa07 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Wed, 3 Jun 2020 11:32:07 +0100 Subject: [PATCH 502/549] [misc] make: ignore a lint/format task failure --- services/track-changes/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index e1b4843fea..b01ebbcbe0 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -17,13 +17,13 @@ clean: docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) format: - $(DOCKER_COMPOSE) run --rm test_unit npm run format + $(DOCKER_COMPOSE) run --rm test_unit npm run format || true format_fix: $(DOCKER_COMPOSE) run --rm test_unit npm run format:fix lint: - $(DOCKER_COMPOSE) run --rm test_unit npm run lint + $(DOCKER_COMPOSE) run --rm test_unit npm run lint || true test: format lint test_unit test_acceptance From d3a7ffd1a90cafe588b4595ab3576868a6c08168 Mon Sep 17 00:00:00 2001 From: Tim Alby Date: Thu, 4 Jun 2020 10:24:21 +0200 Subject: [PATCH 503/549] fix formatting --- services/track-changes/app.js | 20 +- services/track-changes/app.js.map | 10 + .../track-changes/app/js/DiffGenerator.js | 2 +- .../track-changes/app/js/DiffGenerator.js.map | 10 + services/track-changes/app/js/DiffManager.js | 20 +- .../track-changes/app/js/DiffManager.js.map | 10 + .../app/js/DocumentUpdaterManager.js | 8 +- .../app/js/DocumentUpdaterManager.js.map | 10 + .../track-changes/app/js/HealthChecker.js | 12 +- .../track-changes/app/js/HealthChecker.js.map | 10 + .../track-changes/app/js/HttpController.js | 81 +- .../app/js/HttpController.js.map | 10 + services/track-changes/app/js/LockManager.js | 26 +- .../track-changes/app/js/LockManager.js.map | 10 + services/track-changes/app/js/MongoAWS.js | 38 +- services/track-changes/app/js/MongoAWS.js.map | 10 + services/track-changes/app/js/MongoManager.js | 23 +- .../track-changes/app/js/MongoManager.js.map | 10 + services/track-changes/app/js/PackManager.js | 208 +- .../track-changes/app/js/PackManager.js.map | 10 + services/track-changes/app/js/PackWorker.js | 36 +- .../track-changes/app/js/PackWorker.js.map | 10 + .../track-changes/app/js/ProjectIterator.js | 2 +- .../app/js/ProjectIterator.js.map | 10 + services/track-changes/app/js/RedisManager.js | 26 +- .../track-changes/app/js/RedisManager.js.map | 10 + .../track-changes/app/js/RestoreManager.js | 6 +- .../app/js/RestoreManager.js.map | 10 + .../track-changes/app/js/UpdateCompressor.js | 17 +- .../app/js/UpdateCompressor.js.map | 10 + .../track-changes/app/js/UpdateTrimmer.js | 14 +- .../track-changes/app/js/UpdateTrimmer.js.map | 10 + .../track-changes/app/js/UpdatesManager.js | 134 +- .../app/js/UpdatesManager.js.map | 10 + .../track-changes/app/js/WebApiManager.js | 12 +- .../track-changes/app/js/WebApiManager.js.map | 10 + services/track-changes/app/js/mongojs.js.map | 10 + .../track-changes/app/lib/diff_match_patch.js | 2195 +++++++++-------- .../track-changes/config/settings.defaults.js | 14 +- .../acceptance/js/AppendingUpdatesTests.js | 102 +- .../acceptance/js/ArchivingUpdatesTests.js | 64 +- .../acceptance/js/FlushingUpdatesTests.js | 50 +- .../test/acceptance/js/GettingADiffTests.js | 12 +- .../test/acceptance/js/GettingUpdatesTests.js | 21 +- .../test/acceptance/js/LockManagerTests.js | 18 +- .../test/acceptance/js/RestoringVersions.js | 12 +- .../acceptance/js/helpers/MockDocStoreApi.js | 6 +- .../js/helpers/MockDocUpdaterApi.js | 41 +- .../test/acceptance/js/helpers/MockWebApi.js | 8 +- .../acceptance/js/helpers/TrackChangesApp.js | 6 +- .../js/helpers/TrackChangesClient.js | 36 +- .../js/DiffGenerator/DiffGeneratorTests.js | 118 +- .../unit/js/DiffManager/DiffManagerTests.js | 113 +- .../test/unit/js/DocArchive/MongoAWS.js | 16 +- .../DocumentUpdaterManagerTests.js | 50 +- .../js/HttpController/HttpControllerTests.js | 44 +- .../unit/js/LockManager/LockManagerTests.js | 122 +- .../unit/js/MongoManager/MongoManagerTests.js | 68 +- .../unit/js/PackManager/PackManagerTests.js | 180 +- .../unit/js/RedisManager/RedisManagerTests.js | 40 +- .../js/RestoreManager/RestoreManagerTests.js | 14 +- .../UpdateCompressor/UpdateCompressorTests.js | 78 +- .../js/UpdateTrimmer/UpdateTrimmerTests.js | 64 +- .../js/UpdatesManager/UpdatesManagerTests.js | 218 +- .../js/WebApiManager/WebApiManagerTests.js | 54 +- 65 files changed, 2496 insertions(+), 2143 deletions(-) create mode 100644 services/track-changes/app.js.map create mode 100644 services/track-changes/app/js/DiffGenerator.js.map create mode 100644 services/track-changes/app/js/DiffManager.js.map create mode 100644 services/track-changes/app/js/DocumentUpdaterManager.js.map create mode 100644 services/track-changes/app/js/HealthChecker.js.map create mode 100644 services/track-changes/app/js/HttpController.js.map create mode 100644 services/track-changes/app/js/LockManager.js.map create mode 100644 services/track-changes/app/js/MongoAWS.js.map create mode 100644 services/track-changes/app/js/MongoManager.js.map create mode 100644 services/track-changes/app/js/PackManager.js.map create mode 100644 services/track-changes/app/js/PackWorker.js.map create mode 100644 services/track-changes/app/js/ProjectIterator.js.map create mode 100644 services/track-changes/app/js/RedisManager.js.map create mode 100644 services/track-changes/app/js/RestoreManager.js.map create mode 100644 services/track-changes/app/js/UpdateCompressor.js.map create mode 100644 services/track-changes/app/js/UpdateTrimmer.js.map create mode 100644 services/track-changes/app/js/UpdatesManager.js.map create mode 100644 services/track-changes/app/js/WebApiManager.js.map create mode 100644 services/track-changes/app/js/mongojs.js.map diff --git a/services/track-changes/app.js b/services/track-changes/app.js index ed49af171e..8d0890e988 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -16,9 +16,9 @@ if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) { } // log updates as truncated strings -const truncateFn = updates => +const truncateFn = (updates) => JSON.parse( - JSON.stringify(updates, function(key, value) { + JSON.stringify(updates, function (key, value) { let len if (typeof value === 'string' && (len = value.length) > 80) { return ( @@ -46,7 +46,7 @@ const child_process = require('child_process') const HttpController = require('./app/js/HttpController') const express = require('express') -const bodyParser = require('body-parser'); +const bodyParser = require('body-parser') const app = express() @@ -79,7 +79,7 @@ app.post('/check/dangling', HttpController.checkDanglingUpdates) let packWorker = null // use a single packing worker -app.post('/pack', function(req, res, next) { +app.post('/pack', function (req, res, next) { if (packWorker != null) { return res.send('pack already running') } else { @@ -89,7 +89,7 @@ app.post('/pack', function(req, res, next) { req.query.delay || 1000, req.query.timeout || 30 * 60 * 1000 ]) - packWorker.on('exit', function(code, signal) { + packWorker.on('exit', function (code, signal) { logger.log({ code, signal }, 'history auto pack exited') return (packWorker = null) }) @@ -99,7 +99,7 @@ app.post('/pack', function(req, res, next) { app.get('/status', (req, res, next) => res.send('track-changes is alive')) -app.get('/oops', function(req, res, next) { +app.get('/oops', function (req, res, next) { throw new Error('dummy test error') }) @@ -107,7 +107,7 @@ app.get('/check_lock', HttpController.checkLock) app.get('/health_check', HttpController.healthCheck) -app.use(function(error, req, res, next) { +app.use(function (error, req, res, next) { logger.error({ err: error, req }, 'an internal error occured') return res.send(500) }) @@ -115,17 +115,17 @@ app.use(function(error, req, res, next) { const port = __guard__( Settings.internal != null ? Settings.internal.trackchanges : undefined, - x => x.port + (x) => x.port ) || 3015 const host = __guard__( Settings.internal != null ? Settings.internal.trackchanges : undefined, - x1 => x1.host + (x1) => x1.host ) || 'localhost' if (!module.parent) { // Called directly - app.listen(port, host, function(error) { + app.listen(port, host, function (error) { if (error != null) { return logger.error( { err: error }, diff --git a/services/track-changes/app.js.map b/services/track-changes/app.js.map new file mode 100644 index 0000000000..c408f74302 --- /dev/null +++ b/services/track-changes/app.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "app.js", + "sourceRoot": "", + "sources": [ + "app.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,OAAO,CAAC,UAAR,CAAmB,eAAnB;;EACA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,kBAAA,GAAqB,MAAM,CAAC,UAAP,CAAkB,eAAlB,CAAkC,CAAC;;EAExD,IAAG,4DAAH;IACC,MAAM,CAAC,wBAAP,CAAgC,QAAQ,CAAC,MAAM,CAAC,GAAhD,EADD;;;EAIA,UAAA,GAAa,SAAC,OAAD;WACX,IAAI,CAAC,KAAL,CACC,IAAI,CAAC,SAAL,CAAe,OAAf,EAAwB,SAAC,GAAD,EAAM,KAAN;AACvB,UAAA;MAAA,IAAG,OAAO,KAAP,KAAgB,QAAhB,IAA4B,CAAC,GAAA,GAAM,KAAK,CAAC,MAAb,CAAA,GAAuB,EAAtD;AACC,eAAO,KAAK,CAAC,MAAN,CAAa,CAAb,EAAe,EAAf,CAAA,GAAqB,CAAA,wBAAA,GAAyB,GAAzB,GAA6B,gBAA7B,CAArB,GAAoE,KAAK,CAAC,MAAN,CAAa,CAAC,EAAd,EAD5E;OAAA,MAAA;AAGC,eAAO,MAHR;;IADuB,CAAxB,CADD;EADW;;EASb,kBAAkB,CAAC,cAAnB,CAAkC;IACjC,SAAA,EAAW,UADsB;IAEjC,UAAA,EAAY,UAFqB;IAGjC,UAAA,EAAY,UAHqB;IAIjC,UAAA,EAAY,UAJqB;GAAlC;;EAOA,IAAA,GAAO,OAAA,CAAQ,MAAR;;EAEP,OAAO,CAAC,MAAM,CAAC,OAAf,CAAuB,MAAvB;;EAEA,aAAA,GAAgB,OAAA,CAAQ,eAAR;;EAEhB,cAAA,GAAiB,OAAA,CAAQ,yBAAR;;EACjB,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,GAAA,GAAM,OAAA,CAAA;;EAEN,GAAG,CAAC,GAAJ,CAAQ,OAAO,CAAC,IAAI,CAAC,OAAb,CAAqB,MAArB,CAAR;;EAEA,OAAO,CAAC,kBAAR,CAA2B,GAA3B;;EAEA,GAAG,CAAC,IAAJ,CAAS,wCAAT,EAAmD,cAAc,CAAC,QAAlE;;EAEA,GAAG,CAAC,GAAJ,CAAQ,uCAAR,EAAiD,cAAc,CAAC,OAAhE;;EAEA,GAAG,CAAC,GAAJ,CAAQ,wCAAR,EAAkD,cAAc,CAAC,QAAjE;;EAEA,GAAG,CAAC,GAAJ,CAAQ,8BAAR,EAAwC,cAAc,CAAC,UAAvD;;EAEA,GAAG,CAAC,IAAJ,CAAS,4BAAT,EAAuC,cAAc,CAAC,YAAtD;;EAEA,GAAG,CAAC,IAAJ,CAAS,2DAAT,EAAsE,cAAc,CAAC,OAArF;;EAEA,GAAG,CAAC,IAAJ,CAAU,uCAAV,EAAmD,cAAc,CAAC,cAAlE;;EACA,GAAG,CAAC,IAAJ,CAAU,uCAAV,EAAmD,cAAc,CAAC,cAAlE;;EAEA,GAAG,CAAC,IAAJ,CAAS,YAAT,EAAuB,cAAc,CAAC,QAAtC;;EACA,GAAG,CAAC,IAAJ,CAAS,iBAAT,EAA4B,cAAc,CAAC,oBAA3C;;EAEA,UAAA,GAAa;;EAEb,GAAG,CAAC,IAAJ,CAAS,OAAT,EAAkB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;IACjB,IAAG,kBAAH;aACC,GAAG,CAAC,IAAJ,CAAS,sBAAT,EADD;KAAA,MAAA;MAGC,MAAM,CAAC,GAAP,CAAW,cAAX;MACA,UAAA,GAAa,aAAa,CAAC,IAAd,CAAmB,SAAA,GAAY,uBAA/B,EACZ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAV,IAAmB,IAApB,EAA0B,GAAG,CAAC,KAAK,CAAC,KAAV,IAAmB,IAA7C,EAAmD,GAAG,CAAC,KAAK,CAAC,OAAV,IAAqB,EAAA,GAAG,EAAH,GAAM,IAA9E,CADY;MAEb,UAAU,CAAC,EAAX,CAAc,MAAd,EAAsB,SAAC,IAAD,EAAO,MAAP;QACrB,MAAM,CAAC,GAAP,CAAW;UAAC,MAAA,IAAD;UAAO,QAAA,MAAP;SAAX,EAA2B,0BAA3B;eACA,UAAA,GAAa;MAFQ,CAAtB;aAGA,GAAG,CAAC,IAAJ,CAAS,cAAT,EATD;;EADiB,CAAlB;;EAYA,GAAG,CAAC,GAAJ,CAAQ,SAAR,EAAmB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;WAClB,GAAG,CAAC,IAAJ,CAAS,wBAAT;EADkB,CAAnB;;EAGA,GAAG,CAAC,GAAJ,CAAQ,OAAR,EAAiB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AAChB,UAAM,IAAI,KAAJ,CAAU,kBAAV;EADU,CAAjB;;EAGA,GAAG,CAAC,GAAJ,CAAQ,aAAR,EAAuB,cAAc,CAAC,SAAtC;;EAEA,GAAG,CAAC,GAAJ,CAAQ,eAAR,EAA0B,cAAc,CAAC,WAAzC;;EAEA,QAAA,GAAW,OAAA,CAAQ,aAAR;;EACX,GAAG,CAAC,GAAJ,CAAQ,UAAR,EAAoB,SAAC,GAAD,EAAM,GAAN;AACnB,QAAA;IAAA,IAAA,GAAO,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,IAAV,IAAkB,MAA3B;IACP,QAAQ,CAAC,cAAT,CAAwB,MAAxB;WACA,UAAA,CAAW,SAAA;AACV,UAAA;MAAA,OAAA,GAAU,QAAQ,CAAC,aAAT,CAAuB,MAAvB;aACV,GAAG,CAAC,IAAJ,CAAS,OAAT;IAFU,CAAX,EAGE,IAHF;EAHmB,CAApB;;EAQA,GAAG,CAAC,GAAJ,CAAQ,SAAC,KAAD,EAAQ,GAAR,EAAa,GAAb,EAAkB,IAAlB;IACP,MAAM,CAAC,KAAP,CAAa;MAAA,GAAA,EAAK,KAAL;MAAY,GAAA,EAAK,GAAjB;KAAb,EAAmC,2BAAnC;WACA,GAAG,CAAC,IAAJ,CAAS,GAAT;EAFO,CAAR;;EAIA,IAAA,kFAAsC,CAAE,uBAAjC,IAAyC;;EAChD,IAAA,kFAAsC,CAAE,uBAAjC,IAAyC;;EAEhD,IAAG,CAAC,MAAM,CAAC,MAAX;IACC,GAAG,CAAC,MAAJ,CAAW,IAAX,EAAiB,IAAjB,EAAuB,SAAC,KAAD;MACtB,IAAG,aAAH;eACC,MAAM,CAAC,KAAP,CAAa;UAAA,GAAA,EAAK,KAAL;SAAb,EAAyB,sCAAzB,EADD;OAAA,MAAA;eAGC,MAAM,CAAC,IAAP,CAAY,yCAAA,GAA0C,IAA1C,GAA+C,GAA/C,GAAkD,IAA9D,EAHD;;IADsB,CAAvB,EADD;;;EAOA,MAAM,CAAC,OAAP,GAAiB;AAzGjB" +} \ No newline at end of file diff --git a/services/track-changes/app/js/DiffGenerator.js b/services/track-changes/app/js/DiffGenerator.js index 57e998d318..ebc6a9fcb1 100644 --- a/services/track-changes/app/js/DiffGenerator.js +++ b/services/track-changes/app/js/DiffGenerator.js @@ -13,7 +13,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let DiffGenerator -var ConsistencyError = function(message) { +var ConsistencyError = function (message) { const error = new Error(message) error.name = 'ConsistencyError' error.__proto__ = ConsistencyError.prototype diff --git a/services/track-changes/app/js/DiffGenerator.js.map b/services/track-changes/app/js/DiffGenerator.js.map new file mode 100644 index 0000000000..2394cfe7df --- /dev/null +++ b/services/track-changes/app/js/DiffGenerator.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "DiffGenerator.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/DiffGenerator.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,gBAAA,GAAmB,SAAC,OAAD;AAClB,QAAA;IAAA,KAAA,GAAQ,IAAI,KAAJ,CAAU,OAAV;IACR,KAAK,CAAC,IAAN,GAAa;IACb,KAAK,CAAC,SAAN,GAAkB,gBAAgB,CAAC;AACnC,WAAO;EAJW;;EAKnB,gBAAgB,CAAC,SAAS,CAAC,SAA3B,GAAuC,KAAK,CAAC;;EAE7C,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,aAAA,GAChB;IAAA,gBAAA,EAAkB,gBAAlB;IAEA,YAAA,EAAc,SAAC,OAAD,EAAU,MAAV;AACb,UAAA;AAAA;AAAA,WAAA,2CAAA;;YAAkC,EAAE,CAAC,MAAH,KAAe;AAChD;YACC,OAAA,GAAU,aAAa,CAAC,QAAd,CAAuB,OAAvB,EAAgC,EAAhC,EADX;WAAA,cAAA;YAEM;YACL,IAAG,CAAA,YAAa,gBAAb,IAAkC,CAAA,CAAA,GAAI,MAAM,CAAC,EAAE,CAAC,MAAV,GAAmB,CAAvB,CAArC;cAGC,MAAM,CAAC,KAAP,CAAa;gBAAC,GAAA,EAAK,CAAN;gBAAS,QAAA,MAAT;gBAAiB,EAAA,EAAI,IAAI,CAAC,SAAL,CAAe,EAAf,CAArB;eAAb,EAAuD,sBAAvD;cACA,EAAE,CAAC,MAAH,GAAY,KAJb;aAAA,MAAA;AAMC,oBAAM,EANP;aAHD;;;AADD;AAWA,aAAO;IAZM,CAFd;IAgBA,QAAA,EAAU,SAAC,OAAD,EAAU,EAAV;AACT,UAAA;MAAA,IAAG,YAAH;QAMC,CAAA,GAAI,EAAE,CAAC;QACP,KAAA,GAAQ,OAAO,CAAC,MAAR,GAAiB,EAAE,CAAC,CAAC,CAAC;QAC9B,IAAG,CAAA,GAAI,KAAP;UACC,MAAM,CAAC,IAAP,CAAY;YAAC,OAAA,KAAD;YAAQ,GAAA,CAAR;WAAZ,EAAwB,uCAAxB;UACA,CAAA,GAAI,MAFL;;QAIA,eAAA,GAAkB,OAAO,CAAC,KAAR,CAAc,CAAd,EAAiB,CAAA,GAAI,EAAE,CAAC,CAAC,CAAC,MAA1B;QAClB,IAAG,EAAE,CAAC,CAAH,KAAQ,eAAX;AACC,gBAAM,IAAI,gBAAJ,CACL,qBAAA,GAAsB,EAAE,CAAC,CAAzB,GAA2B,yCAA3B,GAAoE,eAApE,GAAoF,GAD/E,EADP;;AAKA,eAAO,OAAO,CAAC,KAAR,CAAc,CAAd,EAAiB,CAAjB,CAAA,GAAsB,OAAO,CAAC,KAAR,CAAc,CAAA,GAAI,EAAE,CAAC,CAAC,CAAC,MAAvB,EAlB9B;OAAA,MAoBK,IAAG,YAAH;AACJ,eAAO,OAAO,CAAC,KAAR,CAAc,CAAd,EAAiB,EAAE,CAAC,CAApB,CAAA,GAAyB,EAAE,CAAC,CAA5B,GAAgC,OAAO,CAAC,KAAR,CAAc,EAAE,CAAC,CAAjB,EADnC;OAAA,MAAA;AAIJ,eAAO,QAJH;;IArBI,CAhBV;IA2CA,aAAA,EAAe,SAAC,OAAD,EAAU,OAAV;AACd,UAAA;AAAA;AAAA,WAAA,qCAAA;;AACC;UACC,OAAA,GAAU,aAAa,CAAC,YAAd,CAA2B,OAA3B,EAAoC,MAApC,EADX;SAAA,cAAA;UAEM;UACL,CAAC,CAAC,gBAAF,GAAqB;AACrB,gBAAM,EAJP;;AADD;AAMA,aAAO;IAPO,CA3Cf;IAoDA,SAAA,EAAW,SAAC,cAAD,EAAiB,OAAjB;AACV,UAAA;MAAA,IAAA,GAAO;QAAE;UAAA,CAAA,EAAG,cAAH;SAAF;;AACP,WAAA,yCAAA;;QACC,IAAA,GAAO,aAAa,CAAC,iBAAd,CAAgC,IAAhC,EAAsC,MAAtC;AADR;MAEA,IAAA,GAAO,aAAa,CAAC,YAAd,CAA2B,IAA3B;AACP,aAAO;IALG,CApDX;IA2DA,YAAA,EAAc,SAAC,IAAD;AACb,UAAA;MAAA,OAAA,GAAU;AACV,WAAA,sCAAA;;QACC,QAAA,GAAW,OAAQ,CAAA,OAAO,CAAC,MAAR,GAAiB,CAAjB;QACnB,IAAG,kBAAA,IAAc,6DAAd,IAAuC,2DAA1C;UACC,IAAG,oBAAA,IAAgB,gBAAhB,IAA4B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAnB,KAAyB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAvE;YACC,QAAQ,CAAC,CAAT,IAAc,IAAI,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,QAAd,GAAyB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,QAAvB,EAAiC,IAAI,CAAC,IAAI,CAAC,QAA3C;YACzB,QAAQ,CAAC,IAAI,CAAC,MAAd,GAAuB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,MAAvB,EAA+B,IAAI,CAAC,IAAI,CAAC,MAAzC,EAHxB;WAAA,MAIK,IAAG,oBAAA,IAAgB,gBAAhB,IAA4B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAnB,KAAyB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAvE;YACJ,QAAQ,CAAC,CAAT,IAAc,IAAI,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,QAAd,GAAyB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,QAAvB,EAAiC,IAAI,CAAC,IAAI,CAAC,QAA3C;YACzB,QAAQ,CAAC,IAAI,CAAC,MAAd,GAAuB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,MAAvB,EAA+B,IAAI,CAAC,IAAI,CAAC,MAAzC,EAHnB;WAAA,MAAA;YAKJ,OAAO,CAAC,IAAR,CAAa,IAAb,EALI;WALN;SAAA,MAAA;UAYC,OAAO,CAAC,IAAR,CAAa,IAAb,EAZD;;AAFD;AAeA,aAAO;IAjBM,CA3Dd;IA8EA,aAAA,EAAe,SAAC,IAAD,EAAO,EAAP,EAAW,IAAX;AACd,UAAA;MAAA,QAAA,GAAW;MAEX,aAAA,GAAgB,IAAI,CAAC,KAAL,CAAA;MAChB,MAAgC,aAAa,CAAC,gBAAd,CAA+B,aAA/B,EAA8C,EAAE,CAAC,CAAjD,CAAhC,EAAC,+BAAD,EAAe;MACf,OAAA,GAAU;MAEV,IAAG,YAAH;QACC,OAAO,CAAC,IAAR,CACC;UAAA,CAAA,EAAG,EAAE,CAAC,CAAN;UACA,IAAA,EAAM,IADN;SADD,EADD;OAAA,MAIK,IAAG,YAAH;QACJ,OAAgC,aAAa,CAAC,8BAAd,CAA6C,aAA7C,EAA4D,EAA5D,EAAgE,IAAhE,CAAhC,EAAC,gCAAD,EAAe;QACf,OAAO,CAAC,IAAR,gBAAa,YAAb,EAFI;;MAIL,OAAO,CAAC,IAAR,gBAAa,aAAb;AAEA,aAAO;IAjBO,CA9Ef;IAiGA,iBAAA,EAAmB,SAAC,IAAD,EAAO,MAAP;AAClB,UAAA;AAAA;AAAA,WAAA,qCAAA;;YAAyB,EAAE,CAAC,MAAH,KAAe;UACvC,IAAA,GAAO,aAAa,CAAC,aAAd,CAA4B,IAA5B,EAAkC,EAAlC,EAAsC,MAAM,CAAC,IAA7C;;AADR;AAEA,aAAO;IAHW,CAjGnB;IAsGA,gBAAA,EAAkB,SAAC,aAAD,EAAgB,WAAhB;AACjB,UAAA;MAAA,YAAA,GAAe;MACf,QAAA,GAAW;AACX,aAAM,IAAA,GAAO,aAAa,CAAC,KAAd,CAAA,CAAb;QACC,MAAA,GAAS,aAAa,CAAC,oBAAd,CAAmC,IAAnC;QACT,IAAG,cAAH;UACC,YAAY,CAAC,IAAb,CAAkB,IAAlB,EADD;SAAA,MAEK,IAAG,QAAA,GAAW,MAAX,IAAqB,WAAxB;UACJ,UAAA,GAAa,WAAA,GAAc;UAC3B,IAAG,UAAA,GAAa,CAAhB;YACC,YAAY,CAAC,IAAb,CAAkB,aAAa,CAAC,UAAd,CAAyB,IAAzB,EAA+B,CAA/B,EAAkC,UAAlC,CAAlB,EADD;;UAEA,IAAG,UAAA,GAAa,MAAhB;YACC,aAAa,CAAC,OAAd,CAAsB,aAAa,CAAC,UAAd,CAAyB,IAAzB,EAA+B,UAA/B,CAAtB,EADD;;AAEA,gBANI;SAAA,MAAA;UAQJ,QAAA,IAAY;UACZ,YAAY,CAAC,IAAb,CAAkB,IAAlB,EATI;;MAJN;AAeA,aAAO;QACN,YAAA,EAAc,YADR;QAEN,aAAA,EAAe,aAFT;;IAlBU,CAtGlB;IA6HA,8BAAA,EAAgC,SAAC,aAAD,EAAgB,QAAhB,EAA0B,IAA1B;AAC/B,UAAA;MAAA,YAAA,GAAe;MACf,WAAA,GAAc;AACd,aAAM,WAAA,IAAgB,aAAa,CAAC,MAAd,GAAuB,CAA7C;QACC,MAAwC,aAAa,CAAC,mBAAd,CAAkC,aAAlC,EAAiD,WAAjD,EAA8D,IAA9D,CAAxC,EAAC,qBAAD,EAAU,iCAAV,EAAyB;QACzB,IAA6B,eAA7B;UAAA,YAAY,CAAC,IAAb,CAAkB,OAAlB,EAAA;;MAFD;AAGA,aAAO;QACN,YAAA,EAAc,YADR;QAEN,aAAA,EAAe,aAFT;;IANwB,CA7HhC;IAwIA,mBAAA,EAAqB,SAAC,aAAD,EAAgB,EAAhB,EAAoB,IAApB;AACpB,UAAA;MAAA,IAAA,GAAO,aAAa,CAAC,KAAd,CAAA;MACP,UAAA,GAAa,aAAa,CAAC,oBAAd,CAAmC,IAAnC;MAEb,IAAG,cAAH;QAEC,WAAA,GAAc;QACd,OAAA,GAAU,KAHX;OAAA,MAKK,IAAG,UAAA,GAAa,EAAE,CAAC,CAAC,CAAC,MAArB;QAEJ,aAAA,GAAgB,aAAa,CAAC,UAAd,CAAyB,IAAzB,EAA+B,EAAE,CAAC,CAAC,CAAC,MAApC;QAChB,aAAa,CAAC,OAAd,CAAsB,aAAtB;QAEA,cAAA,GAAiB,aAAa,CAAC,iBAAd,CAAgC,IAAhC,CAAqC,CAAC,KAAtC,CAA4C,CAA5C,EAA+C,EAAE,CAAC,CAAC,CAAC,MAApD;QACjB,IAAG,cAAA,KAAkB,EAAE,CAAC,CAAxB;AACC,gBAAM,IAAI,gBAAJ,CAAqB,oBAAA,GAAqB,cAArB,GAAoC,gCAApC,GAAoE,EAAE,CAAC,CAAvE,GAAyE,GAA9F,EADP;;QAGA,IAAG,cAAH;UACC,OAAA,GACC;YAAA,CAAA,EAAG,EAAE,CAAC,CAAN;YACA,IAAA,EAAM,IADN;YAFF;SAAA,MAIK,IAAG,cAAH;UACJ,OAAA,GAAU,KADN;;QAGL,WAAA,GAAc,KAhBV;OAAA,MAkBA,IAAG,UAAA,KAAc,EAAE,CAAC,CAAC,CAAC,MAAtB;QAGJ,cAAA,GAAiB,aAAa,CAAC,iBAAd,CAAgC,IAAhC;QACjB,IAAG,cAAA,KAAkB,EAAE,CAAC,CAAxB;AACC,gBAAM,IAAI,gBAAJ,CAAqB,oBAAA,GAAqB,cAArB,GAAoC,gCAApC,GAAoE,EAAE,CAAC,CAAvE,GAAyE,GAA9F,EADP;;QAGA,IAAG,cAAH;UACC,OAAA,GACC;YAAA,CAAA,EAAG,EAAE,CAAC,CAAN;YACA,IAAA,EAAM,IADN;YAFF;SAAA,MAIK,IAAG,cAAH;UACJ,OAAA,GAAU,KADN;;QAGL,WAAA,GAAc,KAdV;OAAA,MAgBA,IAAG,UAAA,GAAa,EAAE,CAAC,CAAC,CAAC,MAArB;QAGJ,cAAA,GAAiB,aAAa,CAAC,iBAAd,CAAgC,IAAhC;QACjB,SAAA,GAAY,EAAE,CAAC,CAAC,CAAC,KAAL,CAAW,CAAX,EAAc,cAAc,CAAC,MAA7B;QACZ,IAAG,cAAA,KAAkB,SAArB;AACC,gBAAM,IAAI,gBAAJ,CAAqB,oBAAA,GAAqB,cAArB,GAAoC,gCAApC,GAAoE,SAApE,GAA8E,GAAnG,EADP;;QAGA,IAAG,IAAI,CAAC,CAAR;UACC,OAAA,GACC;YAAA,CAAA,EAAG,IAAI,CAAC,CAAR;YACA,IAAA,EAAM,IADN;YAFF;SAAA,MAIK,IAAG,cAAH;UACJ,OAAA,GAAU,KADN;;QAGL,WAAA,GACC;UAAA,CAAA,EAAG,EAAE,CAAC,CAAN;UAAS,CAAA,EAAG,EAAE,CAAC,CAAC,CAAC,KAAL,CAAW,aAAa,CAAC,oBAAd,CAAmC,IAAnC,CAAX,CAAZ;UAhBG;;AAkBL,aAAO;QACN,OAAA,EAAS,OADH;QAEN,aAAA,EAAe,aAFT;QAGN,WAAA,EAAa,WAHP;;IA7Da,CAxIrB;IA2MA,UAAA,EAAY,SAAC,QAAD,EAAW,IAAX,EAAiB,EAAjB;AACX,UAAA;MAAA,IAAG,kBAAH;QACC,IAAA,GAAO;UAAE,CAAA,EAAG,QAAQ,CAAC,CAAC,CAAC,KAAX,CAAiB,IAAjB,EAAuB,EAAvB,CAAL;UADR;OAAA,MAEK,IAAG,kBAAH;QACJ,IAAA,GAAO;UAAE,CAAA,EAAG,QAAQ,CAAC,CAAC,CAAC,KAAX,CAAiB,IAAjB,EAAuB,EAAvB,CAAL;UADH;;MAEL,IAAG,qBAAH;QACC,IAAI,CAAC,IAAL,GAAY,QAAQ,CAAC,KADtB;;AAEA,aAAO;IAPI,CA3MZ;IAoNA,oBAAA,EAAsB,SAAC,IAAD;aACrB,CAAC,IAAI,CAAC,CAAL,IAAU,IAAI,CAAC,CAAf,IAAoB,IAAI,CAAC,CAAzB,IAA8B,EAA/B,CAAkC,CAAC;IADd,CApNtB;IAuNA,iBAAA,EAAmB,SAAC,IAAD;aAClB,IAAI,CAAC,CAAL,IAAU,IAAI,CAAC,CAAf,IAAoB,IAAI,CAAC,CAAzB,IAA8B;IADZ,CAvNnB;;AAVD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/DiffManager.js b/services/track-changes/app/js/DiffManager.js index 0b8fa39de6..a33ed07c2d 100644 --- a/services/track-changes/app/js/DiffManager.js +++ b/services/track-changes/app/js/DiffManager.js @@ -22,9 +22,9 @@ module.exports = DiffManager = { // Get updates last, since then they must be ahead and it // might be possible to rewind to the same version as the doc. if (callback == null) { - callback = function(error, content, version, updates) {} + callback = function (error, content, version, updates) {} } - return DocumentUpdaterManager.getDocument(project_id, doc_id, function( + return DocumentUpdaterManager.getDocument(project_id, doc_id, function ( error, content, version @@ -40,7 +40,7 @@ module.exports = DiffManager = { project_id, doc_id, { from: fromVersion }, - function(error, updates) { + function (error, updates) { if (error != null) { return callback(error) } @@ -52,13 +52,13 @@ module.exports = DiffManager = { getDiff(project_id, doc_id, fromVersion, toVersion, callback) { if (callback == null) { - callback = function(error, diff) {} + callback = function (error, diff) {} } return DiffManager.getDocumentBeforeVersion( project_id, doc_id, fromVersion, - function(error, startingContent, updates) { + function (error, startingContent, updates) { let diff if (error != null) { if (error.message === 'broken-history') { @@ -94,10 +94,10 @@ module.exports = DiffManager = { // versions. let retry if (_callback == null) { - _callback = function(error, document, rewoundUpdates) {} + _callback = function (error, document, rewoundUpdates) {} } let retries = 3 - const callback = function(error, ...args) { + const callback = function (error, ...args) { if (error != null) { if (error.retry && retries > 0) { logger.warn( @@ -113,7 +113,7 @@ module.exports = DiffManager = { } } - return (retry = function() { + return (retry = function () { retries-- return DiffManager._tryGetDocumentBeforeVersion( project_id, @@ -126,7 +126,7 @@ module.exports = DiffManager = { _tryGetDocumentBeforeVersion(project_id, doc_id, version, callback) { if (callback == null) { - callback = function(error, document, rewoundUpdates) {} + callback = function (error, document, rewoundUpdates) {} } logger.log( { project_id, doc_id, version }, @@ -136,7 +136,7 @@ module.exports = DiffManager = { project_id, doc_id, version, - function(error, content, version, updates) { + function (error, content, version, updates) { let startingContent if (error != null) { return callback(error) diff --git a/services/track-changes/app/js/DiffManager.js.map b/services/track-changes/app/js/DiffManager.js.map new file mode 100644 index 0000000000..9cd8d0895a --- /dev/null +++ b/services/track-changes/app/js/DiffManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "DiffManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/DiffManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA,0EAAA;IAAA;;EAAA,cAAA,GAAiB,OAAA,CAAQ,kBAAR;;EACjB,sBAAA,GAAyB,OAAA,CAAQ,0BAAR;;EACzB,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,WAAA,GAChB;IAAA,sBAAA,EAAwB,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,QAAlC;;QAAkC,WAAW,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB,EAA0B,OAA1B,GAAA;;aAGpE,sBAAsB,CAAC,WAAvB,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB;QACtD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,IAAI,mBAAJ;AACC,iBAAO,QAAA,CAAS,IAAT,EAAe,OAAf,EAAwB,OAAxB,EAAiC,EAAjC,EADR;;eAEA,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,MAArD,EAA6D;UAAA,IAAA,EAAM,WAAN;SAA7D,EAAgF,SAAC,KAAD,EAAQ,OAAR;UAC/E,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT,EAAe,OAAf,EAAwB,OAAxB,EAAiC,OAAjC;QAF+E,CAAhF;MAJsD,CAAvD;IAHuB,CAAxB;IAWA,OAAA,EAAS,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,SAAlC,EAA6C,QAA7C;;QAA6C,WAAW,SAAC,KAAD,EAAQ,IAAR,GAAA;;aAChE,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,WAAzD,EAAsE,SAAC,KAAD,EAAQ,eAAR,EAAyB,OAAzB;AACrE,YAAA;QAAA,IAAG,aAAH;UACC,IAAG,KAAK,CAAC,OAAN,KAAiB,gBAApB;AACC,mBAAO,QAAA,CAAS,IAAT,EAAe,qBAAf,EADR;WAAA,MAAA;AAGC,mBAAO,QAAA,CAAS,KAAT,EAHR;WADD;;QAMA,cAAA,GAAiB;AACjB;AAAA,aAAA,qCAAA;;UACC,IAAG,MAAM,CAAC,CAAP,IAAY,SAAf;YACC,cAAc,CAAC,IAAf,CAAoB,MAApB,EADD;;AADD;AAIA;UACC,IAAA,GAAO,aAAa,CAAC,SAAd,CAAwB,eAAxB,EAAyC,cAAzC,EADR;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,CAAT,EAHR;;eAKA,QAAA,CAAS,IAAT,EAAe,IAAf;MAjBqE,CAAtE;IADQ,CAXT;IA+BA,wBAAA,EAA0B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,SAA9B;AAMzB,UAAA;;QANuD,YAAY,SAAC,KAAD,EAAQ,QAAR,EAAkB,cAAlB,GAAA;;MAMnE,OAAA,GAAU;MACV,QAAA,GAAW,SAAA;AACV,YAAA;QADW,sBAAO;QAClB,IAAG,aAAH;UACC,IAAG,KAAK,CAAC,KAAN,IAAgB,OAAA,GAAU,CAA7B;YACC,MAAM,CAAC,IAAP,CAAY;cAAC,OAAA,KAAD;cAAQ,YAAA,UAAR;cAAoB,QAAA,MAApB;cAA4B,SAAA,OAA5B;cAAqC,SAAA,OAArC;aAAZ,EAA2D,mCAA3D;mBACA,KAAA,CAAA,EAFD;WAAA,MAAA;mBAIC,SAAA,CAAU,KAAV,EAJD;WADD;SAAA,MAAA;iBAOC,SAAA,aAAU,CAAA,IAAM,SAAA,WAAA,IAAA,CAAA,CAAhB,EAPD;;MADU;aAUR,CAAA,KAAA,GAAQ,SAAA;QACV,OAAA;eACA,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,OAA7D,EAAsE,QAAtE;MAFU,CAAR,CAAH,CAAA;IAjByB,CA/B1B;IAoDA,4BAAA,EAA8B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;;QAA8B,WAAW,SAAC,KAAD,EAAQ,QAAR,EAAkB,cAAlB,GAAA;;MACtE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;QAAwC,OAAA,EAAS,OAAjD;OAAX,EAAqE,iCAArE;aACA,WAAW,CAAC,sBAAZ,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,OAAvD,EAAgE,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB,EAA0B,OAA1B;AAC/D,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;AAGA,aAAA,yCAAA;;cAAsB,CAAC,CAAC;AACvB,mBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT;;AADR;AAIA,gDAAgB,CAAE,WAAZ,IAAiB,OAAvB;UACC,OAAO,CAAC,KAAR,CAAA;QADD;QAGA,UAAA,GAAa,OAAQ,CAAA,CAAA;QACrB,IAAG,oBAAA,IAAgB,UAAU,CAAC,CAAX,KAAgB,OAAA,GAAU,CAA7C;UACC,KAAA,GAAQ,IAAI,KAAJ,CAAU,yBAAA,GAA0B,UAAU,CAAC,CAArC,GAAuC,gCAAvC,GAAuE,OAAjF;UACR,KAAK,CAAC,KAAN,GAAc;AACd,iBAAO,QAAA,CAAS,KAAT,EAHR;;QAKA,MAAM,CAAC,GAAP,CAAW;UAAC,UAAA,EAAY,OAAb;UAAsB,iBAAA,uBAAmB,UAAU,CAAE,UAArD;UAAwD,WAAA,EAAa,OAAO,CAAC,MAA7E;SAAX,EAAiG,mBAAjG;QAEA,UAAA,GAAa,OAAO,CAAC,KAAR,CAAA,CAAe,CAAC,OAAhB,CAAA;AAEb;UACC,eAAA,GAAkB,aAAa,CAAC,aAAd,CAA4B,OAA5B,EAAqC,UAArC,EADnB;SAAA,cAAA;UAGM;AACL,iBAAO,QAAA,CAAS,CAAT,EAJR;;eAMA,QAAA,CAAS,IAAT,EAAe,eAAf,EAAgC,UAAhC;MA3B+D,CAAhE;IAF6B,CApD9B;;AAND" +} \ No newline at end of file diff --git a/services/track-changes/app/js/DocumentUpdaterManager.js b/services/track-changes/app/js/DocumentUpdaterManager.js index 5e83cdd9ab..29247b1664 100644 --- a/services/track-changes/app/js/DocumentUpdaterManager.js +++ b/services/track-changes/app/js/DocumentUpdaterManager.js @@ -19,11 +19,11 @@ const Settings = require('settings-sharelatex') module.exports = DocumentUpdaterManager = { getDocument(project_id, doc_id, callback) { if (callback == null) { - callback = function(error, content, version) {} + callback = function (error, content, version) {} } const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}` logger.log({ project_id, doc_id }, 'getting doc from document updater') - return request.get(url, function(error, res, body) { + return request.get(url, function (error, res, body) { if (error != null) { return callback(error) } @@ -54,7 +54,7 @@ module.exports = DocumentUpdaterManager = { setDocument(project_id, doc_id, content, user_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } const url = `${Settings.apis.documentupdater.url}/project/${project_id}/doc/${doc_id}` logger.log({ project_id, doc_id }, 'setting doc in document updater') @@ -68,7 +68,7 @@ module.exports = DocumentUpdaterManager = { undoing: true } }, - function(error, res, body) { + function (error, res, body) { if (error != null) { return callback(error) } diff --git a/services/track-changes/app/js/DocumentUpdaterManager.js.map b/services/track-changes/app/js/DocumentUpdaterManager.js.map new file mode 100644 index 0000000000..50a1d7f569 --- /dev/null +++ b/services/track-changes/app/js/DocumentUpdaterManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "DocumentUpdaterManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/DocumentUpdaterManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EAEX,MAAM,CAAC,OAAP,GAAiB,sBAAA,GAChB;IAAA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;AACZ,UAAA;;QADiC,WAAW,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB,GAAA;;MAC5C,GAAA,GAAS,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,GAA/B,GAAmC,WAAnC,GAA8C,UAA9C,GAAyD,OAAzD,GAAgE;MACxE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAW,UAAX;QAAuB,MAAA,EAAQ,MAA/B;OAAX,EAAkD,mCAAlD;aACA,OAAO,CAAC,GAAR,CAAY,GAAZ,EAAiB,SAAC,KAAD,EAAQ,GAAR,EAAa,IAAb;QAChB,IAAG,aAAH;AACC,iBAAO,QAAA,CAAS,KAAT,EADR;;QAEA,IAAG,GAAG,CAAC,UAAJ,IAAkB,GAAlB,IAA0B,GAAG,CAAC,UAAJ,GAAiB,GAA9C;AACC;YACC,IAAA,GAAO,IAAI,CAAC,KAAL,CAAW,IAAX,EADR;WAAA,cAAA;YAEM;AACL,mBAAO,QAAA,CAAS,KAAT,EAHR;;UAIA,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,OAAA,EAAS,IAAI,CAAC,OAAnC;WAAX,EAAwD,+BAAxD;iBACA,QAAA,CAAS,IAAT,EAAe,IAAI,CAAC,KAAK,CAAC,IAAX,CAAgB,IAAhB,CAAf,EAAsC,IAAI,CAAC,OAA3C,EAND;SAAA,MAAA;UAQC,KAAA,GAAQ,IAAI,KAAJ,CAAU,kDAAA,GAAmD,GAAG,CAAC,UAAjE;UACR,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAW,UAAvB;YAAmC,MAAA,EAAO,MAA1C;YAAkD,GAAA,EAAK,GAAvD;WAAb,EAAyE,6BAAzE;iBACA,QAAA,CAAS,KAAT,EAVD;;MAHgB,CAAjB;IAHY,CAAb;IAkBA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,OAA9B,EAAuC,QAAvC;AACZ,UAAA;;QADmD,WAAW,SAAC,KAAD,GAAA;;MAC9D,GAAA,GAAS,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,GAA/B,GAAmC,WAAnC,GAA8C,UAA9C,GAAyD,OAAzD,GAAgE;MACxE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAW,UAAX;QAAuB,MAAA,EAAQ,MAA/B;OAAX,EAAkD,iCAAlD;aACA,OAAO,CAAC,IAAR,CAAa;QACZ,GAAA,EAAK,GADO;QAEZ,IAAA,EACC;UAAA,KAAA,EAAO,OAAO,CAAC,KAAR,CAAc,IAAd,CAAP;UACA,MAAA,EAAQ,SADR;UAEA,OAAA,EAAS,OAFT;UAGA,OAAA,EAAS,IAHT;SAHW;OAAb,EAOG,SAAC,KAAD,EAAQ,GAAR,EAAa,IAAb;QACF,IAAG,aAAH;AACC,iBAAO,QAAA,CAAS,KAAT,EADR;;QAEA,IAAG,GAAG,CAAC,UAAJ,IAAkB,GAAlB,IAA0B,GAAG,CAAC,UAAJ,GAAiB,GAA9C;iBACC,QAAA,CAAS,IAAT,EADD;SAAA,MAAA;UAGC,KAAA,GAAQ,IAAI,KAAJ,CAAU,kDAAA,GAAmD,GAAG,CAAC,UAAjE;UACR,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAW,UAAvB;YAAmC,MAAA,EAAO,MAA1C;YAAkD,GAAA,EAAK,GAAvD;WAAb,EAAyE,6BAAzE;iBACA,QAAA,CAAS,KAAT,EALD;;MAHE,CAPH;IAHY,CAlBb;;AALD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js index 7d02157fe8..a4331b4eac 100644 --- a/services/track-changes/app/js/HealthChecker.js +++ b/services/track-changes/app/js/HealthChecker.js @@ -24,10 +24,10 @@ module.exports = { const url = `http://localhost:${port}/project/${project_id}` logger.log({ project_id }, 'running health check') const jobs = [ - cb => + (cb) => request.get( { url: `http://localhost:${port}/check_lock`, timeout: 3000 }, - function(err, res, body) { + function (err, res, body) { if (err != null) { logger.err( { err, project_id }, @@ -41,8 +41,8 @@ module.exports = { } } ), - cb => - request.post({ url: `${url}/flush`, timeout: 10000 }, function( + (cb) => + request.post({ url: `${url}/flush`, timeout: 10000 }, function ( err, res, body @@ -56,8 +56,8 @@ module.exports = { return cb() } }), - cb => - request.get({ url: `${url}/updates`, timeout: 10000 }, function( + (cb) => + request.get({ url: `${url}/updates`, timeout: 10000 }, function ( err, res, body diff --git a/services/track-changes/app/js/HealthChecker.js.map b/services/track-changes/app/js/HealthChecker.js.map new file mode 100644 index 0000000000..aeb4de5ac2 --- /dev/null +++ b/services/track-changes/app/js/HealthChecker.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "HealthChecker.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/HealthChecker.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,SAAR,CAAkB,CAAC;;EAC9B,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,IAAA,GAAO,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;;EACtC,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,WAAA,GAAc,OAAA,CAAQ,eAAR;;EAEd,MAAM,CAAC,OAAP,GACC;IAAA,KAAA,EAAQ,SAAC,QAAD;AACP,UAAA;MAAA,UAAA,GAAa,QAAA,CAAS,QAAQ,CAAC,YAAY,CAAC,WAAW,CAAC,UAA3C;MACb,GAAA,GAAM,mBAAA,GAAoB,IAApB,GAAyB,WAAzB,GAAoC;MAC1C,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAW,UAAX;OAAX,EAAkC,sBAAlC;MACA,IAAA,GAAO;QACN,SAAC,EAAD;iBACC,OAAO,CAAC,GAAR,CAAY;YAAC,GAAA,EAAI,mBAAA,GAAoB,IAApB,GAAyB,aAA9B;YAA4C,OAAA,EAAQ,IAApD;WAAZ,EAAuE,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;YACtE,IAAG,WAAH;cACC,MAAM,CAAC,GAAP,CAAW;gBAAA,GAAA,EAAI,GAAJ;gBAAS,UAAA,EAAW,UAApB;eAAX,EAA2C,sCAA3C;qBACA,EAAA,CAAG,GAAH,EAFD;aAAA,MAGK,mBAAG,GAAG,CAAE,oBAAL,KAAmB,GAAtB;qBACJ,EAAA,CAAG,4BAAA,GAA6B,GAAG,CAAC,UAApC,EADI;aAAA,MAAA;qBAGJ,EAAA,CAAA,EAHI;;UAJiE,CAAvE;QADD,CADM,EAUN,SAAC,EAAD;iBACC,OAAO,CAAC,IAAR,CAAa;YAAC,GAAA,EAAO,GAAD,GAAK,QAAZ;YAAqB,OAAA,EAAQ,KAA7B;WAAb,EAAkD,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;YACjD,IAAG,WAAH;cACC,MAAM,CAAC,GAAP,CAAW;gBAAA,GAAA,EAAI,GAAJ;gBAAS,UAAA,EAAW,UAApB;eAAX,EAA2C,iCAA3C;qBACA,EAAA,CAAG,GAAH,EAFD;aAAA,MAGK,mBAAG,GAAG,CAAE,oBAAL,KAAmB,GAAtB;qBACJ,EAAA,CAAG,4BAAA,GAA6B,GAAG,CAAC,UAApC,EADI;aAAA,MAAA;qBAGJ,EAAA,CAAA,EAHI;;UAJ4C,CAAlD;QADD,CAVM,EAmBN,SAAC,EAAD;iBACC,OAAO,CAAC,GAAR,CAAY;YAAC,GAAA,EAAO,GAAD,GAAK,UAAZ;YAAuB,OAAA,EAAQ,KAA/B;WAAZ,EAAmD,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;YAClD,IAAG,WAAH;cACC,MAAM,CAAC,GAAP,CAAW;gBAAA,GAAA,EAAI,GAAJ;gBAAS,UAAA,EAAW,UAApB;eAAX,EAA2C,wCAA3C;qBACA,EAAA,CAAG,GAAH,EAFD;aAAA,MAGK,mBAAG,GAAG,CAAE,oBAAL,KAAmB,GAAtB;qBACJ,EAAA,CAAG,4BAAA,GAA6B,GAAG,CAAC,UAApC,EADI;aAAA,MAAA;qBAGJ,EAAA,CAAA,EAHI;;UAJ6C,CAAnD;QADD,CAnBM;;aA6BP,KAAK,CAAC,MAAN,CAAa,IAAb,EAAmB,QAAnB;IAjCO,CAAR;IAmCA,SAAA,EAAW,SAAC,QAAD;aACV,WAAW,CAAC,WAAZ,CAAwB,QAAxB;IADU,CAnCX;;AATD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js index 662ca49f0d..c167bae1c9 100644 --- a/services/track-changes/app/js/HttpController.js +++ b/services/track-changes/app/js/HttpController.js @@ -24,7 +24,7 @@ const _ = require('underscore') module.exports = HttpController = { flushDoc(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } const { doc_id } = req.params const { project_id } = req.params @@ -32,7 +32,7 @@ module.exports = HttpController = { return UpdatesManager.processUncompressedUpdatesWithLock( project_id, doc_id, - function(error) { + function (error) { if (error != null) { return next(error) } @@ -43,13 +43,13 @@ module.exports = HttpController = { flushProject(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } const { project_id } = req.params logger.log({ project_id }, 'compressing project history') return UpdatesManager.processUncompressedUpdatesForProject( project_id, - function(error) { + function (error) { if (error != null) { return next(error) } @@ -61,11 +61,11 @@ module.exports = HttpController = { flushAll(req, res, next) { // limit on projects to flush or -1 for all (default) if (next == null) { - next = function(error) {} + next = function (error) {} } const limit = req.query.limit != null ? parseInt(req.query.limit, 10) : -1 logger.log({ limit }, 'flushing all projects') - return UpdatesManager.flushAll(limit, function(error, result) { + return UpdatesManager.flushAll(limit, function (error, result) { if (error != null) { return next(error) } @@ -92,10 +92,10 @@ module.exports = HttpController = { checkDanglingUpdates(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } logger.log('checking dangling updates') - return UpdatesManager.getDanglingUpdates(function(error, result) { + return UpdatesManager.getDanglingUpdates(function (error, result) { if (error != null) { return next(error) } @@ -110,39 +110,40 @@ module.exports = HttpController = { checkDoc(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } const { doc_id } = req.params const { project_id } = req.params logger.log({ project_id, doc_id }, 'checking doc history') - return DiffManager.getDocumentBeforeVersion(project_id, doc_id, 1, function( - error, - document, - rewoundUpdates - ) { - if (error != null) { - return next(error) - } - const broken = [] - for (const update of Array.from(rewoundUpdates)) { - for (const op of Array.from(update.op)) { - if (op.broken === true) { - broken.push(op) + return DiffManager.getDocumentBeforeVersion( + project_id, + doc_id, + 1, + function (error, document, rewoundUpdates) { + if (error != null) { + return next(error) + } + const broken = [] + for (const update of Array.from(rewoundUpdates)) { + for (const op of Array.from(update.op)) { + if (op.broken === true) { + broken.push(op) + } } } + if (broken.length > 0) { + return res.send(broken) + } else { + return res.sendStatus(204) + } } - if (broken.length > 0) { - return res.send(broken) - } else { - return res.sendStatus(204) - } - }) + ) }, getDiff(req, res, next) { let from, to if (next == null) { - next = function(error) {} + next = function (error) {} } const { doc_id } = req.params const { project_id } = req.params @@ -159,7 +160,7 @@ module.exports = HttpController = { } logger.log({ project_id, doc_id, from, to }, 'getting diff') - return DiffManager.getDiff(project_id, doc_id, from, to, function( + return DiffManager.getDiff(project_id, doc_id, from, to, function ( error, diff ) { @@ -173,7 +174,7 @@ module.exports = HttpController = { getUpdates(req, res, next) { let before, min_count if (next == null) { - next = function(error) {} + next = function (error) {} } const { project_id } = req.params @@ -187,7 +188,7 @@ module.exports = HttpController = { return UpdatesManager.getSummarizedProjectUpdates( project_id, { before, min_count }, - function(error, updates, nextBeforeTimestamp) { + function (error, updates, nextBeforeTimestamp) { if (error != null) { return next(error) } @@ -201,7 +202,7 @@ module.exports = HttpController = { restore(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } let { doc_id, project_id, version } = req.params const user_id = req.headers['x-user-id'] @@ -211,7 +212,7 @@ module.exports = HttpController = { doc_id, version, user_id, - function(error) { + function (error) { if (error != null) { return next(error) } @@ -222,12 +223,12 @@ module.exports = HttpController = { pushDocHistory(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } const { project_id } = req.params const { doc_id } = req.params logger.log({ project_id, doc_id }, 'pushing all finalised changes to s3') - return PackManager.pushOldPacks(project_id, doc_id, function(error) { + return PackManager.pushOldPacks(project_id, doc_id, function (error) { if (error != null) { return next(error) } @@ -237,12 +238,12 @@ module.exports = HttpController = { pullDocHistory(req, res, next) { if (next == null) { - next = function(error) {} + next = function (error) {} } const { project_id } = req.params const { doc_id } = req.params logger.log({ project_id, doc_id }, 'pulling all packs from s3') - return PackManager.pullOldPacks(project_id, doc_id, function(error) { + return PackManager.pullOldPacks(project_id, doc_id, function (error) { if (error != null) { return next(error) } @@ -251,7 +252,7 @@ module.exports = HttpController = { }, healthCheck(req, res) { - return HealthChecker.check(function(err) { + return HealthChecker.check(function (err) { if (err != null) { logger.err({ err }, 'error performing health check') return res.sendStatus(500) @@ -262,7 +263,7 @@ module.exports = HttpController = { }, checkLock(req, res) { - return HealthChecker.checkLock(function(err) { + return HealthChecker.checkLock(function (err) { if (err != null) { logger.err({ err }, 'error performing lock check') return res.sendStatus(500) diff --git a/services/track-changes/app/js/HttpController.js.map b/services/track-changes/app/js/HttpController.js.map new file mode 100644 index 0000000000..b2d95f4b7b --- /dev/null +++ b/services/track-changes/app/js/HttpController.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "HttpController.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/HttpController.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,cAAA,GAAiB,OAAA,CAAQ,kBAAR;;EACjB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,cAAA,GAAiB,OAAA,CAAQ,kBAAR;;EACjB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,CAAA,GAAI,OAAA,CAAQ,YAAR;;EAEJ,MAAM,CAAC,OAAP,GAAiB,cAAA,GAChB;IAAA,QAAA,EAAU,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACT,UAAA;;QADoB,OAAO,SAAC,KAAD,GAAA;;MAC3B,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;OAAX,EAAmD,yBAAnD;aACA,cAAc,CAAC,kCAAf,CAAkD,UAAlD,EAA8D,MAA9D,EAAsE,SAAC,KAAD;QACrE,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAFqE,CAAtE;IAJS,CAAV;IAQA,YAAA,EAAc,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACb,UAAA;;QADwB,OAAO,SAAC,KAAD,GAAA;;MAC/B,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;OAAX,EAAmC,6BAAnC;aACA,cAAc,CAAC,oCAAf,CAAoD,UAApD,EAAgE,SAAC,KAAD;QAC/D,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF+D,CAAhE;IAHa,CARd;IAeA,QAAA,EAAU,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AAET,UAAA;;QAFoB,OAAO,SAAC,KAAD,GAAA;;MAE3B,KAAA,GAAW,uBAAH,GAAyB,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,KAAnB,EAA0B,EAA1B,CAAzB,GAA4D,CAAC;MACrE,MAAM,CAAC,GAAP,CAAW;QAAC,KAAA,EAAO,KAAR;OAAX,EAA2B,uBAA3B;aACA,cAAc,CAAC,QAAf,CAAwB,KAAxB,EAA+B,SAAC,KAAD,EAAQ,MAAR;AAC9B,YAAA;QAAA,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;QACC,sBAAD,EAAS,4BAAT,EAAoB;QACpB,MAAA,GAAY,SAAS,CAAC,MAAX,GAAkB,cAAlB,GAAgC,MAAM,CAAC,MAAvC,GAA8C;QACzD,IAAG,KAAA,KAAS,CAAZ;iBACC,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAwB,MAAD,GAAQ,kBAAR,GAAyB,CAAC,GAAG,CAAC,IAAJ,CAAS,IAAT,CAAD,CAAzB,GAAyC,IAAhE,EADD;SAAA,MAEK,IAAG,MAAM,CAAC,MAAP,GAAgB,CAAnB;UACJ,MAAM,CAAC,GAAP,CAAW;YAAC,MAAA,EAAQ,MAAT;YAAiB,SAAA,EAAW,SAA5B;WAAX,EAAmD,yBAAnD;iBACA,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAwB,MAAD,GAAQ,sBAAR,GAA6B,CAAC,MAAM,CAAC,IAAP,CAAY,IAAZ,CAAD,CAA7B,GAAgD,IAAvE,EAFI;SAAA,MAAA;iBAIJ,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAwB,MAAD,GAAQ,YAAR,GAAoB,SAAS,CAAC,MAA9B,GAAqC,eAArC,GAAoD,GAAG,CAAC,MAAxD,GAA+D,IAAtF,EAJI;;MANyB,CAA/B;IAJS,CAfV;IA+BA,oBAAA,EAAsB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;;QAAW,OAAO,SAAC,KAAD,GAAA;;MACvC,MAAM,CAAC,GAAP,CAAW,2BAAX;aACA,cAAc,CAAC,kBAAf,CAAkC,SAAC,KAAD,EAAQ,MAAR;QACjC,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;QACA,IAAG,MAAM,CAAC,MAAP,GAAgB,CAAnB;UACC,MAAM,CAAC,GAAP,CAAW;YAAC,QAAA,EAAU,MAAX;WAAX,EAA+B,wBAA/B;iBACA,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAqB,qBAAA,GAAqB,CAAC,MAAM,CAAC,IAAP,CAAY,IAAZ,CAAD,CAArB,GAAwC,IAA7D,EAFD;SAAA,MAAA;iBAIC,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAqB,6BAArB,EAJD;;MAFiC,CAAlC;IAFqB,CA/BtB;IAyCA,QAAA,EAAU,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACT,UAAA;;QADoB,OAAO,SAAC,KAAD,GAAA;;MAC3B,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;OAAX,EAAmD,sBAAnD;aACA,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,CAAzD,EAA4D,SAAC,KAAD,EAAQ,QAAR,EAAkB,cAAlB;AAC3D,YAAA;QAAA,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;QACA,MAAA,GAAS;AACT,aAAA,gDAAA;;AACC;AAAA,eAAA,uCAAA;;gBAAyB,EAAE,CAAC,MAAH,KAAa;cACrC,MAAM,CAAC,IAAP,CAAY,EAAZ;;AADD;AADD;QAGA,IAAG,MAAM,CAAC,MAAP,GAAgB,CAAnB;iBACC,GAAG,CAAC,IAAJ,CAAS,MAAT,EADD;SAAA,MAAA;iBAGC,GAAG,CAAC,IAAJ,CAAS,GAAT,EAHD;;MAN2D,CAA5D;IAJS,CAzCV;IAwDA,OAAA,EAAS,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACR,UAAA;;QADmB,OAAO,SAAC,KAAD,GAAA;;MAC1B,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MAExB,IAAG,sBAAH;QACC,IAAA,GAAO,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,IAAnB,EAAyB,EAAzB,EADR;OAAA,MAAA;QAGC,IAAA,GAAO,KAHR;;MAIA,IAAG,oBAAH;QACC,EAAA,GAAK,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,EAAnB,EAAuB,EAAvB,EADN;OAAA,MAAA;QAGC,EAAA,GAAK,KAHN;;MAKA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,MAAA,IAArB;QAA2B,IAAA,EAA3B;OAAX,EAA2C,cAA3C;aACA,WAAW,CAAC,OAAZ,CAAoB,UAApB,EAAgC,MAAhC,EAAwC,IAAxC,EAA8C,EAA9C,EAAkD,SAAC,KAAD,EAAQ,IAAR;QACjD,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS;UAAC,IAAA,EAAM,IAAP;SAAT;MAFiD,CAAlD;IAdQ,CAxDT;IA0EA,UAAA,EAAY,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACX,UAAA;;QADsB,OAAO,SAAC,KAAD,GAAA;;MAC7B,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MAExB,IAAG,wBAAH;QACC,MAAA,GAAS,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,MAAnB,EAA2B,EAA3B,EADV;;MAEA,IAAG,2BAAH;QACC,SAAA,GAAY,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,SAAnB,EAA8B,EAA9B,EADb;;aAGA,cAAc,CAAC,2BAAf,CAA2C,UAA3C,EAAuD;QAAA,MAAA,EAAQ,MAAR;QAAgB,SAAA,EAAW,SAA3B;OAAvD,EAA6F,SAAC,KAAD,EAAQ,OAAR,EAAiB,mBAAjB;QAC5F,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS;UACR,OAAA,EAAS,OADD;UAER,mBAAA,EAAqB,mBAFb;SAAT;MAF4F,CAA7F;IARW,CA1EZ;IAyFA,OAAA,EAAS,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACR,UAAA;;QADmB,OAAO,SAAC,KAAD,GAAA;;MAC1B,MAAgC,GAAG,CAAC,MAApC,EAAC,mBAAD,EAAS,2BAAT,EAAqB;MACrB,OAAA,GAAU,GAAG,CAAC,OAAQ,CAAA,WAAA;MACtB,OAAA,GAAU,QAAA,CAAS,OAAT,EAAkB,EAAlB;aACV,cAAc,CAAC,sBAAf,CAAsC,UAAtC,EAAkD,MAAlD,EAA0D,OAA1D,EAAmE,OAAnE,EAA4E,SAAC,KAAD;QAC3E,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF2E,CAA5E;IAJQ,CAzFT;IAiGA,cAAA,EAAgB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACf,UAAA;;QAD0B,OAAO,SAAC,KAAD,GAAA;;MACjC,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,qCAAjC;aACA,WAAW,CAAC,YAAZ,CAAyB,UAAzB,EAAqC,MAArC,EAA6C,SAAC,KAAD;QAC5C,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF4C,CAA7C;IAJe,CAjGhB;IAyGA,cAAA,EAAgB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACf,UAAA;;QAD0B,OAAO,SAAC,KAAD,GAAA;;MACjC,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,2BAAjC;aACA,WAAW,CAAC,YAAZ,CAAyB,UAAzB,EAAqC,MAArC,EAA6C,SAAC,KAAD;QAC5C,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF4C,CAA7C;IAJe,CAzGhB;IAiHA,WAAA,EAAa,SAAC,GAAD,EAAM,GAAN;aACZ,aAAa,CAAC,KAAd,CAAoB,SAAC,GAAD;QACnB,IAAG,WAAH;UACC,MAAM,CAAC,GAAP,CAAW;YAAA,GAAA,EAAI,GAAJ;WAAX,EAAoB,+BAApB;iBACA,GAAG,CAAC,IAAJ,CAAS,GAAT,EAFD;SAAA,MAAA;iBAIC,GAAG,CAAC,IAAJ,CAAS,GAAT,EAJD;;MADmB,CAApB;IADY,CAjHb;IAyHA,SAAA,EAAW,SAAC,GAAD,EAAM,GAAN;aACV,aAAa,CAAC,SAAd,CAAwB,SAAC,GAAD;QACvB,IAAG,WAAH;UACC,MAAM,CAAC,GAAP,CAAW;YAAA,GAAA,EAAI,GAAJ;WAAX,EAAoB,6BAApB;iBACA,GAAG,CAAC,IAAJ,CAAS,GAAT,EAFD;SAAA,MAAA;iBAIC,GAAG,CAAC,IAAJ,CAAS,GAAT,EAJD;;MADuB,CAAxB;IADU,CAzHX;;AATD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/LockManager.js b/services/track-changes/app/js/LockManager.js index 729e9bab6b..28701f4ac1 100644 --- a/services/track-changes/app/js/LockManager.js +++ b/services/track-changes/app/js/LockManager.js @@ -40,10 +40,10 @@ module.exports = LockManager = { tryLock(key, callback) { if (callback == null) { - callback = function(err, gotLock) {} + callback = function (err, gotLock) {} } const lockValue = LockManager.randomLock() - return rclient.set(key, lockValue, 'EX', this.LOCK_TTL, 'NX', function( + return rclient.set(key, lockValue, 'EX', this.LOCK_TTL, 'NX', function ( err, gotLock ) { @@ -61,17 +61,17 @@ module.exports = LockManager = { getLock(key, callback) { let attempt if (callback == null) { - callback = function(error) {} + callback = function (error) {} } const startTime = Date.now() - return (attempt = function() { + return (attempt = function () { if (Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME) { const e = new Error('Timeout') e.key = key return callback(e) } - return LockManager.tryLock(key, function(error, gotLock, lockValue) { + return LockManager.tryLock(key, function (error, gotLock, lockValue) { if (error != null) { return callback(error) } @@ -86,9 +86,9 @@ module.exports = LockManager = { checkLock(key, callback) { if (callback == null) { - callback = function(err, isFree) {} + callback = function (err, isFree) {} } - return rclient.exists(key, function(err, exists) { + return rclient.exists(key, function (err, exists) { if (err != null) { return callback(err) } @@ -102,7 +102,7 @@ module.exports = LockManager = { }, releaseLock(key, lockValue, callback) { - return rclient.eval(LockManager.unlockScript, 1, key, lockValue, function( + return rclient.eval(LockManager.unlockScript, 1, key, lockValue, function ( err, result ) { @@ -123,14 +123,14 @@ module.exports = LockManager = { runWithLock(key, runner, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } - return LockManager.getLock(key, function(error, lockValue) { + return LockManager.getLock(key, function (error, lockValue) { if (error != null) { return callback(error) } - return runner(error1 => - LockManager.releaseLock(key, lockValue, function(error2) { + return runner((error1) => + LockManager.releaseLock(key, lockValue, function (error2) { error = error1 || error2 if (error != null) { return callback(error) @@ -142,7 +142,7 @@ module.exports = LockManager = { }, healthCheck(callback) { - const action = releaseLock => releaseLock() + const action = (releaseLock) => releaseLock() return LockManager.runWithLock( `HistoryLock:HealthCheck:host=${HOST}:pid=${PID}:random=${RND}`, action, diff --git a/services/track-changes/app/js/LockManager.js.map b/services/track-changes/app/js/LockManager.js.map new file mode 100644 index 0000000000..60b765a17b --- /dev/null +++ b/services/track-changes/app/js/LockManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "LockManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/LockManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,KAAA,GAAQ,OAAA,CAAQ,kBAAR;;EACR,OAAA,GAAU,KAAK,CAAC,YAAN,CAAmB,QAAQ,CAAC,KAAK,CAAC,IAAlC;;EACV,EAAA,GAAK,OAAA,CAAQ,IAAR;;EACL,MAAA,GAAS,OAAA,CAAQ,QAAR;;EACT,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,IAAA,GAAO,EAAE,CAAC,QAAH,CAAA;;EACP,GAAA,GAAM,OAAO,CAAC;;EACd,GAAA,GAAM,MAAM,CAAC,WAAP,CAAmB,CAAnB,CAAqB,CAAC,QAAtB,CAA+B,KAA/B;;EACN,KAAA,GAAQ;;EAER,MAAM,CAAC,OAAP,GAAiB,WAAA,GAChB;IAAA,kBAAA,EAAoB,EAApB;IACA,kBAAA,EAAoB,KADpB;IAEA,QAAA,EAAU,GAFV;IAOA,UAAA,EAAa,SAAA;AACZ,UAAA;MAAA,IAAA,GAAO,IAAI,CAAC,GAAL,CAAA;AACP,aAAO,cAAA,GAAe,IAAf,GAAoB,OAApB,GAA2B,GAA3B,GAA+B,UAA/B,GAAyC,GAAzC,GAA6C,QAA7C,GAAqD,IAArD,GAA0D,SAA1D,GAAkE,CAAC,KAAA,EAAD;IAF7D,CAPb;IAWA,YAAA,EAAc,mGAXd;IAaA,OAAA,EAAU,SAAC,GAAD,EAAM,QAAN;AACT,UAAA;;QADe,WAAW,SAAC,GAAD,EAAM,OAAN,GAAA;;MAC1B,SAAA,GAAY,WAAW,CAAC,UAAZ,CAAA;aACZ,OAAO,CAAC,GAAR,CAAY,GAAZ,EAAiB,SAAjB,EAA4B,IAA5B,EAAkC,IAAC,CAAA,QAAnC,EAA6C,IAA7C,EAAmD,SAAC,GAAD,EAAM,OAAN;QAClD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAG,OAAA,KAAW,IAAd;iBACC,QAAA,CAAS,GAAT,EAAc,IAAd,EAAoB,SAApB,EADD;SAAA,MAAA;iBAGC,QAAA,CAAS,GAAT,EAAc,KAAd,EAHD;;MAFkD,CAAnD;IAFS,CAbV;IAsBA,OAAA,EAAS,SAAC,GAAD,EAAM,QAAN;AACR,UAAA;;QADc,WAAW,SAAC,KAAD,GAAA;;MACzB,SAAA,GAAY,IAAI,CAAC,GAAL,CAAA;aACT,CAAA,OAAA,GAAU,SAAA;AACZ,YAAA;QAAA,IAAG,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,SAAb,GAAyB,WAAW,CAAC,kBAAxC;UACC,CAAA,GAAI,IAAI,KAAJ,CAAU,SAAV;UACJ,CAAC,CAAC,GAAF,GAAQ;AACR,iBAAO,QAAA,CAAS,CAAT,EAHR;;eAKA,WAAW,CAAC,OAAZ,CAAoB,GAApB,EAAyB,SAAC,KAAD,EAAQ,OAAR,EAAiB,SAAjB;UACxB,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACA,IAAG,OAAH;mBACC,QAAA,CAAS,IAAT,EAAe,SAAf,EADD;WAAA,MAAA;mBAGC,UAAA,CAAW,OAAX,EAAoB,WAAW,CAAC,kBAAhC,EAHD;;QAFwB,CAAzB;MANY,CAAV,CAAH,CAAA;IAFQ,CAtBT;IAqCA,SAAA,EAAW,SAAC,GAAD,EAAM,QAAN;;QAAM,WAAW,SAAC,GAAD,EAAM,MAAN,GAAA;;aAC3B,OAAO,CAAC,MAAR,CAAe,GAAf,EAAoB,SAAC,GAAD,EAAM,MAAN;QACnB,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,MAAA,GAAS,QAAA,CAAS,MAAT;QACT,IAAG,MAAA,KAAU,CAAb;iBACC,QAAA,CAAS,GAAT,EAAc,KAAd,EADD;SAAA,MAAA;iBAGC,QAAA,CAAS,GAAT,EAAc,IAAd,EAHD;;MAHmB,CAApB;IADU,CArCX;IA8CA,WAAA,EAAa,SAAC,GAAD,EAAM,SAAN,EAAiB,QAAjB;aACZ,OAAO,EAAC,IAAD,EAAP,CAAa,WAAW,CAAC,YAAzB,EAAuC,CAAvC,EAA0C,GAA1C,EAA+C,SAA/C,EAA0D,SAAC,GAAD,EAAM,MAAN;QACzD,IAAG,WAAH;AACC,iBAAO,QAAA,CAAS,GAAT,EADR;;QAEA,IAAG,gBAAA,IAAY,MAAA,KAAY,CAA3B;UACC,MAAM,CAAC,KAAP,CAAa;YAAC,GAAA,EAAI,GAAL;YAAU,SAAA,EAAU,SAApB;YAA+B,SAAA,EAAU,GAAzC;YAA8C,YAAA,EAAa,MAA3D;WAAb,EAAiF,iBAAjF;AACA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,iCAAV,CAAT,EAFR;;eAGA,QAAA,CAAS,GAAT,EAAa,MAAb;MANyD,CAA1D;IADY,CA9Cb;IAuDA,WAAA,EAAa,SAAC,GAAD,EAAM,MAAN,EAAkD,QAAlD;;QAAM,SAAS,CAAE,SAAC,WAAD;;YAAC,cAAc,SAAC,KAAD,GAAA;;QAAf,CAAF;;;QAAmC,WAAW,CAAE,SAAC,KAAD,GAAA,CAAF;;aACzE,WAAW,CAAC,OAAZ,CAAoB,GAApB,EAAyB,SAAC,KAAD,EAAQ,SAAR;QACxB,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,MAAA,CAAO,SAAC,MAAD;iBACN,WAAW,CAAC,WAAZ,CAAwB,GAAxB,EAA6B,SAA7B,EAAwC,SAAC,MAAD;YACvC,KAAA,GAAQ,MAAA,IAAU;YAClB,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;mBACA,QAAA,CAAA;UAHuC,CAAxC;QADM,CAAP;MAFwB,CAAzB;IADY,CAvDb;IAgEA,WAAA,EAAa,SAAC,QAAD;AACZ,UAAA;MAAA,MAAA,GAAS,SAAC,WAAD;eACR,WAAA,CAAA;MADQ;aAET,WAAW,CAAC,WAAZ,CAAwB,+BAAA,GAAgC,IAAhC,GAAqC,OAArC,GAA4C,GAA5C,GAAgD,UAAhD,GAA0D,GAAlF,EAAyF,MAAzF,EAAiG,QAAjG;IAHY,CAhEb;IAqEA,KAAA,EAAO,SAAC,QAAD;MACN,OAAO,CAAC,IAAR,CAAA;aACA,OAAO,CAAC,IAAR,CAAa,KAAb,EAAoB,QAApB;IAFM,CArEP;;AAbD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js index 613ed930f4..bcf2a49715 100644 --- a/services/track-changes/app/js/MongoAWS.js +++ b/services/track-changes/app/js/MongoAWS.js @@ -26,7 +26,7 @@ const Metrics = require('metrics-sharelatex') const DAYS = 24 * 3600 * 1000 // one day in milliseconds -const createStream = function(streamConstructor, project_id, doc_id, pack_id) { +const createStream = function (streamConstructor, project_id, doc_id, pack_id) { const AWS_CONFIG = { accessKeyId: settings.trackchanges.s3.key, secretAccessKey: settings.trackchanges.s3.secret, @@ -43,11 +43,11 @@ const createStream = function(streamConstructor, project_id, doc_id, pack_id) { module.exports = MongoAWS = { archivePack(project_id, doc_id, pack_id, _callback) { if (_callback == null) { - _callback = function(error) {} + _callback = function (error) {} } - const callback = function(...args) { + const callback = function (...args) { _callback(...Array.from(args || [])) - return (_callback = function() {}) + return (_callback = function () {}) } const query = { @@ -69,7 +69,7 @@ module.exports = MongoAWS = { const upload = createStream(S3S.WriteStream, project_id, doc_id, pack_id) - return db.docHistory.findOne(query, function(err, result) { + return db.docHistory.findOne(query, function (err, result) { if (err != null) { return callback(err) } @@ -85,7 +85,7 @@ module.exports = MongoAWS = { logger.error({ err: error, project_id, doc_id, pack_id }, error.message) return callback(error) } - return zlib.gzip(uncompressedData, function(err, buf) { + return zlib.gzip(uncompressedData, function (err, buf) { logger.log( { project_id, @@ -99,8 +99,8 @@ module.exports = MongoAWS = { if (err != null) { return callback(err) } - upload.on('error', err => callback(err)) - upload.on('finish', function() { + upload.on('error', (err) => callback(err)) + upload.on('finish', function () { Metrics.inc('archive-pack') logger.log({ project_id, doc_id, pack_id }, 'upload to s3 completed') return callback(null) @@ -113,11 +113,11 @@ module.exports = MongoAWS = { readArchivedPack(project_id, doc_id, pack_id, _callback) { if (_callback == null) { - _callback = function(error, result) {} + _callback = function (error, result) {} } - const callback = function(...args) { + const callback = function (...args) { _callback(...Array.from(args || [])) - return (_callback = function() {}) + return (_callback = function () {}) } if (project_id == null) { @@ -135,12 +135,12 @@ module.exports = MongoAWS = { const download = createStream(S3S.ReadStream, project_id, doc_id, pack_id) const inputStream = download - .on('open', obj => 1) - .on('error', err => callback(err)) + .on('open', (obj) => 1) + .on('error', (err) => callback(err)) const gunzip = zlib.createGunzip() gunzip.setEncoding('utf8') - gunzip.on('error', function(err) { + gunzip.on('error', function (err) { logger.log( { project_id, doc_id, pack_id, err }, 'error uncompressing gzip stream' @@ -150,8 +150,8 @@ module.exports = MongoAWS = { const outputStream = inputStream.pipe(gunzip) const parts = [] - outputStream.on('error', err => callback(err)) - outputStream.on('end', function() { + outputStream.on('error', (err) => callback(err)) + outputStream.on('end', function () { let object logger.log({ project_id, doc_id, pack_id }, 'download from s3 completed') try { @@ -169,14 +169,14 @@ module.exports = MongoAWS = { } return callback(null, object) }) - return outputStream.on('data', data => parts.push(data)) + return outputStream.on('data', (data) => parts.push(data)) }, unArchivePack(project_id, doc_id, pack_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } - return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function( + return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function ( err, object ) { diff --git a/services/track-changes/app/js/MongoAWS.js.map b/services/track-changes/app/js/MongoAWS.js.map new file mode 100644 index 0000000000..8594f9a104 --- /dev/null +++ b/services/track-changes/app/js/MongoAWS.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "MongoAWS.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/MongoAWS.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA,sHAAA;IAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,GAAA,GAAM,OAAA,CAAQ,SAAR;;EACN,GAAA,GAAM,OAAA,CAAQ,YAAR;;EACN,MAAiB,OAAA,CAAQ,WAAR,CAAjB,EAAC,WAAD,EAAK;;EACL,UAAA,GAAa,OAAA,CAAQ,YAAR;;EACb,cAAA,GAAiB,OAAA,CAAQ,QAAR;;EACjB,IAAA,GAAO,OAAA,CAAQ,MAAR;;EACP,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EAEV,IAAA,GAAO,EAAA,GAAK,IAAL,GAAY;;EAEnB,YAAA,GAAe,SAAC,iBAAD,EAAoB,UAApB,EAAgC,MAAhC,EAAwC,OAAxC;AACd,QAAA;IAAA,UAAA,GACC;MAAA,WAAA,EAAa,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,GAAtC;MACA,eAAA,EAAiB,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,MAD1C;;AAGD,WAAO,iBAAA,CAAkB,IAAI,GAAG,CAAC,EAAR,CAAW,UAAX,CAAlB,EAA0C;MAChD,QAAA,EAAU,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,WADS;MAEhD,KAAA,EAAO,UAAA,GAAW,WAAX,GAAuB,MAAvB,GAA8B,QAA9B,GAAuC,OAFE;KAA1C;EALO;;EAUf,MAAM,CAAC,OAAP,GAAiB,QAAA,GAEhB;IAAA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,SAA9B;AAEZ,UAAA;;QAF0C,YAAY,SAAC,KAAD,GAAA;;MAEtD,QAAA,GAAW,SAAA;AACV,YAAA;QADW;QACX,SAAA,aAAU,IAAV;eACA,SAAA,GAAY,SAAA,GAAA;MAFF;MAIX,KAAA,GAAQ;QACP,GAAA,EAAK,QAAA,CAAS,OAAT,CADE;QAEP,MAAA,EAAQ,QAAA,CAAS,MAAT,CAFD;;MAKR,IAAuD,kBAAvD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,oBAAV,CAAT,EAAP;;MACA,IAAmD,cAAnD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT,EAAP;;MACA,IAAoD,eAApD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,iBAAV,CAAT,EAAP;;MAEA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,sBAA1C;MAEA,MAAA,GAAS,YAAA,CAAa,GAAG,CAAC,WAAjB,EAA8B,UAA9B,EAA0C,MAA1C,EAAkD,OAAlD;aAET,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB,KAAtB,EAA6B,SAAC,GAAD,EAAM,MAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAmE,cAAnE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gCAAV,CAAT,EAAP;;QACA,IAAqE,wBAArE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,sCAAV,CAAT,EAAP;;QACA,gBAAA,GAAmB,IAAI,CAAC,SAAL,CAAe,MAAf;QACnB,IAAG,gBAAgB,CAAC,OAAjB,CAAyB,QAAzB,CAAA,KAAsC,CAAC,CAA1C;UACC,KAAA,GAAQ,IAAI,KAAJ,CAAU,4BAAV;UACR,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAY,UAAxB;YAAoC,MAAA,EAAQ,MAA5C;YAAoD,OAAA,EAAS,OAA7D;WAAb,EAAmF,KAAK,CAAC,OAAzF;AACA,iBAAO,QAAA,CAAS,KAAT,EAHR;;eAIA,IAAI,CAAC,IAAL,CAAU,gBAAV,EAA4B,SAAC,GAAD,EAAM,GAAN;UAC3B,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,SAAA,OAArB;YAA8B,QAAA,EAAU,gBAAgB,CAAC,MAAzD;YAAiE,OAAA,EAAS,GAAG,CAAC,MAA9E;WAAX,EAAkG,iBAAlG;UACA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,MAAM,CAAC,EAAP,CAAU,OAAV,EAAmB,SAAC,GAAD;mBAClB,QAAA,CAAS,GAAT;UADkB,CAAnB;UAEA,MAAM,CAAC,EAAP,CAAU,QAAV,EAAoB,SAAA;YACnB,OAAO,CAAC,GAAR,CAAY,cAAZ;YACA,MAAM,CAAC,GAAP,CAAW;cAAC,YAAA,UAAD;cAAa,QAAA,MAAb;cAAqB,SAAA,OAArB;aAAX,EAA0C,wBAA1C;mBACA,QAAA,CAAS,IAAT;UAHmB,CAApB;UAIA,MAAM,CAAC,KAAP,CAAa,GAAb;iBACA,MAAM,CAAC,GAAP,CAAA;QAV2B,CAA5B;MAT4B,CAA7B;IAnBY,CAAb;IAwCA,gBAAA,EAAkB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,SAA9B;AACjB,UAAA;;QAD+C,YAAY,SAAC,KAAD,EAAQ,MAAR,GAAA;;MAC3D,QAAA,GAAW,SAAA;AACV,YAAA;QADW;QACX,SAAA,aAAU,IAAV;eACA,SAAA,GAAY,SAAA,GAAA;MAFF;MAIX,IAAuD,kBAAvD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,oBAAV,CAAT,EAAP;;MACA,IAAmD,cAAnD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT,EAAP;;MACA,IAAoD,eAApD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,iBAAV,CAAT,EAAP;;MAEA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,0BAA1C;MAEA,QAAA,GAAW,YAAA,CAAa,GAAG,CAAC,UAAjB,EAA6B,UAA7B,EAAyC,MAAzC,EAAiD,OAAjD;MAEX,WAAA,GAAc,QACb,CAAC,EADY,CACT,MADS,EACD,SAAC,GAAD;AACX,eAAO;MADI,CADC,CAGb,CAAC,EAHY,CAGT,OAHS,EAGA,SAAC,GAAD;eACZ,QAAA,CAAS,GAAT;MADY,CAHA;MAMd,MAAA,GAAS,IAAI,CAAC,YAAL,CAAA;MACT,MAAM,CAAC,WAAP,CAAmB,MAAnB;MACA,MAAM,CAAC,EAAP,CAAU,OAAV,EAAmB,SAAC,GAAD;QAClB,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;SAAX,EAA+C,iCAA/C;eACA,QAAA,CAAS,GAAT;MAFkB,CAAnB;MAIA,YAAA,GAAe,WAAW,CAAC,IAAZ,CAAiB,MAAjB;MACf,KAAA,GAAQ;MACR,YAAY,CAAC,EAAb,CAAgB,OAAhB,EAAyB,SAAC,GAAD;AACxB,eAAO,QAAA,CAAS,GAAT;MADiB,CAAzB;MAEA,YAAY,CAAC,EAAb,CAAgB,KAAhB,EAAuB,SAAA;AACtB,YAAA;QAAA,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,4BAA1C;AACA;UACC,MAAA,GAAS,IAAI,CAAC,KAAL,CAAW,KAAK,CAAC,IAAN,CAAW,EAAX,CAAX,EADV;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,CAAT,EAHR;;QAIA,MAAM,CAAC,GAAP,GAAa,QAAA,CAAS,MAAM,CAAC,GAAhB;QACb,MAAM,CAAC,MAAP,GAAgB,QAAA,CAAS,MAAM,CAAC,MAAhB;QAChB,MAAM,CAAC,UAAP,GAAoB,QAAA,CAAS,MAAM,CAAC,UAAhB;AACpB;AAAA,aAAA,sCAAA;;UACC,IAA6B,cAA7B;YAAA,EAAE,CAAC,GAAH,GAAS,QAAA,CAAS,EAAE,CAAC,GAAZ,EAAT;;AADD;eAEA,QAAA,CAAS,IAAT,EAAe,MAAf;MAXsB,CAAvB;aAYA,YAAY,CAAC,EAAb,CAAgB,MAAhB,EAAwB,SAAC,IAAD;eACvB,KAAK,CAAC,IAAN,CAAW,IAAX;MADuB,CAAxB;IAzCiB,CAxClB;IAoFA,aAAA,EAAe,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;;QAA8B,WAAW,SAAC,KAAD,GAAA;;aACvD,QAAQ,CAAC,gBAAT,CAA0B,UAA1B,EAAsC,MAAtC,EAA8C,OAA9C,EAAuD,SAAC,GAAD,EAAM,MAAN;QACtD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,OAAO,CAAC,GAAR,CAAY,gBAAZ;QAEA,MAAM,CAAC,SAAP,GAAmB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B;QACnB,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,0BAA1C;eACA,EAAE,CAAC,UAAU,CAAC,MAAd,CAAqB,MAArB,EAA6B,QAA7B;MANsD,CAAvD;IADc,CApFf;;AAxBD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/MongoManager.js b/services/track-changes/app/js/MongoManager.js index 947e5b1db1..6a84fe7bc1 100644 --- a/services/track-changes/app/js/MongoManager.js +++ b/services/track-changes/app/js/MongoManager.js @@ -22,13 +22,13 @@ const logger = require('logger-sharelatex') module.exports = MongoManager = { getLastCompressedUpdate(doc_id, callback) { if (callback == null) { - callback = function(error, update) {} + callback = function (error, update) {} } return db.docHistory .find({ doc_id: ObjectId(doc_id.toString()) }, { pack: { $slice: -1 } }) // only return the last entry in a pack .sort({ v: -1 }) .limit(1) - .toArray(function(error, compressedUpdates) { + .toArray(function (error, compressedUpdates) { if (error != null) { return callback(error) } @@ -44,9 +44,9 @@ module.exports = MongoManager = { // to start, we pass it back as callback(null,null,version), just // giving the version so we can check consistency. if (callback == null) { - callback = function(error, update, version) {} + callback = function (error, update, version) {} } - return MongoManager.getLastCompressedUpdate(doc_id, function( + return MongoManager.getLastCompressedUpdate(doc_id, function ( error, update ) { @@ -76,7 +76,7 @@ module.exports = MongoManager = { return callback(null, update, update.v) } } else { - return PackManager.getLastPackFromIndex(doc_id, function(error, pack) { + return PackManager.getLastPackFromIndex(doc_id, function (error, pack) { if (error != null) { return callback(error) } @@ -94,7 +94,7 @@ module.exports = MongoManager = { backportProjectId(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return db.docHistory.update( { @@ -113,13 +113,13 @@ module.exports = MongoManager = { getProjectMetaData(project_id, callback) { if (callback == null) { - callback = function(error, metadata) {} + callback = function (error, metadata) {} } return db.projectHistoryMetaData.find( { project_id: ObjectId(project_id.toString()) }, - function(error, results) { + function (error, results) { if (error != null) { return callback(error) } @@ -130,7 +130,7 @@ module.exports = MongoManager = { setProjectMetaData(project_id, metadata, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return db.projectHistoryMetaData.update( { @@ -149,7 +149,7 @@ module.exports = MongoManager = { upgradeHistory(project_id, callback) { // preserve the project's existing history if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return db.docHistory.update( { @@ -200,11 +200,10 @@ module.exports = MongoManager = { ) } } - ;[ 'getLastCompressedUpdate', 'getProjectMetaData', 'setProjectMetaData' -].map(method => +].map((method) => metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger) ) diff --git a/services/track-changes/app/js/MongoManager.js.map b/services/track-changes/app/js/MongoManager.js.map new file mode 100644 index 0000000000..af11849e38 --- /dev/null +++ b/services/track-changes/app/js/MongoManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "MongoManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/MongoManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,MAAiB,OAAA,CAAQ,WAAR,CAAjB,EAAC,WAAD,EAAK;;EACL,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,YAAA,GAChB;IAAA,uBAAA,EAAyB,SAAC,MAAD,EAAS,QAAT;;QAAS,WAAW,SAAC,KAAD,EAAQ,MAAR,GAAA;;aAC5C,EAAE,CAAC,UACF,CAAC,IADF,CACO;QAAA,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAR;OADP,EAC4C;QAAC,IAAA,EAAM;UAAC,MAAA,EAAO,CAAC,CAAT;SAAP;OAD5C,CAEC,CAAC,IAFF,CAEQ;QAAA,CAAA,EAAG,CAAC,CAAJ;OAFR,CAGC,CAAC,KAHF,CAGQ,CAHR,CAIC,CAAC,OAJF,CAIU,SAAC,KAAD,EAAQ,iBAAR;QACR,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,iBAAkB,CAAA,CAAA,CAAlB,IAAwB,IAAvC;MAFQ,CAJV;IADwB,CAAzB;IASA,wBAAA,EAA0B,SAAC,MAAD,EAAS,QAAT;;QAAS,WAAW,SAAC,KAAD,EAAQ,MAAR,EAAgB,OAAhB,GAAA;;aAO7C,YAAY,CAAC,uBAAb,CAAqC,MAArC,EAA6C,SAAC,KAAD,EAAQ,MAAR;AAC5C,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,IAAG,cAAH;UACC,IAAG,MAAM,CAAC,MAAV;AACC,mBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EADR;WAAA,MAEK,IAAG,mBAAH;YACJ,IAAG,MAAM,CAAC,SAAV;AACC,qBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,wCAAmC,CAAE,UAArC,EADR;aAAA,MAAA;AAGC,qBAAO,QAAA,CAAS,IAAT,EAAe,MAAf,wCAAqC,CAAE,UAAvC,EAHR;aADI;WAAA,MAAA;AAMJ,mBAAO,QAAA,CAAS,IAAT,EAAe,MAAf,EAAuB,MAAM,CAAC,CAA9B,EANH;WAHN;SAAA,MAAA;iBAWC,WAAW,CAAC,oBAAZ,CAAiC,MAAjC,EAAyC,SAAC,KAAD,EAAQ,IAAR;YACxC,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,IAA2C,6CAAA,IAAgB,8CAA3D;AAAA,qBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EAAqB,IAAI,CAAC,KAA1B,EAAP;;mBACA,QAAA,CAAS,IAAT,EAAe,IAAf;UAHwC,CAAzC,EAXD;;MAF4C,CAA7C;IAPyB,CAT1B;IAkCA,iBAAA,EAAmB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;;QAAqB,WAAW,SAAC,KAAD,GAAA;;aAClD,EAAE,CAAC,UAAU,CAAC,MAAd,CAAqB;QACpB,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CADY;QAEpB,UAAA,EAAY;UAAE,OAAA,EAAS,KAAX;SAFQ;OAArB,EAGG;QACF,IAAA,EAAM;UAAE,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CAAd;SADJ;OAHH,EAKG;QACF,KAAA,EAAO,IADL;OALH,EAOG,QAPH;IADkB,CAlCnB;IA4CA,kBAAA,EAAoB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,QAAR,GAAA;;aAC3C,EAAE,CAAC,sBAAsB,CAAC,IAA1B,CAA+B;QAC9B,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CADkB;OAA/B,EAEG,SAAC,KAAD,EAAQ,OAAR;QACF,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,OAAQ,CAAA,CAAA,CAAvB;MAFE,CAFH;IADmB,CA5CpB;IAmDA,kBAAA,EAAoB,SAAC,UAAD,EAAa,QAAb,EAAuB,QAAvB;;QAAuB,WAAW,SAAC,KAAD,GAAA;;aACrD,EAAE,CAAC,sBAAsB,CAAC,MAA1B,CAAiC;QAChC,UAAA,EAAY,QAAA,CAAS,UAAT,CADoB;OAAjC,EAEG;QACF,IAAA,EAAM,QADJ;OAFH,EAIG;QACF,MAAA,EAAQ,IADN;OAJH,EAMG,QANH;IADmB,CAnDpB;IA4DA,cAAA,EAAgB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,GAAA;;aAEvC,EAAE,CAAC,UAAU,CAAC,MAAd,CAAqB;QACpB,UAAA,EAAY,QAAA,CAAS,UAAT,CADQ;QAEpB,SAAA,EAAW,IAFS;QAGpB,SAAA,EAAW;UAAC,OAAA,EAAS,IAAV;SAHS;OAArB,EAIG;QACF,IAAA,EAAM;UAAC,SAAA,EAAW,KAAZ;SADJ;QAEF,MAAA,EAAQ;UAAC,SAAA,EAAW,EAAZ;SAFN;OAJH,EAOG;QACF,KAAA,EAAO,IADL;OAPH,EASG,QATH;IAFe,CA5DhB;IAyEA,aAAA,EAAe,SAAA;MAEd,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,MAAA,EAAQ,CAAV;QAAa,CAAA,EAAG,CAAhB;OAA1B,EAA+C;QAAE,UAAA,EAAY,IAAd;OAA/C;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,UAAA,EAAY,CAAd;QAAiB,aAAA,EAAe,CAAhC;OAA1B,EAA+D;QAAE,UAAA,EAAY,IAAd;OAA/D;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,MAAA,EAAQ,CAAV;QAAa,UAAA,EAAY,CAAzB;OAA1B,EAAwD;QAAE,UAAA,EAAY,IAAd;OAAxD;MAEA,EAAE,CAAC,sBAAsB,CAAC,WAA1B,CAAsC;QAAE,UAAA,EAAY,CAAd;OAAtC,EAAyD;QAAE,UAAA,EAAY,IAAd;OAAzD;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,SAAA,EAAW,CAAb;OAA1B,EAA4C;QAAE,kBAAA,EAAoB,CAAtB;QAAyB,UAAA,EAAY,IAArC;OAA5C;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,YAAA,EAAc,CAAhB;OAA1B,EAA+C;QAAE,UAAA,EAAY,IAAd;OAA/C;aAEA,EAAE,CAAC,eAAe,CAAC,WAAnB,CAA+B;QAAE,UAAA,EAAY,CAAd;OAA/B,EAAkD;QAAE,UAAA,EAAY,IAAd;OAAlD;IAdc,CAzEf;;;EA0FD,CACC,yBADD,EAEC,oBAFD,EAGC,oBAHD,CAIC,CAAC,GAJF,CAIM,SAAC,MAAD;WACL,OAAO,CAAC,eAAR,CAAwB,YAAxB,EAAsC,MAAtC,EAA8C,oBAA9C,EAAoE,MAApE;EADK,CAJN;AAlGA" +} \ No newline at end of file diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 5f5c0444e1..5d753aebb2 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -74,7 +74,7 @@ module.exports = PackManager = { callback ) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } if (newUpdates.length === 0) { return callback() @@ -115,7 +115,7 @@ module.exports = PackManager = { lastUpdate, updatesToFlush, temporary, - function(error) { + function (error) { if (error != null) { return callback(error) } @@ -140,7 +140,7 @@ module.exports = PackManager = { callback ) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } if (newUpdates.length === 0) { return callback() @@ -190,7 +190,7 @@ module.exports = PackManager = { callback ) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } const first = newUpdates[0] const last = newUpdates[newUpdates.length - 1] @@ -218,7 +218,7 @@ module.exports = PackManager = { { project_id, doc_id, newUpdates }, 'inserting updates into new pack' ) - return db.docHistory.save(newPack, function(err, result) { + return db.docHistory.save(newPack, function (err, result) { if (err != null) { return callback(err) } @@ -240,7 +240,7 @@ module.exports = PackManager = { callback ) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } const first = newUpdates[0] const last = newUpdates[newUpdates.length - 1] @@ -283,14 +283,14 @@ module.exports = PackManager = { getOpsByVersionRange(project_id, doc_id, fromVersion, toVersion, callback) { if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } return PackManager.loadPacksByVersionRange( project_id, doc_id, fromVersion, toVersion, - function(error) { + function (error) { const query = { doc_id: ObjectId(doc_id.toString()) } if (toVersion != null) { query.v = { $lte: toVersion } @@ -299,34 +299,36 @@ module.exports = PackManager = { query.v_end = { $gte: fromVersion } } // console.log "query:", query - return db.docHistory.find(query).sort({ v: -1 }, function(err, result) { - if (err != null) { - return callback(err) - } - // console.log "getOpsByVersionRange:", err, result - const updates = [] - const opInRange = function(op, from, to) { - if (fromVersion != null && op.v < fromVersion) { - return false + return db.docHistory + .find(query) + .sort({ v: -1 }, function (err, result) { + if (err != null) { + return callback(err) } - if (toVersion != null && op.v > toVersion) { - return false + // console.log "getOpsByVersionRange:", err, result + const updates = [] + const opInRange = function (op, from, to) { + if (fromVersion != null && op.v < fromVersion) { + return false + } + if (toVersion != null && op.v > toVersion) { + return false + } + return true } - return true - } - for (const docHistory of Array.from(result)) { - // console.log 'adding', docHistory.pack - for (const op of Array.from(docHistory.pack.reverse())) { - if (opInRange(op, fromVersion, toVersion)) { - op.project_id = docHistory.project_id - op.doc_id = docHistory.doc_id - // console.log "added op", op.v, fromVersion, toVersion - updates.push(op) + for (const docHistory of Array.from(result)) { + // console.log 'adding', docHistory.pack + for (const op of Array.from(docHistory.pack.reverse())) { + if (opInRange(op, fromVersion, toVersion)) { + op.project_id = docHistory.project_id + op.doc_id = docHistory.doc_id + // console.log "added op", op.v, fromVersion, toVersion + updates.push(op) + } } } - } - return callback(null, updates) - }) + return callback(null, updates) + }) } ) }, @@ -338,14 +340,14 @@ module.exports = PackManager = { toVersion, callback ) { - return PackManager.getIndex(doc_id, function(err, indexResult) { + return PackManager.getIndex(doc_id, function (err, indexResult) { let pack if (err != null) { return callback(err) } const indexPacks = (indexResult != null ? indexResult.packs : undefined) || [] - const packInRange = function(pack, from, to) { + const packInRange = function (pack, from, to) { if (fromVersion != null && pack.v_end < fromVersion) { return false } @@ -391,7 +393,7 @@ module.exports = PackManager = { } }, { _id: 1 }, - function(err, loadedPacks) { + function (err, loadedPacks) { if (err != null) { return callback(err) } @@ -402,7 +404,7 @@ module.exports = PackManager = { } return result1 })() - const loadedPackIds = Array.from(loadedPacks).map(pack => + const loadedPackIds = Array.from(loadedPacks).map((pack) => pack._id.toString() ) const packIdsToFetch = _.difference(allPackIds, loadedPackIds) @@ -418,7 +420,7 @@ module.exports = PackManager = { 4, (pack_id, cb) => MongoAWS.unArchivePack(project_id, doc_id, pack_id, cb), - function(err) { + function (err) { if (err != null) { return callback(err) } @@ -436,7 +438,7 @@ module.exports = PackManager = { // get all the docHistory Entries return db.docHistory .find({ project_id: ObjectId(project_id) }, { pack: false }) - .sort({ 'meta.end_ts': -1 }, function(err, packs) { + .sort({ 'meta.end_ts': -1 }, function (err, packs) { let pack if (err != null) { return callback(err) @@ -449,7 +451,7 @@ module.exports = PackManager = { } return db.docHistoryIndex.find( { project_id: ObjectId(project_id) }, - function(err, indexes) { + function (err, indexes) { if (err != null) { return callback(err) } @@ -474,7 +476,7 @@ module.exports = PackManager = { }, getPackById(project_id, doc_id, pack_id, callback) { - return db.docHistory.findOne({ _id: pack_id }, function(err, pack) { + return db.docHistory.findOne({ _id: pack_id }, function (err, pack) { if (err != null) { return callback(err) } @@ -500,7 +502,7 @@ module.exports = PackManager = { query: { _id: pack._id }, update: { $set: { expiresAt: new Date(Date.now() + 7 * DAYS) } } }, - err => callback(err, pack) + (err) => callback(err, pack) ) } else { return callback(null, pack) @@ -528,7 +530,7 @@ module.exports = PackManager = { return db.docHistoryIndex.findOne( { _id: ObjectId(doc_id.toString()) }, { packs: { $slice: -1 } }, - function(err, indexPack) { + function (err, indexPack) { if (err != null) { return callback(err) } @@ -541,7 +543,7 @@ module.exports = PackManager = { }, getIndexWithKeys(doc_id, callback) { - return PackManager.getIndex(doc_id, function(err, index) { + return PackManager.getIndex(doc_id, function (err, index) { if (err != null) { return callback(err) } @@ -558,7 +560,7 @@ module.exports = PackManager = { }, initialiseIndex(project_id, doc_id, callback) { - return PackManager.findCompletedPacks(project_id, doc_id, function( + return PackManager.findCompletedPacks(project_id, doc_id, function ( err, packs ) { @@ -580,7 +582,7 @@ module.exports = PackManager = { updateIndex(project_id, doc_id, callback) { // find all packs prior to current pack - return PackManager.findUnindexedPacks(project_id, doc_id, function( + return PackManager.findUnindexedPacks(project_id, doc_id, function ( err, newPacks ) { @@ -594,7 +596,7 @@ module.exports = PackManager = { project_id, doc_id, newPacks, - function(err) { + function (err) { if (err != null) { return callback(err) } @@ -615,7 +617,7 @@ module.exports = PackManager = { } return db.docHistory .find(query, { pack: false }) - .sort({ v: 1 }, function(err, packs) { + .sort({ v: 1 }, function (err, packs) { if (err != null) { return callback(err) } @@ -640,7 +642,7 @@ module.exports = PackManager = { } return db.docHistory .find(query, { pack: false }) - .sort({ v: 1 }, function(err, packs) { + .sort({ v: 1 }, function (err, packs) { if (err != null) { return callback(err) } @@ -655,11 +657,11 @@ module.exports = PackManager = { }, findUnindexedPacks(project_id, doc_id, callback) { - return PackManager.getIndexWithKeys(doc_id, function(err, indexResult) { + return PackManager.getIndexWithKeys(doc_id, function (err, indexResult) { if (err != null) { return callback(err) } - return PackManager.findCompletedPacks(project_id, doc_id, function( + return PackManager.findCompletedPacks(project_id, doc_id, function ( err, historyPacks ) { @@ -713,7 +715,7 @@ module.exports = PackManager = { insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, callback) { return LockManager.runWithLock( keys.historyIndexLock({ doc_id }), - releaseLock => + (releaseLock) => PackManager._insertPacksIntoIndex( project_id, doc_id, @@ -743,14 +745,14 @@ module.exports = PackManager = { // Archiving packs to S3 archivePack(project_id, doc_id, pack_id, callback) { - const clearFlagOnError = function(err, cb) { + const clearFlagOnError = function (err, cb) { if (err != null) { // clear the inS3 flag on error return PackManager.clearPackAsArchiveInProgress( project_id, doc_id, pack_id, - function(err2) { + function (err2) { if (err2 != null) { return cb(err2) } @@ -763,30 +765,30 @@ module.exports = PackManager = { } return async.series( [ - cb => + (cb) => PackManager.checkArchiveNotInProgress( project_id, doc_id, pack_id, cb ), - cb => + (cb) => PackManager.markPackAsArchiveInProgress( project_id, doc_id, pack_id, cb ), - cb => - MongoAWS.archivePack(project_id, doc_id, pack_id, err => + (cb) => + MongoAWS.archivePack(project_id, doc_id, pack_id, (err) => clearFlagOnError(err, cb) ), - cb => - PackManager.checkArchivedPack(project_id, doc_id, pack_id, err => + (cb) => + PackManager.checkArchivedPack(project_id, doc_id, pack_id, (err) => clearFlagOnError(err, cb) ), - cb => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), - cb => + (cb) => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), + (cb) => PackManager.setTTLOnArchivedPack( project_id, doc_id, @@ -799,14 +801,14 @@ module.exports = PackManager = { }, checkArchivedPack(project_id, doc_id, pack_id, callback) { - return db.docHistory.findOne({ _id: pack_id }, function(err, pack) { + return db.docHistory.findOne({ _id: pack_id }, function (err, pack) { if (err != null) { return callback(err) } if (pack == null) { return callback(new Error('pack not found')) } - return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function( + return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function ( err, result ) { @@ -845,7 +847,7 @@ module.exports = PackManager = { // Extra methods to test archive/unarchive for a doc_id pushOldPacks(project_id, doc_id, callback) { - return PackManager.findPacks(project_id, doc_id, function(err, packs) { + return PackManager.findPacks(project_id, doc_id, function (err, packs) { if (err != null) { return callback(err) } @@ -874,8 +876,8 @@ module.exports = PackManager = { // Processing old packs via worker processOldPack(project_id, doc_id, pack_id, callback) { - const markAsChecked = err => - PackManager.markPackAsChecked(project_id, doc_id, pack_id, function( + const markAsChecked = (err) => + PackManager.markPackAsChecked(project_id, doc_id, pack_id, function ( err2 ) { if (err2 != null) { @@ -884,7 +886,7 @@ module.exports = PackManager = { return callback(err) }) logger.log({ project_id, doc_id }, 'processing old packs') - return db.docHistory.findOne({ _id: pack_id }, function(err, pack) { + return db.docHistory.findOne({ _id: pack_id }, function (err, pack) { if (err != null) { return markAsChecked(err) } @@ -899,42 +901,45 @@ module.exports = PackManager = { doc_id, pack._id, pack, - function(err) { + function (err) { if (err != null) { return markAsChecked(err) } - return PackManager.updateIndexIfNeeded(project_id, doc_id, function( + return PackManager.updateIndexIfNeeded(project_id, doc_id, function ( err ) { if (err != null) { return markAsChecked(err) } - return PackManager.findUnarchivedPacks(project_id, doc_id, function( - err, - unarchivedPacks - ) { - if (err != null) { - return markAsChecked(err) - } - if ( - !(unarchivedPacks != null ? unarchivedPacks.length : undefined) - ) { - logger.log({ project_id, doc_id }, 'no packs need archiving') - return markAsChecked() - } - return async.eachSeries( - unarchivedPacks, - (pack, cb) => - PackManager.archivePack(project_id, doc_id, pack._id, cb), - function(err) { - if (err != null) { - return markAsChecked(err) - } - logger.log({ project_id, doc_id }, 'done processing') + return PackManager.findUnarchivedPacks( + project_id, + doc_id, + function (err, unarchivedPacks) { + if (err != null) { + return markAsChecked(err) + } + if ( + !(unarchivedPacks != null + ? unarchivedPacks.length + : undefined) + ) { + logger.log({ project_id, doc_id }, 'no packs need archiving') return markAsChecked() } - ) - }) + return async.eachSeries( + unarchivedPacks, + (pack, cb) => + PackManager.archivePack(project_id, doc_id, pack._id, cb), + function (err) { + if (err != null) { + return markAsChecked(err) + } + logger.log({ project_id, doc_id }, 'done processing') + return markAsChecked() + } + ) + } + ) }) } ) @@ -975,7 +980,7 @@ module.exports = PackManager = { markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback) { return LockManager.runWithLock( keys.historyLock({ doc_id }), - releaseLock => + (releaseLock) => PackManager._markPackAsFinalised( project_id, doc_id, @@ -999,7 +1004,7 @@ module.exports = PackManager = { updateIndexIfNeeded(project_id, doc_id, callback) { logger.log({ project_id, doc_id }, 'archiving old packs') - return PackManager.getIndexWithKeys(doc_id, function(err, index) { + return PackManager.getIndexWithKeys(doc_id, function (err, index) { if (err != null) { return callback(err) } @@ -1023,7 +1028,7 @@ module.exports = PackManager = { }, findUnarchivedPacks(project_id, doc_id, callback) { - return PackManager.getIndex(doc_id, function(err, indexResult) { + return PackManager.getIndex(doc_id, function (err, indexResult) { if (err != null) { return callback(err) } @@ -1055,7 +1060,10 @@ module.exports = PackManager = { { project_id, doc_id, pack_id }, 'checking if archive in progress' ) - return PackManager.getPackFromIndex(doc_id, pack_id, function(err, result) { + return PackManager.getPackFromIndex(doc_id, pack_id, function ( + err, + result + ) { if (err != null) { return callback(err) } @@ -1086,7 +1094,7 @@ module.exports = PackManager = { fields: { 'packs.$': 1 }, update: { $set: { 'packs.$.inS3': false } } }, - function(err, result) { + function (err, result) { if (err != null) { return callback(err) } @@ -1131,7 +1139,7 @@ module.exports = PackManager = { fields: { 'packs.$': 1 }, update: { $set: { 'packs.$.inS3': true } } }, - function(err, result) { + function (err, result) { if (err != null) { return callback(err) } @@ -1150,7 +1158,7 @@ module.exports = PackManager = { query: { _id: pack_id }, update: { $set: { expiresAt: new Date(Date.now() + 1 * DAYS) } } }, - function(err) { + function (err) { logger.log({ project_id, doc_id, pack_id }, 'set expiry on pack') return callback() } diff --git a/services/track-changes/app/js/PackManager.js.map b/services/track-changes/app/js/PackManager.js.map new file mode 100644 index 0000000000..bc7445c5e4 --- /dev/null +++ b/services/track-changes/app/js/PackManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "PackManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/PackManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,MAAuB,OAAA,CAAQ,WAAR,CAAvB,EAAC,WAAD,EAAK,uBAAL,EAAe;;EACf,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,QAAA,GAAW,OAAA,CAAQ,YAAR;;EACX,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,eAAA,GAAkB,OAAA,CAAQ,mBAAR;;EAClB,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,IAAA,GAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;;EAoC3B,IAAA,GAAO,EAAA,GAAK,IAAL,GAAY;;EAEnB,MAAM,CAAC,OAAP,GAAiB,WAAA,GAEhB;IAAA,QAAA,EAAW,IAAA,GAAK,IAAhB;IACA,SAAA,EAAW,IADX;IAGA,uBAAA,EAAyB,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,UAAjC,EAA6C,SAA7C,EAAwD,QAAxD;AACxB,UAAA;;QADgF,WAAW,SAAC,KAAD,GAAA;;MAC3F,IAAqB,UAAU,CAAC,MAAX,KAAqB,CAA1C;AAAA,eAAO,QAAA,CAAA,EAAP;;MAGA,IAAqB,8DAAA,IAA2B,CAAI,SAApD;QAAA,UAAA,GAAa,KAAb;;MAEA,cAAA,GAAiB;MACjB,gBAAA,GAAmB,UAAU,CAAC,KAAX,CAAA;MAEnB,CAAA,yBAAI,UAAU,CAAE,WAAZ,IAAiB;MACrB,EAAA,yBAAK,UAAU,CAAE,YAAZ,IAAkB;AAEvB,aAAM,gBAAgB,CAAC,MAAjB,IAA4B,CAAA,GAAI,WAAW,CAAC,SAA5C,IAA0D,EAAA,GAAK,WAAW,CAAC,QAAjF;QACC,UAAA,GAAa,gBAAiB,CAAA,CAAA;QAC9B,cAAA,GAAiB,IAAI,CAAC,mBAAL,CAAyB,UAAzB;QACjB,IAAG,cAAA,GAAiB,EAAjB,GAAsB,WAAW,CAAC,QAAlC,IAA+C,CAAA,GAAI,CAAtD;AACC,gBADD;;QAEA,CAAA;QACA,EAAA,IAAM;QACN,cAAc,CAAC,IAAf,CAAoB,gBAAgB,CAAC,KAAjB,CAAA,CAApB;MAPD;aASA,WAAW,CAAC,sBAAZ,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,UAAvD,EAAmE,cAAnE,EAAmF,SAAnF,EAA8F,SAAC,KAAD;QAC7F,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,IAAxD,EAA8D,gBAA9D,EAAgF,SAAhF,EAA2F,QAA3F;MAF6F,CAA9F;IArBwB,CAHzB;IA4BA,sBAAA,EAAwB,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,UAAjC,EAA6C,SAA7C,EAAwD,QAAxD;AACvB,UAAA;;QAD+E,WAAW,SAAC,KAAD,GAAA;;MAC1F,IAAqB,UAAU,CAAC,MAAX,KAAqB,CAA1C;AAAA,eAAO,QAAA,CAAA,EAAP;;MAEA,SAAA,GAAY;MAEZ,IAAG,kBAAH;QACC,IAAG,CAAI,SAAJ,IAAsB,8BAAzB;UAEC,SAAA,GAAY,KAFb;;QAGA,GAAA,GAAM,IAAI,CAAC,GAAL,CAAA,CAAA,2CAA4B,CAAE;QACpC,IAAG,SAAA,IAAc,8BAAd,IAAwC,GAAA,GAAM,CAAA,GAAI,IAArD;UAEC,SAAA,GAAY,KAFb;SALD;;MASA,IAAG,SAAH;eACC,WAAW,CAAC,2BAAZ,CAAwC,UAAxC,EAAoD,MAApD,EAA4D,UAA5D,EAAwE,UAAxE,EAAoF,SAApF,EAA+F,QAA/F,EADD;OAAA,MAAA;eAGC,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,UAAzD,EAAqE,SAArE,EAAgF,QAAhF,EAHD;;IAduB,CA5BxB;IA+CA,wBAAA,EAA0B,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,SAAjC,EAA4C,QAA5C;AACzB,UAAA;;QADqE,WAAW,SAAC,KAAD,GAAA;;MAChF,KAAA,GAAQ,UAAW,CAAA,CAAA;MACnB,IAAA,GAAO,UAAW,CAAA,UAAU,CAAC,MAAX,GAAoB,CAApB;MAClB,CAAA,GAAI,UAAU,CAAC;MACf,EAAA,GAAK,IAAI,CAAC,mBAAL,CAAyB,UAAzB;MACL,OAAA,GACC;QAAA,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CAAZ;QACA,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CADR;QAEA,IAAA,EAAM,UAFN;QAGA,CAAA,EAAG,CAHH;QAIA,EAAA,EAAI,EAJJ;QAKA,IAAA,EACC;UAAA,QAAA,EAAU,KAAK,CAAC,IAAI,CAAC,QAArB;UACA,MAAA,EAAQ,IAAI,CAAC,IAAI,CAAC,MADlB;SAND;QAQA,CAAA,EAAG,KAAK,CAAC,CART;QASA,KAAA,EAAO,IAAI,CAAC,CATZ;QAUA,SAAA,EAAW,SAVX;;MAWD,IAAG,SAAH;QACC,OAAO,CAAC,SAAR,GAAoB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B;QACpB,OAAO,CAAC,YAAR,GAAuB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,EAAA,GAAK,IAA3B,EAFxB;;MAGA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,YAAA,UAArB;OAAX,EAA6C,iCAA7C;aACA,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,OAAnB,EAA4B,SAAC,GAAD,EAAM,MAAN;QAC3B,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,OAAO,CAAC,GAAR,CAAY,cAAA,GAAiB,CAAG,SAAH,GAAkB,WAAlB,GAAmC,WAAnC,CAA7B;QACA,IAAG,SAAH;AACC,iBAAO,QAAA,CAAA,EADR;SAAA,MAAA;iBAGC,WAAW,CAAC,WAAZ,CAAwB,UAAxB,EAAoC,MAApC,EAA4C,QAA5C,EAHD;;MAH2B,CAA5B;IArByB,CA/C1B;IA4EA,2BAAA,EAA6B,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,UAAjC,EAA6C,SAA7C,EAAwD,QAAxD;AAC5B,UAAA;;QADoF,WAAW,SAAC,KAAD,GAAA;;MAC/F,KAAA,GAAQ,UAAW,CAAA,CAAA;MACnB,IAAA,GAAO,UAAW,CAAA,UAAU,CAAC,MAAX,GAAoB,CAApB;MAClB,CAAA,GAAI,UAAU,CAAC;MACf,EAAA,GAAK,IAAI,CAAC,mBAAL,CAAyB,UAAzB;MACL,KAAA,GACC;QAAA,GAAA,EAAK,UAAU,CAAC,GAAhB;QACA,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CADZ;QAEA,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAFR;QAGA,IAAA,EAAM;UAAC,OAAA,EAAS,IAAV;SAHN;;MAID,MAAA,GACC;QAAA,KAAA,EACC;UAAA,MAAA,EAAQ;YAAC,KAAA,EAAO,UAAR;WAAR;SADD;QAEA,IAAA,EACC;UAAA,GAAA,EAAK,CAAL;UACA,IAAA,EAAO,EADP;SAHD;QAKA,IAAA,EACC;UAAA,aAAA,EAAe,IAAI,CAAC,IAAI,CAAC,MAAzB;UACA,OAAA,EAAS,IAAI,CAAC,CADd;SAND;;MAQD,IAAG,UAAU,CAAC,SAAX,IAAyB,SAA5B;QACC,MAAM,CAAC,IAAI,CAAC,SAAZ,GAAwB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B,EADzB;;MAEA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,YAAA,UAArB;QAAiC,YAAA,UAAjC;OAAX,EAAyD,oCAAzD;MACA,OAAO,CAAC,GAAR,CAAY,cAAA,GAAiB,CAAG,SAAH,GAAkB,WAAlB,GAAmC,WAAnC,CAA7B;aACA,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAAC,OAAA,KAAD;QAAQ,QAAA,MAAR;QAAgB,CAAA,GAAA,CAAA,EAAI,IAApB;QAA0B,MAAA,EAAO;UAAC,IAAA,EAAK,CAAN;UAAQ,KAAA,EAAM,CAAd;SAAjC;OAA5B,EAAgF,QAAhF;IAvB4B,CA5E7B;IAuGA,oBAAA,EAAsB,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,SAAlC,EAA6C,QAA7C;;QAA6C,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAC7E,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,WAAxD,EAAqE,SAArE,EAAgF,SAAC,KAAD;AAC/E,YAAA;QAAA,KAAA,GAAQ;UAAC,MAAA,EAAO,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAR;;QACR,IAA8B,iBAA9B;UAAA,KAAK,CAAC,CAAN,GAAU;YAAC,IAAA,EAAK,SAAN;YAAV;;QACA,IAAoC,mBAApC;UAAA,KAAK,CAAC,KAAN,GAAc;YAAC,IAAA,EAAK,WAAN;YAAd;;eAEA,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,KAAnB,CAAyB,CAAC,IAA1B,CAA+B;UAAC,CAAA,EAAE,CAAC,CAAJ;SAA/B,EAAuC,SAAC,GAAD,EAAM,MAAN;AACtC,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UAEA,OAAA,GAAU;UACV,SAAA,GAAY,SAAC,EAAD,EAAK,IAAL,EAAW,EAAX;YACX,IAAgB,qBAAA,IAAiB,EAAE,CAAC,CAAH,GAAO,WAAxC;AAAA,qBAAO,MAAP;;YACA,IAAgB,mBAAA,IAAe,EAAE,CAAC,CAAH,GAAO,SAAtC;AAAA,qBAAO,MAAP;;AACA,mBAAO;UAHI;AAIZ,eAAA,wCAAA;;AAEC;AAAA,iBAAA,wCAAA;;oBAAyC,SAAA,CAAU,EAAV,EAAc,WAAd,EAA2B,SAA3B;;;cACxC,EAAE,CAAC,UAAH,GAAgB,UAAU,CAAC;cAC3B,EAAE,CAAC,MAAH,GAAY,UAAU,CAAC;cAEvB,OAAO,CAAC,IAAR,CAAa,EAAb;AAJD;AAFD;iBAOA,QAAA,CAAS,IAAT,EAAe,OAAf;QAfsC,CAAvC;MAL+E,CAAhF;IADqB,CAvGtB;IA8HA,uBAAA,EAAyB,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,SAAlC,EAA6C,QAA7C;aACxB,WAAW,CAAC,QAAZ,CAAqB,MAArB,EAA6B,SAAC,GAAD,EAAM,WAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,UAAA,0BAAa,WAAW,CAAE,eAAb,IAAsB;QACnC,WAAA,GAAc,SAAC,IAAD,EAAO,IAAP,EAAa,EAAb;UACb,IAAgB,qBAAA,IAAiB,IAAI,CAAC,KAAL,GAAa,WAA9C;AAAA,mBAAO,MAAP;;UACA,IAAgB,mBAAA,IAAe,IAAI,CAAC,CAAL,GAAS,SAAxC;AAAA,mBAAO,MAAP;;AACA,iBAAO;QAHM;QAId,SAAA;;AAAa;eAAA,4CAAA;;gBAAqC,WAAA,CAAY,IAAZ,EAAkB,WAAlB,EAA+B,SAA/B;2BAArC,IAAI,CAAC;;AAAL;;;QACb,IAAG,SAAS,CAAC,MAAb;iBACC,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAnD,EAA8D,QAA9D,EADD;SAAA,MAAA;iBAGC,QAAA,CAAA,EAHD;;MAR4B,CAA7B;IADwB,CA9HzB;IA4IA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB,EAA+B,QAA/B;AACnB,UAAA;aAAA,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB;QAAC,GAAA,EAAK;UAAC,GAAA;;AAAM;iBAAA,0CAAA;;2BAAA,QAAA,CAAS,EAAT;AAAA;;cAAP;SAAN;OAAnB,EAAoE;QAAC,GAAA,EAAI,CAAL;OAApE,EAA6E,SAAC,GAAD,EAAM,WAAN;AAC5E,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,UAAA;;AAAc;eAAA,0CAAA;;yBAAA,EAAE,CAAC,QAAH,CAAA;AAAA;;;QACd,aAAA;;AAAiB;eAAA,6CAAA;;yBAAA,IAAI,CAAC,GAAG,CAAC,QAAT,CAAA;AAAA;;;QACjB,cAAA,GAAiB,CAAC,CAAC,UAAF,CAAa,UAAb,EAAyB,aAAzB;QACjB,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,eAAA,aAArB;UAAoC,YAAA,UAApC;UAAgD,gBAAA,cAAhD;SAAX,EAA4E,gBAA5E;QACA,IAAqB,cAAc,CAAC,MAAf,KAAyB,CAA9C;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,KAAK,CAAC,SAAN,CAAgB,cAAhB,EAAgC,CAAhC,EAAmC,SAAC,OAAD,EAAU,EAAV;iBAClC,QAAQ,CAAC,aAAT,CAAuB,UAAvB,EAAmC,MAAnC,EAA2C,OAA3C,EAAoD,EAApD;QADkC,CAAnC,EAEE,SAAC,GAAD;UACD,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;WAAX,EAAiC,kBAAjC;iBACA,QAAA,CAAA;QAHC,CAFF;MAP4E,CAA7E;IADmB,CA5IpB;IA6JA,mBAAA,EAAqB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aAEpB,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB;QAAC,UAAA,EAAY,QAAA,CAAS,UAAT,CAAb;OAAnB,EAAsD;QAAC,IAAA,EAAK,KAAN;OAAtD,CAAmE,CAAC,IAApE,CAAyE;QAAC,aAAA,EAAc,CAAC,CAAhB;OAAzE,EAA6F,SAAC,GAAD,EAAM,KAAN;AAC5F,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,QAAA,GAAW;QACX,OAAA,GAAU;AACV,aAAA,uCAAA;;UACC,QAAQ,CAAC,IAAT,CAAc,IAAd;UACA,OAAQ,CAAA,IAAI,CAAC,GAAL,CAAR,GAAoB;AAFrB;eAGA,EAAE,CAAC,eAAe,CAAC,IAAnB,CAAwB;UAAC,UAAA,EAAY,QAAA,CAAS,UAAT,CAAb;SAAxB,EAA4D,SAAC,GAAD,EAAM,OAAN;AAC3D,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;AACA,eAAA,2CAAA;;AACC;AAAA,iBAAA,wCAAA;;oBAA6B,CAAI,OAAQ,CAAA,IAAI,CAAC,GAAL;;;cACxC,IAAI,CAAC,UAAL,GAAkB,KAAK,CAAC;cACxB,IAAI,CAAC,MAAL,GAAc,KAAK,CAAC;cACpB,IAAI,CAAC,SAAL,GAAiB;cACjB,QAAQ,CAAC,IAAT,CAAc,IAAd;cACA,OAAQ,CAAA,IAAI,CAAC,GAAL,CAAR,GAAoB;AALrB;AADD;iBAOA,QAAA,CAAS,IAAT,EAAe,IAAI,eAAJ,CAAoB,QAApB,EAA8B,MAA9B,EAAsC,WAAW,CAAC,WAAlD,CAAf;QAT2D,CAA5D;MAP4F,CAA7F;IAFoB,CA7JrB;IAiLA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aACZ,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB;QAAC,GAAA,EAAK,OAAN;OAAtB,EAAsC,SAAC,GAAD,EAAM,IAAN;QACrC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAO,YAAP;iBACC,QAAQ,CAAC,aAAT,CAAuB,UAAvB,EAAmC,MAAnC,EAA2C,OAA3C,EAAoD,QAApD,EADD;SAAA,MAEK,IAAG,wBAAA,IAAoB,IAAI,CAAC,SAAL,KAAkB,KAAzC;iBAGJ,WAAW,CAAC,WAAZ,CAAwB,IAAxB,EAA8B,QAA9B,EAHI;SAAA,MAAA;iBAOJ,QAAA,CAAS,IAAT,EAAe,IAAf,EAPI;;MAJgC,CAAtC;IADY,CAjLb;IA+LA,WAAA,EAAa,SAAC,IAAD,EAAO,QAAP;MACZ,IAAG,IAAI,CAAC,SAAL,GAAiB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B,CAApB;eAEC,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;UAC3B,KAAA,EAAO;YAAC,GAAA,EAAK,IAAI,CAAC,GAAX;WADoB;UAE3B,MAAA,EAAQ;YAAC,IAAA,EAAM;cAAC,SAAA,EAAW,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B,CAAZ;aAAP;WAFmB;SAA5B,EAGG,SAAC,GAAD;AACF,iBAAO,QAAA,CAAS,GAAT,EAAc,IAAd;QADL,CAHH,EAFD;OAAA,MAAA;eAQC,QAAA,CAAS,IAAT,EAAe,IAAf,EARD;;IADY,CA/Lb;IA4MA,QAAA,EAAU,SAAC,MAAD,EAAS,QAAT;aACT,EAAE,CAAC,eAAe,CAAC,OAAnB,CAA2B;QAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;OAA3B,EAA8D,QAA9D;IADS,CA5MV;IA+MA,gBAAA,EAAkB,SAAC,MAAD,EAAS,OAAT,EAAkB,QAAlB;aACjB,EAAE,CAAC,eAAe,CAAC,OAAnB,CAA2B;QAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;QAAkC,WAAA,EAAa,OAA/C;OAA3B,EAAoF;QAAC,SAAA,EAAU,CAAX;OAApF,EAAmG,QAAnG;IADiB,CA/MlB;IAkNA,oBAAA,EAAsB,SAAC,MAAD,EAAS,QAAT;aACrB,EAAE,CAAC,eAAe,CAAC,OAAnB,CAA2B;QAAC,GAAA,EAAK,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAN;OAA3B,EAA+D;QAAC,KAAA,EAAM;UAAC,MAAA,EAAO,CAAC,CAAT;SAAP;OAA/D,EAAoF,SAAC,GAAD,EAAM,SAAN;QACnF,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,iBAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,QAAA,CAAS,IAAT,EAAc,SAAU,CAAA,CAAA,CAAxB;MAHmF,CAApF;IADqB,CAlNtB;IAwNA,gBAAA,EAAkB,SAAC,MAAD,EAAS,QAAT;aACjB,WAAW,CAAC,QAAZ,CAAqB,MAArB,EAA6B,SAAC,GAAD,EAAM,KAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;AACA;AAAA,aAAA,sCAAA;;UACC,KAAM,CAAA,IAAI,CAAC,GAAL,CAAN,GAAkB;AADnB;eAEA,QAAA,CAAS,IAAT,EAAe,KAAf;MAL4B,CAA7B;IADiB,CAxNlB;IAgOA,eAAA,EAAiB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aAChB,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,GAAD,EAAM,KAAN;QAElD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,KAA7D,EAAoE,QAApE;MAJkD,CAAnD;IADgB,CAhOjB;IAuOA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aAEZ,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,GAAD,EAAM,QAAN;QAClD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,kBAAJ,IAAiB,QAAQ,CAAC,MAAT,KAAmB,CAAzD;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,QAA7D,EAAuE,SAAC,GAAD;UACtE,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,UAAA,QAArB;WAAX,EAA2C,0BAA3C;iBACA,QAAA,CAAA;QAHsE,CAAvE;MAHkD,CAAnD;IAFY,CAvOb;IAiPA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;AACnB,UAAA;MAAA,KAAA,GAAQ;QAAE,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAV;QAAuC,SAAA,EAAW;UAAC,OAAA,EAAQ,KAAT;SAAlD;;aACR,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,KAAnB,EAA0B;QAAC,IAAA,EAAK,KAAN;OAA1B,CAAuC,CAAC,IAAxC,CAA6C;QAAC,CAAA,EAAE,CAAH;OAA7C,EAAoD,SAAC,GAAD,EAAM,KAAN;AACnD,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;QACA,IAAqB,kBAAI,KAAK,CAAE,gBAAhC;AAAA,iBAAO,QAAA,CAAA,EAAP;;QACA,IAAA,GAAO,KAAK,CAAC,GAAN,CAAA;QACP,IAAoB,IAAI,CAAC,SAAzB;UAAA,KAAK,CAAC,IAAN,CAAW,IAAX,EAAA;;eACA,QAAA,CAAS,IAAT,EAAe,KAAf;MANmD,CAApD;IAFmB,CAjPpB;IA2PA,SAAA,EAAW,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;AACV,UAAA;MAAA,KAAA,GAAQ;QAAE,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAV;QAAuC,SAAA,EAAW;UAAC,OAAA,EAAQ,KAAT;SAAlD;;aACR,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,KAAnB,EAA0B;QAAC,IAAA,EAAK,KAAN;OAA1B,CAAuC,CAAC,IAAxC,CAA6C;QAAC,CAAA,EAAE,CAAH;OAA7C,EAAoD,SAAC,GAAD,EAAM,KAAN;QACnD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;QACA,IAAqB,kBAAI,KAAK,CAAE,gBAAhC;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,KAAf;MAJmD,CAApD;IAFU,CA3PX;IAmQA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACnB,WAAW,CAAC,gBAAZ,CAA6B,MAA7B,EAAqC,SAAC,GAAD,EAAM,WAAN;QACpC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;eACA,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,GAAD,EAAM,YAAN;AAClD,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,IAAyB,oBAAzB;AAAA,mBAAO,QAAA,CAAA,EAAP;;UAEA,QAAA;;AAAY;iBAAA,8CAAA;;kBAAuC;6BAAvC;;AAAA;;;UACZ,QAAA;;AAAY;iBAAA,0CAAA;;2BAAA,CAAC,CAAC,IAAF,CAAO,IAAP,EAAa,QAAb,EAAuB,YAAvB,EAAqC,GAArC,EAA0C,IAA1C,EAAgD,cAAhD,EAAgE,WAAhE;AAAA;;;UACZ,IAAG,QAAQ,CAAC,MAAZ;YACC,MAAM,CAAC,GAAP,CAAW;cAAC,YAAA,UAAD;cAAa,QAAA,MAAb;cAAqB,CAAA,EAAG,QAAQ,CAAC,MAAjC;aAAX,EAAqD,iBAArD,EADD;;iBAEA,QAAA,CAAS,IAAT,EAAe,QAAf;QARkD,CAAnD;MAFoC,CAArC;IADmB,CAnQpB;IAgRA,4BAAA,EAA8B,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB,EAA+B,QAA/B;aAC7B,WAAW,CAAC,WAAZ,CACC,IAAI,CAAC,gBAAL,CAAsB;QAAC,QAAA,MAAD;OAAtB,CADD,EAEC,SAAC,WAAD;eACC,WAAW,CAAC,qBAAZ,CAAkC,UAAlC,EAA8C,MAA9C,EAAsD,QAAtD,EAAgE,WAAhE;MADD,CAFD,EAIC,QAJD;IAD6B,CAhR9B;IAwRA,qBAAA,EAAuB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB,EAA+B,QAA/B;aACtB,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;SADyB;QAEhC,MAAA,EACC;UAAA,YAAA,EAAc;YAAA,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CAAZ;WAAd;UACA,KAAA,EACC;YAAA,KAAA,EAAO;cAAC,KAAA,EAAO,QAAR;cAAkB,KAAA,EAAO;gBAAC,CAAA,EAAG,CAAJ;eAAzB;aAAP;WAFD;SAH+B;QAMhC,MAAA,EAAQ,IANwB;OAAjC,EAOG,QAPH;IADsB,CAxRvB;IAoSA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;AACZ,UAAA;MAAA,gBAAA,GAAmB,SAAC,GAAD,EAAM,EAAN;QAClB,IAAG,WAAH;iBACC,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,OAA7D,EAAsE,SAAC,IAAD;YACrE,IAAmB,YAAnB;AAAA,qBAAO,EAAA,CAAG,IAAH,EAAP;;AACA,mBAAO,EAAA,CAAG,GAAH;UAF8D,CAAtE,EADD;SAAA,MAAA;iBAKC,EAAA,CAAA,EALD;;MADkB;aAOnB,KAAK,CAAC,MAAN,CAAa;QACZ,SAAC,EAAD;iBACC,WAAW,CAAC,yBAAZ,CAAsC,UAAtC,EAAkD,MAAlD,EAA0D,OAA1D,EAAmE,EAAnE;QADD,CADY,EAGZ,SAAC,EAAD;iBACC,WAAW,CAAC,2BAAZ,CAAwC,UAAxC,EAAoD,MAApD,EAA4D,OAA5D,EAAqE,EAArE;QADD,CAHY,EAKZ,SAAC,EAAD;iBACC,QAAQ,CAAC,WAAT,CAAqB,UAArB,EAAiC,MAAjC,EAAyC,OAAzC,EAAkD,SAAC,GAAD;mBACjD,gBAAA,CAAiB,GAAjB,EAAsB,EAAtB;UADiD,CAAlD;QADD,CALY,EAQZ,SAAC,EAAD;iBACC,WAAW,CAAC,iBAAZ,CAA8B,UAA9B,EAA0C,MAA1C,EAAkD,OAAlD,EAA2D,SAAC,GAAD;mBAC1D,gBAAA,CAAiB,GAAjB,EAAsB,EAAtB;UAD0D,CAA3D;QADD,CARY,EAWZ,SAAC,EAAD;iBACC,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,OAAnD,EAA4D,EAA5D;QADD,CAXY,EAaZ,SAAC,EAAD;iBACC,WAAW,CAAC,oBAAZ,CAAiC,UAAjC,EAA6C,MAA7C,EAAqD,OAArD,EAA8D,QAA9D;QADD,CAbY;OAAb,EAeG,QAfH;IARY,CApSb;IA8TA,iBAAA,EAAmB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aAClB,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB;QAAC,GAAA,EAAK,OAAN;OAAtB,EAAsC,SAAC,GAAD,EAAM,IAAN;QACrC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAmD,YAAnD;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT,EAAP;;eACA,QAAQ,CAAC,gBAAT,CAA0B,UAA1B,EAAsC,MAAtC,EAA8C,OAA9C,EAAuD,SAAC,GAAD,EAAM,MAAN;AACtD,cAAA;UAAA,OAAO,MAAM,CAAC;UACd,OAAO,IAAI,CAAC;AAEZ;AAAA,eAAA,sCAAA;;YACC,IAA2B,MAAO,CAAA,GAAA,CAAI,CAAC,MAAZ,CAAmB,IAAK,CAAA,GAAA,CAAxB,CAA3B;cAAA,MAAO,CAAA,GAAA,CAAP,GAAc,IAAK,CAAA,GAAA,EAAnB;;AADD;AAEA;AAAA,eAAA,gDAAA;;YACC,IAA6B,gBAAA,IAAY,EAAE,CAAC,GAAG,CAAC,MAAP,CAAc,IAAI,CAAC,IAAK,CAAA,CAAA,CAAE,CAAC,GAA3B,CAAzC;cAAA,EAAE,CAAC,GAAH,GAAS,IAAI,CAAC,IAAK,CAAA,CAAA,CAAE,CAAC,IAAtB;;AADD;UAEA,IAAG,CAAC,CAAC,OAAF,CAAU,IAAV,EAAgB,MAAhB,CAAH;mBACC,QAAA,CAAA,EADD;WAAA,MAAA;YAGC,MAAM,CAAC,GAAP,CAAW;cAAC,MAAA,IAAD;cAAO,QAAA,MAAP;cAAe,QAAA,EAAU,IAAI,CAAC,SAAL,CAAe,IAAf,CAAA,KAAwB,IAAI,CAAC,SAAL,CAAe,MAAf,CAAjD;aAAX,EAAqF,iCAArF;mBACA,QAAA,CAAS,IAAI,KAAJ,CAAU,qDAAV,CAAT,EAJD;;QARsD,CAAvD;MAHqC,CAAtC;IADkB,CA9TnB;IAiVA,YAAA,EAAc,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACb,WAAW,CAAC,SAAZ,CAAsB,UAAtB,EAAkC,MAAlC,EAA0C,SAAC,GAAD,EAAM,KAAN;QACzC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAqB,kBAAI,KAAK,CAAE,gBAAhC;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,cAAZ,CAA2B,UAA3B,EAAuC,MAAvC,EAA+C,KAAM,CAAA,CAAA,CAAE,CAAC,GAAxD,EAA6D,QAA7D;MAHyC,CAA1C;IADa,CAjVd;IAuVA,YAAA,EAAc,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACb,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,IAAxD,EAA8D,IAA9D,EAAoE,QAApE;IADa,CAvVd;IA6VA,cAAA,EAAgB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;AACf,UAAA;MAAA,aAAA,GAAgB,SAAC,GAAD;eACf,WAAW,CAAC,iBAAZ,CAA8B,UAA9B,EAA0C,MAA1C,EAAkD,OAAlD,EAA2D,SAAC,IAAD;UAC1D,IAAyB,YAAzB;AAAA,mBAAO,QAAA,CAAS,IAAT,EAAP;;iBACA,QAAA,CAAS,GAAT;QAF0D,CAA3D;MADe;MAIhB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,sBAAjC;aACA,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB;QAAC,GAAA,EAAI,OAAL;OAAtB,EAAqC,SAAC,GAAD,EAAM,IAAN;QACpC,IAA6B,WAA7B;AAAA,iBAAO,aAAA,CAAc,GAAd,EAAP;;QACA,IAA8B,YAA9B;AAAA,iBAAO,aAAA,CAAA,EAAP;;QACA,IAAqB,sBAArB;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,gBAAZ,CAA6B,UAA7B,EAAyC,MAAzC,EAAiD,IAAI,CAAC,GAAtD,EAA2D,IAA3D,EAAiE,SAAC,GAAD;UAChE,IAA6B,WAA7B;AAAA,mBAAO,aAAA,CAAc,GAAd,EAAP;;iBACA,WAAW,CAAC,mBAAZ,CAAgC,UAAhC,EAA4C,MAA5C,EAAoD,SAAC,GAAD;YACnD,IAA6B,WAA7B;AAAA,qBAAO,aAAA,CAAc,GAAd,EAAP;;mBACA,WAAW,CAAC,mBAAZ,CAAgC,UAAhC,EAA4C,MAA5C,EAAoD,SAAC,GAAD,EAAM,eAAN;cACnD,IAA6B,WAA7B;AAAA,uBAAO,aAAA,CAAc,GAAd,EAAP;;cACA,IAAG,4BAAI,eAAe,CAAE,gBAAxB;gBACC,MAAM,CAAC,GAAP,CAAW;kBAAC,YAAA,UAAD;kBAAa,QAAA,MAAb;iBAAX,EAAiC,yBAAjC;AACA,uBAAO,aAAA,CAAA,EAFR;;qBAGA,KAAK,CAAC,UAAN,CAAiB,eAAjB,EAAkC,SAAC,IAAD,EAAO,EAAP;uBACjC,WAAW,CAAC,WAAZ,CAAwB,UAAxB,EAAoC,MAApC,EAA4C,IAAI,CAAC,GAAjD,EAAsD,EAAtD;cADiC,CAAlC,EAEE,SAAC,GAAD;gBACD,IAA6B,WAA7B;AAAA,yBAAO,aAAA,CAAc,GAAd,EAAP;;gBACA,MAAM,CAAC,GAAP,CAAY;kBAAC,YAAA,UAAD;kBAAa,QAAA,MAAb;iBAAZ,EAAkC,iBAAlC;uBACA,aAAA,CAAA;cAHC,CAFF;YALmD,CAApD;UAFmD,CAApD;QAFgE,CAAjE;MAJoC,CAArC;IANe,CA7VhB;IAuXA,gBAAA,EAAkB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,IAA9B,EAAoC,QAApC;AACjB,UAAA;MAAA,EAAA,GAAK,IAAI,CAAC,EAAL,GAAU,CAAC,IAAA,GAAO,IAAR;MACf,CAAA,GAAI,IAAI,CAAC,CAAL,GAAS;MACb,GAAA,GAAM,CAAC,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,IAAI,CAAC,IAAI,CAAC,MAAxB,CAAA,GAAkC;MACxC,IAAG,GAAA,GAAM,EAAT;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;SAAX,EAA+C,uBAA/C;AACA,eAAO,QAAA,CAAA,EAFR;;MAIA,iBAAA,GAAoB,EAAA,GAAK;MACzB,IAAG,EAAA,GAAK,iBAAL,IAA0B,CAAA,GAAI,iBAA9B,IAAmD,GAAA,GAAM,EAA5D;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;UAAmC,mBAAA,iBAAnC;UAAsD,IAAA,EAAtD;UAA0D,GAAA,CAA1D;SAAX,EAAyE,yBAAzE;eACA,WAAW,CAAC,2BAAZ,CAAwC,UAAxC,EAAoD,MAApD,EAA4D,OAA5D,EAAqE,QAArE,EAFD;OAAA,MAAA;QAIC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;UAAmC,mBAAA,iBAAnC;UAAsD,IAAA,EAAtD;UAA0D,GAAA,CAA1D;SAAX,EAAyE,iCAAzE;eACA,QAAA,CAAA,EALD;;IATiB,CAvXlB;IAuYA,2BAAA,EAA6B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aAC5B,WAAW,CAAC,WAAZ,CACC,IAAI,CAAC,WAAL,CAAiB;QAAC,QAAA,MAAD;OAAjB,CADD,EAEC,SAAC,WAAD;eACC,WAAW,CAAC,oBAAZ,CAAiC,UAAjC,EAA6C,MAA7C,EAAqD,OAArD,EAA8D,WAA9D;MADD,CAFD,EAIC,QAJD;IAD4B,CAvY7B;IA+YA,oBAAA,EAAsB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MACrB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,2BAA1C;aACA,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAC3B,KAAA,EAAO;UAAC,GAAA,EAAK,OAAN;SADoB;QAE3B,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,SAAA,EAAW,IAAZ;WAAP;SAFmB;OAA5B,EAGG,QAHH;IAFqB,CA/YtB;IAuZA,mBAAA,EAAqB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;MACpB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,qBAAjC;aACA,WAAW,CAAC,gBAAZ,CAA6B,MAA7B,EAAqC,SAAC,GAAD,EAAM,KAAN;QACpC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAO,aAAP;iBACC,WAAW,CAAC,eAAZ,CAA4B,UAA5B,EAAwC,MAAxC,EAAgD,QAAhD,EADD;SAAA,MAAA;iBAGC,WAAW,CAAC,WAAZ,CAAwB,UAAxB,EAAoC,MAApC,EAA4C,QAA5C,EAHD;;MAFoC,CAArC;IAFoB,CAvZrB;IAgaA,iBAAA,EAAmB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAClB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,yBAA1C;aACA,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAC3B,KAAA,EAAO;UAAC,GAAA,EAAK,OAAN;SADoB;QAE3B,MAAA,EAAQ;UAAC,YAAA,EAAc;YAAC,cAAA,EAAe,IAAhB;WAAf;SAFmB;OAA5B,EAGG,QAHH;IAFkB,CAhanB;IAuaA,mBAAA,EAAqB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACpB,WAAW,CAAC,QAAZ,CAAqB,MAArB,EAA6B,SAAC,GAAD,EAAM,WAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,UAAA,0BAAa,WAAW,CAAE,eAAb,IAAsB;QACnC,eAAA;;AAAmB;eAAA,4CAAA;;gBAAqC;2BAArC;;AAAA;;;QACnB,IAAG,eAAe,CAAC,MAAnB;UACC,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,CAAA,EAAG,eAAe,CAAC,MAAxC;WAAX,EAA4D,uBAA5D,EADD;;eAEA,QAAA,CAAS,IAAT,EAAe,eAAf;MAN4B,CAA7B;IADoB,CAvarB;IAkbA,yBAAA,EAA2B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAC1B,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,iCAA1C;aACA,WAAW,CAAC,gBAAZ,CAA6B,MAA7B,EAAqC,OAArC,EAA8C,SAAC,GAAD,EAAM,MAAN;QAC7C,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAA4D,cAA5D;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,yBAAV,CAAT,EAAP;;QACA,IAAG,MAAM,CAAC,IAAV;AACC,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,6BAAV,CAAT,EADR;SAAA,MAEK,IAAG,mBAAH;AACJ,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,oCAAV,CAAT,EADH;SAAA,MAAA;AAGJ,iBAAO,QAAA,CAAA,EAHH;;MALwC,CAA9C;IAF0B,CAlb3B;IA8bA,2BAAA,EAA6B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAC5B,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,4CAAjC;aACA,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;UAAmC,KAAA,EAAO;YAAC,UAAA,EAAY;cAAC,KAAA,EAAO,OAAR;cAAiB,IAAA,EAAM;gBAAC,OAAA,EAAQ,KAAT;eAAvB;aAAb;WAA1C;SADyB;QAEhC,MAAA,EAAS;UAAE,SAAA,EAAW,CAAb;SAFuB;QAGhC,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,cAAA,EAAe,KAAhB;WAAP;SAHwB;OAAjC,EAIG,SAAC,GAAD,EAAM,MAAN;QACF,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAmE,cAAnE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gCAAV,CAAT,EAAP;;QACA,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,+BAA1C;eACA,QAAA,CAAA;MAJE,CAJH;IAF4B,CA9b7B;IA0cA,4BAAA,EAA8B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAC7B,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,iCAA1C;aACA,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;UAAkC,OAAA,EAAU;YAAC,UAAA,EAAY;cAAC,KAAA,EAAO,OAAR;cAAiB,IAAA,EAAM,KAAvB;aAAb;WAA5C;SADyB;QAEhC,MAAA,EAAS;UAAE,SAAA,EAAW,CAAb;SAFuB;QAGhC,MAAA,EAAQ;UAAC,MAAA,EAAQ;YAAC,cAAA,EAAe,IAAhB;WAAT;SAHwB;OAAjC,EAIG,QAJH;IAF6B,CA1c9B;IAkdA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MACnB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,0BAA1C;aACA,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;UAAkC,OAAA,EAAU;YAAC,UAAA,EAAY;cAAC,KAAA,EAAO,OAAR;cAAiB,IAAA,EAAM,KAAvB;aAAb;WAA5C;SADyB;QAEhC,MAAA,EAAQ;UAAE,SAAA,EAAW,CAAb;SAFwB;QAGhC,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,cAAA,EAAe,IAAhB;WAAP;SAHwB;OAAjC,EAIG,SAAC,GAAD,EAAM,MAAN;QACF,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAsE,cAAtE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,mCAAV,CAAT,EAAP;;QACA,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,oBAA1C;eACA,QAAA,CAAA;MAJE,CAJH;IAFmB,CAldpB;IA8dA,oBAAA,EAAsB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aACrB,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAC3B,KAAA,EAAO;UAAC,GAAA,EAAK,OAAN;SADoB;QAE3B,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,SAAA,EAAW,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAE,IAAxB,CAAZ;WAAP;SAFmB;OAA5B,EAGG,SAAC,GAAD;QACF,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,oBAA1C;eACA,QAAA,CAAA;MAFE,CAHH;IADqB,CA9dtB;;AAjDD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index f24ddf5fce..e6a02fef3d 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -52,8 +52,8 @@ if (!source.match(/^[0-9]+$/)) { } return result1 })() - pending = _.filter(result, row => - __guard__(row != null ? row.doc_id : undefined, x => + pending = _.filter(result, (row) => + __guard__(row != null ? row.doc_id : undefined, (x) => x.match(/^[a-f0-9]{24}$/) ) ) @@ -62,12 +62,12 @@ if (!source.match(/^[0-9]+$/)) { } let shutDownRequested = false -const shutDownTimer = setTimeout(function() { +const shutDownTimer = setTimeout(function () { logger.log('pack timed out, requesting shutdown') // start the shutdown on the next pack shutDownRequested = true // do a hard shutdown after a further 5 minutes - const hardTimeout = setTimeout(function() { + const hardTimeout = setTimeout(function () { logger.error('HARD TIMEOUT in pack archive worker') return process.exit() }, 5 * 60 * 1000) @@ -79,8 +79,8 @@ logger.log( ) // work around for https://github.com/mafintosh/mongojs/issues/224 -db.close = function(callback) { - return this._getServer(function(err, server) { +db.close = function (callback) { + return this._getServer(function (err, server) { if (err != null) { return callback(err) } @@ -90,20 +90,20 @@ db.close = function(callback) { }) } -const finish = function() { +const finish = function () { if (shutDownTimer != null) { logger.log('cancelling timeout') clearTimeout(shutDownTimer) } logger.log('closing db') - return db.close(function() { + return db.close(function () { logger.log('closing LockManager Redis Connection') - return LockManager.close(function() { + return LockManager.close(function () { logger.log( { processedCount: COUNT, allCount: TOTAL }, 'ready to exit from pack archive worker' ) - const hardTimeout = setTimeout(function() { + const hardTimeout = setTimeout(function () { logger.error('hard exit from pack archive worker') return process.exit(1) }, 5 * 1000) @@ -112,12 +112,12 @@ const finish = function() { }) } -process.on('exit', code => logger.log({ code }, 'pack archive worker exited')) +process.on('exit', (code) => logger.log({ code }, 'pack archive worker exited')) -const processUpdates = pending => +const processUpdates = (pending) => async.eachSeries( pending, - function(result, callback) { + function (result, callback) { let _id ;({ _id, project_id, doc_id } = result) COUNT++ @@ -129,7 +129,7 @@ const processUpdates = pending => ) return callback() } - const handler = function(err, result) { + const handler = function (err, result) { if (err != null && err.code === 'InternalError' && err.retryable) { logger.warn( { err, result }, @@ -154,7 +154,7 @@ const processUpdates = pending => return PackManager.processOldPack(project_id, doc_id, _id, handler) } }, - function(err, results) { + function (err, results) { if (err != null && err.message !== 'shutdown') { logger.error({ err }, 'error in pack archive worker processUpdates') } @@ -163,7 +163,7 @@ const processUpdates = pending => ) // find the packs which can be archived -const ObjectIdFromDate = function(date) { +const ObjectIdFromDate = function (date) { const id = Math.floor(date.getTime() / 1000).toString(16) + '0000000000000000' return ObjectId(id) } @@ -191,13 +191,13 @@ if (pending != null) { .sort({ last_checked: 1 }) - .limit(LIMIT, function(err, results) { + .limit(LIMIT, function (err, results) { if (err != null) { logger.log({ err }, 'error checking for updates') finish() return } - pending = _.uniq(results, false, result => result.doc_id.toString()) + pending = _.uniq(results, false, (result) => result.doc_id.toString()) TOTAL = pending.length logger.log(`found ${TOTAL} documents to archive`) return processUpdates(pending) diff --git a/services/track-changes/app/js/PackWorker.js.map b/services/track-changes/app/js/PackWorker.js.map new file mode 100644 index 0000000000..ff2a9b07af --- /dev/null +++ b/services/track-changes/app/js/PackWorker.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "PackWorker.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/PackWorker.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,MAAuB,OAAA,CAAQ,WAAR,CAAvB,EAAC,WAAD,EAAK,uBAAL,EAAe;;EACf,EAAA,GAAK,OAAA,CAAQ,IAAR;;EACL,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,OAAO,CAAC,UAAR,CAAmB,eAAnB;;EACA,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,MAAM,CAAC,UAAP,CAAkB,0BAAlB;;EACA,IAAG,8DAAH;IACC,MAAM,CAAC,wBAAP,CAAgC,QAAQ,CAAC,MAAM,CAAC,GAAhD,EADD;;;EAGA,IAAA,GAAO,EAAA,GAAK,IAAL,GAAY;;EAEnB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,WAAA,GAAc,OAAA,CAAQ,eAAR;;EAKd,MAAA,GAAS,OAAO,CAAC,IAAK,CAAA,CAAA;;EACtB,mBAAA,GAAsB,MAAA,CAAO,OAAO,CAAC,IAAK,CAAA,CAAA,CAApB,CAAA,IAA2B;;EACjD,OAAA,GAAU,MAAA,CAAO,OAAO,CAAC,IAAK,CAAA,CAAA,CAApB,CAAA,IAA2B,EAAA,GAAG,EAAH,GAAM;;EAC3C,KAAA,GAAQ;;EACR,KAAA,GAAQ;;EAER,IAAG,CAAC,MAAM,CAAC,KAAP,CAAa,UAAb,CAAJ;IACC,IAAA,GAAO,EAAE,CAAC,YAAH,CAAgB,MAAhB;IACP,MAAA;;AAAS;AAAA;WAAA,sCAAA;;QACR,OAAuB,IAAI,CAAC,KAAL,CAAW,GAAX,CAAvB,EAAC,oBAAD,EAAa;sBACb;UAAC,QAAA,MAAD;UAAS,YAAA,UAAT;;AAFQ;;;IAGT,OAAA,GAAU,CAAC,CAAC,MAAF,CAAS,MAAT,EAAiB,SAAC,GAAD;AAAS,UAAA;6DAAW,CAAE,KAAb,CAAmB,gBAAnB;IAAT,CAAjB,EALX;GAAA,MAAA;IAOC,KAAA,GAAQ,MAAA,CAAO,OAAO,CAAC,IAAK,CAAA,CAAA,CAApB,CAAA,IAA2B,KAPpC;;;EASA,iBAAA,GAAoB;;EACpB,aAAA,GAAgB,UAAA,CAAW,SAAA;AAC1B,QAAA;IAAA,MAAM,CAAC,GAAP,CAAW,qCAAX;IAEA,iBAAA,GAAoB;IAEpB,WAAA,GAAc,UAAA,CAAW,SAAA;MACxB,MAAM,CAAC,KAAP,CAAa,qCAAb;aACA,OAAO,CAAC,IAAR,CAAA;IAFwB,CAAX,EAGZ,CAAA,GAAE,EAAF,GAAK,IAHO;WAId,WAAW,CAAC,KAAZ,CAAA;EAT0B,CAAX,EAUd,OAVc;;EAYhB,MAAM,CAAC,GAAP,CAAW,8BAAA,GAA+B,KAA/B,GAAqC,UAArC,GAA+C,mBAA/C,GAAmE,YAAnE,GAA+E,OAA1F;;EAGA,EAAE,CAAC,KAAH,GAAY,SAAC,QAAD;WACX,IAAI,CAAC,UAAL,CAAgB,SAAC,GAAD,EAAM,MAAN;MACf,IAAwB,WAAxB;AAAA,eAAO,QAAA,CAAS,GAAT,EAAP;;MACA,MAAA,GAAY,sBAAH,GAAwB,MAAxB,GAAoC,MAAM,CAAC;MACpD,MAAM,CAAC,OAAP,CAAe,IAAf,EAAqB,IAArB;aACA,QAAA,CAAA;IAJe,CAAhB;EADW;;EAOZ,MAAA,GAAS,SAAA;IACR,IAAG,qBAAH;MACC,MAAM,CAAC,GAAP,CAAW,oBAAX;MACA,YAAA,CAAa,aAAb,EAFD;;IAGA,MAAM,CAAC,GAAP,CAAW,YAAX;WACA,EAAE,CAAC,KAAH,CAAS,SAAA;MACR,MAAM,CAAC,GAAP,CAAW,sCAAX;aACA,WAAW,CAAC,KAAZ,CAAkB,SAAA;AACjB,YAAA;QAAA,MAAM,CAAC,GAAP,CAAW;UAAC,cAAA,EAAgB,KAAjB;UAAwB,QAAA,EAAU,KAAlC;SAAX,EAAqD,wCAArD;QACA,WAAA,GAAc,UAAA,CAAW,SAAA;UACxB,MAAM,CAAC,KAAP,CAAa,oCAAb;iBACA,OAAO,CAAC,IAAR,CAAa,CAAb;QAFwB,CAAX,EAGZ,CAAA,GAAE,IAHU;eAId,WAAW,CAAC,KAAZ,CAAA;MANiB,CAAlB;IAFQ,CAAT;EALQ;;EAeT,OAAO,CAAC,EAAR,CAAW,MAAX,EAAmB,SAAC,IAAD;WAClB,MAAM,CAAC,GAAP,CAAW;MAAC,MAAA,IAAD;KAAX,EAAmB,4BAAnB;EADkB,CAAnB;;EAGA,cAAA,GAAiB,SAAC,OAAD;WAChB,KAAK,CAAC,UAAN,CAAiB,OAAjB,EAA0B,SAAC,MAAD,EAAS,QAAT;AACzB,UAAA;MAAC,gBAAD,EAAM,8BAAN,EAAkB;MAClB,KAAA;MACA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,aAAA,GAAc,KAAd,GAAoB,GAApB,GAAuB,KAAxD;MACA,IAAO,oBAAJ,IAAuB,gBAA1B;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;SAAX,EAAiC,uCAAjC;AACA,eAAO,QAAA,CAAA,EAFR;;MAGA,OAAA,GAAU,SAAC,GAAD,EAAM,MAAN;QACT,IAAG,aAAA,IAAS,GAAG,CAAC,IAAJ,KAAY,eAArB,IAAyC,GAAG,CAAC,SAAhD;UACC,MAAM,CAAC,IAAP,CAAY;YAAC,KAAA,GAAD;YAAM,QAAA,MAAN;WAAZ,EAA2B,0CAA3B;UAEA,GAAA,GAAM,KAHP;;QAIA,IAAG,WAAH;UACC,MAAM,CAAC,KAAP,CAAa;YAAC,KAAA,GAAD;YAAM,QAAA,MAAN;WAAb,EAA4B,8BAA5B;AACA,iBAAO,QAAA,CAAS,GAAT,EAFR;;QAGA,IAAG,iBAAH;UACC,MAAM,CAAC,IAAP,CAAY,mCAAZ;AACA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,UAAV,CAAT,EAFR;;eAGA,UAAA,CAAW,SAAA;iBACV,QAAA,CAAS,GAAT,EAAc,MAAd;QADU,CAAX,EAEE,mBAFF;MAXS;MAcV,IAAO,WAAP;eACC,WAAW,CAAC,YAAZ,CAAyB,UAAzB,EAAqC,MAArC,EAA6C,OAA7C,EADD;OAAA,MAAA;eAGC,WAAW,CAAC,cAAZ,CAA2B,UAA3B,EAAuC,MAAvC,EAA+C,GAA/C,EAAoD,OAApD,EAHD;;IArByB,CAA1B,EAyBE,SAAC,GAAD,EAAM,OAAN;MACD,IAAG,aAAA,IAAS,GAAG,CAAC,OAAJ,KAAe,UAA3B;QACC,MAAM,CAAC,KAAP,CAAa;UAAC,KAAA,GAAD;SAAb,EAAoB,6CAApB,EADD;;aAEA,MAAA,CAAA;IAHC,CAzBF;EADgB;;EAiCjB,gBAAA,GAAoB,SAAC,IAAD;AACnB,QAAA;IAAA,EAAA,GAAK,IAAI,CAAC,KAAL,CAAW,IAAI,CAAC,OAAL,CAAA,CAAA,GAAiB,IAA5B,CAAiC,CAAC,QAAlC,CAA2C,EAA3C,CAAA,GAAiD;AACtD,WAAO,QAAA,CAAS,EAAT;EAFY;;EAQpB,IAAG,eAAH;IACC,MAAM,CAAC,GAAP,CAAW,MAAA,GAAO,OAAO,CAAC,MAAf,GAAsB,gBAAtB,GAAsC,MAAjD;IACA,cAAA,CAAe,OAAf,EAFD;GAAA,MAAA;IAIC,UAAA,GAAa,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B;IACb,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB;MAClB,SAAA,EAAW;QAAC,OAAA,EAAS,KAAV;OADO;MAElB,UAAA,EAAY;QAAC,OAAA,EAAS,IAAV;OAFM;MAGlB,KAAA,EAAO;QAAC,OAAA,EAAS,IAAV;OAHW;MAIlB,GAAA,EAAK;QAAC,GAAA,EAAK,gBAAA,CAAiB,UAAjB,CAAN;OAJa;MAKlB,YAAA,EAAc;QAAC,GAAA,EAAK,UAAN;OALI;KAAnB,EAMG;MAAC,GAAA,EAAI,CAAL;MAAQ,MAAA,EAAO,CAAf;MAAkB,UAAA,EAAW,CAA7B;KANH,CAMmC,CAAC,IANpC,CAMyC;MACxC,YAAA,EAAa,CAD2B;KANzC,CAQE,CAAC,KARH,CAQS,KART,EAQgB,SAAC,GAAD,EAAM,OAAN;MACf,IAAG,WAAH;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,KAAA,GAAD;SAAX,EAAkB,4BAAlB;QACA,MAAA,CAAA;AACA,eAHD;;MAIA,OAAA,GAAU,CAAC,CAAC,IAAF,CAAO,OAAP,EAAgB,KAAhB,EAAuB,SAAC,MAAD;eAAY,MAAM,CAAC,MAAM,CAAC,QAAd,CAAA;MAAZ,CAAvB;MACV,KAAA,GAAQ,OAAO,CAAC;MAChB,MAAM,CAAC,GAAP,CAAW,QAAA,GAAS,KAAT,GAAe,uBAA1B;aACA,cAAA,CAAe,OAAf;IARe,CARhB,EALD;;AArHA" +} \ No newline at end of file diff --git a/services/track-changes/app/js/ProjectIterator.js b/services/track-changes/app/js/ProjectIterator.js index 589c68c241..43ddbc7b2f 100644 --- a/services/track-changes/app/js/ProjectIterator.js +++ b/services/track-changes/app/js/ProjectIterator.js @@ -62,7 +62,7 @@ module.exports = ProjectIterator = ProjectIterator = class ProjectIterator { nextPack.project_id, nextPack.doc_id, nextPack._id, - function(err, pack) { + function (err, pack) { if (err != null) { return callback(err) } diff --git a/services/track-changes/app/js/ProjectIterator.js.map b/services/track-changes/app/js/ProjectIterator.js.map new file mode 100644 index 0000000000..f6aa31a7d1 --- /dev/null +++ b/services/track-changes/app/js/ProjectIterator.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "ProjectIterator.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/ProjectIterator.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,IAAA,GAAO,OAAA,CAAQ,MAAR;;EAEP,MAAM,CAAC,OAAP,GAAiB,eAAA,GAEV;IACQ,yBAAC,KAAD,EAAQ,OAAR,EAAiB,aAAjB;AACZ,UAAA;MADoB,IAAC,CAAA,SAAD;MAAS,IAAC,CAAA,gBAAD;MAC7B,OAAA,GAAU,SAAC,CAAD,EAAG,CAAH;eAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAP,GAAgB,CAAC,CAAC,IAAI,CAAC,MAAxB,CAAA,IAAmC,CAAC,CAAC,CAAC,SAAF,GAAc,CAAC,CAAC,SAAjB;MAA5C;MACV,IAAC,CAAA,KAAD,GAAS,KAAK,CAAC,KAAN,CAAA,CAAa,CAAC,IAAd,CAAmB,OAAnB;MACT,IAAC,CAAA,KAAD,GAAS,IAAI,IAAJ,CAAS,OAAT;IAHG;;8BAKb,IAAA,GAAM,SAAC,QAAD;AAGL,UAAA;MAAA,QAAA,GAAW;MACX,MAAA,GAAS,IAAC,CAAA;MACV,KAAA,GAAQ,QAAQ,CAAC;MACjB,WAAA,GAAc;MACd,QAAA,GAAW,QAAQ,CAAC,KAAM,CAAA,CAAA;MAC1B,YAAA,uBAAe,QAAQ,CAAE,IAAI,CAAC,gBAAf,IAAyB;MACxC,QAAA,GAAW,KAAK,CAAC,IAAN,CAAA;AAOX,aAAM,gBAAA,wBAAY,QAAQ,CAAE,IAAI,CAAC,kBAAf,GAA0B,MAA5C;QAEC,QAAQ,CAAC,KAAK,CAAC,KAAf,CAAA;QACA,QAAA,GAAW,QAAQ,CAAC,KAAM,CAAA,CAAA;QAC1B,YAAA,uBAAe,QAAQ,CAAE,IAAI,CAAC,gBAAf,IAAyB;MAJzC;MAMA,IAAG,CAAC,KAAK,CAAC,KAAN,CAAA,CAAA,wBAAiB,QAAQ,CAAE,IAAI,CAAC,gBAAf,IAAyB,YAA3C,CAAA,IAA6D,kBAAhE;AAEC,eAAO,IAAC,CAAA,aAAD,CAAe,QAAQ,CAAC,UAAxB,EAAoC,QAAQ,CAAC,MAA7C,EAAqD,QAAQ,CAAC,GAA9D,EAAmE,SAAC,GAAD,EAAM,IAAN;AACzE,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,QAAQ,CAAC,KAAK,CAAC,KAAf,CAAA;AAEA;AAAA,eAAA,qCAAA;;kBAA8B,gBAAJ,IAAe,EAAE,CAAC,IAAI,CAAC,MAAR,GAAiB;;;YAEzD,EAAE,CAAC,MAAH,GAAY,QAAQ,CAAC;YACrB,EAAE,CAAC,UAAH,GAAgB,QAAQ,CAAC;YACzB,KAAK,CAAC,IAAN,CAAW,EAAX;AAJD;AAMA,iBAAO,QAAQ,CAAC,IAAT,CAAc,QAAd;QAVkE,CAAnE,EAFR;;AAeA,aAAM,kBAAA,IAAc,qBAAC,QAAQ,CAAE,IAAI,CAAC,gBAAf,GAAwB,YAAzB,CAApB;QACC,WAAW,CAAC,IAAZ,CAAiB,QAAjB;QACA,KAAK,CAAC,GAAN,CAAA;QACA,QAAA,GAAW,KAAK,CAAC,IAAN,CAAA;MAHZ;MAQA,IAAG,KAAK,CAAC,KAAN,CAAA,CAAA,IAAsB,kBAAzB;QACC,QAAQ,CAAC,KAAT,GAAiB,KADlB;;aAGA,QAAA,CAAS,IAAT,EAAe,WAAf;IAhDK;;8BAkDN,IAAA,GAAM,SAAA;AACL,aAAO,IAAC,CAAA;IADH;;;;;AA5DR" +} \ No newline at end of file diff --git a/services/track-changes/app/js/RedisManager.js b/services/track-changes/app/js/RedisManager.js index a4d8cb1167..5e762bf45a 100644 --- a/services/track-changes/app/js/RedisManager.js +++ b/services/track-changes/app/js/RedisManager.js @@ -22,7 +22,7 @@ const async = require('async') module.exports = RedisManager = { getOldestDocUpdates(doc_id, batchSize, callback) { if (callback == null) { - callback = function(error, jsonUpdates) {} + callback = function (error, jsonUpdates) {} } const key = Keys.uncompressedHistoryOps({ doc_id }) return rclient.lrange(key, 0, batchSize - 1, callback) @@ -31,10 +31,10 @@ module.exports = RedisManager = { expandDocUpdates(jsonUpdates, callback) { let rawUpdates if (callback == null) { - callback = function(error, rawUpdates) {} + callback = function (error, rawUpdates) {} } try { - rawUpdates = Array.from(jsonUpdates || []).map(update => + rawUpdates = Array.from(jsonUpdates || []).map((update) => JSON.parse(update) ) } catch (e) { @@ -45,14 +45,14 @@ module.exports = RedisManager = { deleteAppliedDocUpdates(project_id, doc_id, docUpdates, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } const multi = rclient.multi() // Delete all the updates which have been applied (exact match) for (const update of Array.from(docUpdates || [])) { multi.lrem(Keys.uncompressedHistoryOps({ doc_id }), 1, update) } - return multi.exec(function(error, results) { + return multi.exec(function (error, results) { if (error != null) { return callback(error) } @@ -61,7 +61,7 @@ module.exports = RedisManager = { return rclient.srem( Keys.docsWithHistoryOps({ project_id }), doc_id, - function(error) { + function (error) { if (error != null) { return callback(error) } @@ -73,7 +73,7 @@ module.exports = RedisManager = { getDocIdsWithHistoryOps(project_id, callback) { if (callback == null) { - callback = function(error, doc_ids) {} + callback = function (error, doc_ids) {} } return rclient.smembers(Keys.docsWithHistoryOps({ project_id }), callback) }, @@ -93,8 +93,8 @@ module.exports = RedisManager = { let cursor = 0 // redis iterator const keySet = {} // use hash to avoid duplicate results // scan over all keys looking for pattern - var doIteration = cb => - node.scan(cursor, 'MATCH', pattern, 'COUNT', 1000, function( + var doIteration = (cb) => + node.scan(cursor, 'MATCH', pattern, 'COUNT', 1000, function ( error, reply ) { @@ -132,11 +132,11 @@ module.exports = RedisManager = { getProjectIdsWithHistoryOps(callback) { if (callback == null) { - callback = function(error, project_ids) {} + callback = function (error, project_ids) {} } return RedisManager._getKeys( Keys.docsWithHistoryOps({ project_id: '*' }), - function(error, project_keys) { + function (error, project_keys) { if (error != null) { return callback(error) } @@ -150,11 +150,11 @@ module.exports = RedisManager = { // return all the docids, to find dangling history entries after // everything is flushed. if (callback == null) { - callback = function(error, doc_ids) {} + callback = function (error, doc_ids) {} } return RedisManager._getKeys( Keys.uncompressedHistoryOps({ doc_id: '*' }), - function(error, doc_keys) { + function (error, doc_keys) { if (error != null) { return callback(error) } diff --git a/services/track-changes/app/js/RedisManager.js.map b/services/track-changes/app/js/RedisManager.js.map new file mode 100644 index 0000000000..a0e01b35f6 --- /dev/null +++ b/services/track-changes/app/js/RedisManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "RedisManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/RedisManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,KAAA,GAAQ,OAAA,CAAQ,kBAAR;;EACR,OAAA,GAAU,KAAK,CAAC,YAAN,CAAmB,QAAQ,CAAC,KAAK,CAAC,OAAlC;;EACV,IAAA,GAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;;EAC9B,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EAER,MAAM,CAAC,OAAP,GAAiB,YAAA,GAEhB;IAAA,mBAAA,EAAqB,SAAC,MAAD,EAAS,SAAT,EAAoB,QAApB;AACpB,UAAA;;QADwC,WAAW,SAAC,KAAD,EAAQ,WAAR,GAAA;;MACnD,GAAA,GAAM,IAAI,CAAC,sBAAL,CAA4B;QAAC,QAAA,MAAD;OAA5B;aACN,OAAO,CAAC,MAAR,CAAe,GAAf,EAAoB,CAApB,EAAuB,SAAA,GAAY,CAAnC,EAAsC,QAAtC;IAFoB,CAArB;IAIA,gBAAA,EAAkB,SAAC,WAAD,EAAc,QAAd;AACjB,UAAA;;QAD+B,WAAW,SAAC,KAAD,EAAQ,UAAR,GAAA;;AAC1C;QACC,UAAA;;AAAe;AAAA;eAAA,qCAAA;;0BAAA,IAAI,CAAC,KAAL,CAAW,MAAX;AAAA;;aADhB;OAAA,cAAA;QAEM;AACL,eAAO,QAAA,CAAS,CAAT,EAHR;;aAIA,QAAA,CAAS,IAAT,EAAe,UAAf;IALiB,CAJlB;IAWA,uBAAA,EAAyB,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,QAAjC;AACxB,UAAA;;QADyD,WAAW,SAAC,KAAD,GAAA;;MACpE,KAAA,GAAQ,OAAO,CAAC,KAAR,CAAA;AAER;AAAA,WAAA,qCAAA;;QACC,KAAK,CAAC,IAAN,CAAW,IAAI,CAAC,sBAAL,CAA4B;UAAC,QAAA,MAAD;SAA5B,CAAX,EAAkD,CAAlD,EAAqD,MAArD;AADD;aAEA,KAAK,CAAC,IAAN,CAAW,SAAC,KAAD,EAAQ,OAAR;QACV,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eAGA,OAAO,CAAC,IAAR,CAAa,IAAI,CAAC,kBAAL,CAAwB;UAAC,YAAA,UAAD;SAAxB,CAAb,EAAoD,MAApD,EAA4D,SAAC,KAAD;UAC3D,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT;QAF2D,CAA5D;MAJU,CAAX;IALwB,CAXzB;IAwBA,uBAAA,EAAyB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAChD,OAAO,CAAC,QAAR,CAAiB,IAAI,CAAC,kBAAL,CAAwB;QAAC,YAAA,UAAD;OAAxB,CAAjB,EAAwD,QAAxD;IADwB,CAxBzB;IA6BA,QAAA,EAAU,SAAC,OAAD,EAAU,QAAV;AACT,UAAA;MAAA,KAAA,0CAAQ,OAAO,CAAC,MAAO,mBAAf,IAA4B,CAAE,OAAF;MACpC,kBAAA,GAAqB,SAAC,IAAD,EAAO,EAAP;eACpB,YAAY,CAAC,gBAAb,CAA8B,IAA9B,EAAoC,OAApC,EAA6C,EAA7C;MADoB;aAErB,KAAK,CAAC,YAAN,CAAmB,KAAnB,EAA0B,kBAA1B,EAA8C,QAA9C;IAJS,CA7BV;IAmCA,gBAAA,EAAkB,SAAC,IAAD,EAAO,OAAP,EAAgB,QAAhB;AACjB,UAAA;MAAA,MAAA,GAAS;MACT,MAAA,GAAS;MAET,WAAA,GAAc,SAAC,EAAD;eACb,IAAI,CAAC,IAAL,CAAU,MAAV,EAAkB,OAAlB,EAA2B,OAA3B,EAAoC,OAApC,EAA6C,IAA7C,EAAmD,SAAC,KAAD,EAAQ,KAAR;AAClD,cAAA;UAAA,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACC,iBAAD,EAAS;AACT,eAAA,sCAAA;;YACC,MAAO,CAAA,GAAA,CAAP,GAAc;AADf;UAEA,IAAG,MAAA,KAAU,GAAb;AACC,mBAAO,QAAA,CAAS,IAAT,EAAe,MAAM,CAAC,IAAP,CAAY,MAAZ,CAAf,EADR;WAAA,MAAA;mBAGC,WAAA,CAAA,EAHD;;QALkD,CAAnD;MADa;aAUd,WAAA,CAAA;IAdiB,CAnClB;IAqDA,WAAA,EAAa,SAAC,OAAD;AACZ,UAAA;MAAA,GAAA;;AAAM;aAAA,yCAAA;;UACL,CAAA,GAAI,GAAG,CAAC,KAAJ,CAAU,uBAAV;wBACJ,CAAE,CAAA,CAAA;AAFG;;;AAGN,aAAO;IAJK,CArDb;IA2DA,2BAAA,EAA6B,SAAC,QAAD;;QAAC,WAAW,SAAC,KAAD,EAAQ,WAAR,GAAA;;aACxC,YAAY,CAAC,QAAb,CAAsB,IAAI,CAAC,kBAAL,CAAwB;QAAC,UAAA,EAAW,GAAZ;OAAxB,CAAtB,EAAiE,SAAC,KAAD,EAAQ,YAAR;AAChE,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,WAAA,GAAc,YAAY,CAAC,WAAb,CAAyB,YAAzB;eACd,QAAA,CAAS,KAAT,EAAgB,WAAhB;MAHgE,CAAjE;IAD4B,CA3D7B;IAiEA,0BAAA,EAA4B,SAAC,QAAD;;QAAC,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAGvC,YAAY,CAAC,QAAb,CAAsB,IAAI,CAAC,sBAAL,CAA4B;QAAC,MAAA,EAAO,GAAR;OAA5B,CAAtB,EAAiE,SAAC,KAAD,EAAQ,QAAR;AAChE,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,OAAA,GAAU,YAAY,CAAC,WAAb,CAAyB,QAAzB;eACV,QAAA,CAAS,KAAT,EAAgB,OAAhB;MAHgE,CAAjE;IAH2B,CAjE5B;;AARD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/RestoreManager.js b/services/track-changes/app/js/RestoreManager.js index cc365f6709..6a1af1af44 100644 --- a/services/track-changes/app/js/RestoreManager.js +++ b/services/track-changes/app/js/RestoreManager.js @@ -19,14 +19,14 @@ const logger = require('logger-sharelatex') module.exports = RestoreManager = { restoreToBeforeVersion(project_id, doc_id, version, user_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } logger.log({ project_id, doc_id, version, user_id }, 'restoring document') return DiffManager.getDocumentBeforeVersion( project_id, doc_id, version, - function(error, content) { + function (error, content) { if (error != null) { return callback(error) } @@ -35,7 +35,7 @@ module.exports = RestoreManager = { doc_id, content, user_id, - function(error) { + function (error) { if (error != null) { return callback(error) } diff --git a/services/track-changes/app/js/RestoreManager.js.map b/services/track-changes/app/js/RestoreManager.js.map new file mode 100644 index 0000000000..b9ab8c044c --- /dev/null +++ b/services/track-changes/app/js/RestoreManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "RestoreManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/RestoreManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,sBAAA,GAAyB,OAAA,CAAQ,0BAAR;;EACzB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,cAAA,GAChB;IAAA,sBAAA,EAAwB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,OAA9B,EAAuC,QAAvC;;QAAuC,WAAW,SAAC,KAAD,GAAA;;MACzE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;QAAwC,OAAA,EAAS,OAAjD;QAA0D,OAAA,EAAS,OAAnE;OAAX,EAAuF,oBAAvF;aACA,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,OAAzD,EAAkE,SAAC,KAAD,EAAQ,OAAR;QACjE,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,sBAAsB,CAAC,WAAvB,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,OAAvD,EAAgE,OAAhE,EAAyE,SAAC,KAAD;UACxE,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAA;QAFwE,CAAzE;MAFiE,CAAlE;IAFuB,CAAxB;;AALD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/UpdateCompressor.js b/services/track-changes/app/js/UpdateCompressor.js index 570bc97a61..019ad32760 100644 --- a/services/track-changes/app/js/UpdateCompressor.js +++ b/services/track-changes/app/js/UpdateCompressor.js @@ -42,7 +42,7 @@ module.exports = UpdateCompressor = { const splitUpdates = [] for (const update of Array.from(updates)) { // Reject any non-insert or delete ops, i.e. comments - const ops = update.op.filter(o => o.i != null || o.d != null) + const ops = update.op.filter((o) => o.i != null || o.d != null) if (ops.length === 0) { splitUpdates.push({ op: UpdateCompressor.NOOP, @@ -97,7 +97,7 @@ module.exports = UpdateCompressor = { if ( __guard__( lastPreviousUpdate != null ? lastPreviousUpdate.op : undefined, - x => x.length + (x) => x.length ) > 1 ) { // if the last previous update was an array op, don't compress onto it. @@ -183,7 +183,8 @@ module.exports = UpdateCompressor = { if ( firstOp.i != null && secondOp.i != null && - firstOp.p <= secondOp.p && secondOp.p <= firstOp.p + firstOp.i.length && + firstOp.p <= secondOp.p && + secondOp.p <= firstOp.p + firstOp.i.length && firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE ) { return [ @@ -204,7 +205,8 @@ module.exports = UpdateCompressor = { } else if ( firstOp.d != null && secondOp.d != null && - secondOp.p <= firstOp.p && firstOp.p <= secondOp.p + secondOp.d.length && + secondOp.p <= firstOp.p && + firstOp.p <= secondOp.p + secondOp.d.length && firstSize + secondSize < UpdateCompressor.MAX_UPDATE_SIZE ) { return [ @@ -225,7 +227,8 @@ module.exports = UpdateCompressor = { } else if ( firstOp.i != null && secondOp.d != null && - firstOp.p <= secondOp.p && secondOp.p <= firstOp.p + firstOp.i.length + firstOp.p <= secondOp.p && + secondOp.p <= firstOp.p + firstOp.i.length ) { offset = secondOp.p - firstOp.p const insertedText = firstOp.i.slice(offset, offset + secondOp.d.length) @@ -276,7 +279,7 @@ module.exports = UpdateCompressor = { } ] } else { - return diff_ops.map(function(op) { + return diff_ops.map(function (op) { op.p += offset return { meta: { @@ -299,7 +302,7 @@ module.exports = UpdateCompressor = { UNCHANGED: 0, diffAsShareJsOps(before, after, callback) { if (callback == null) { - callback = function(error, ops) {} + callback = function (error, ops) {} } const diffs = dmp.diff_main(before, after) dmp.diff_cleanupSemantic(diffs) diff --git a/services/track-changes/app/js/UpdateCompressor.js.map b/services/track-changes/app/js/UpdateCompressor.js.map new file mode 100644 index 0000000000..6ff4402373 --- /dev/null +++ b/services/track-changes/app/js/UpdateCompressor.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "UpdateCompressor.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/UpdateCompressor.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,SAAA,GAAY,SAAC,EAAD,EAAK,GAAL,EAAU,EAAV;WAAiB,EAAG,cAAH,GAAa,EAAb,GAAkB,EAAG;EAAtC;;EACZ,SAAA,GAAY,SAAC,EAAD,EAAK,GAAL,EAAU,MAAV;WAAqB,EAAG,cAAH,GAAa,EAAG;EAArC;;EAEZ,gBAAA,GAAmB,OAAA,CAAQ,yBAAR,CAAkC,CAAC;;EACtD,GAAA,GAAM,IAAI,gBAAJ,CAAA;;EAEN,MAAM,CAAC,OAAP,GAAiB,gBAAA,GAChB;IAAA,IAAA,EAAM,MAAN;IAgBA,wBAAA,EAA0B,SAAC,OAAD;AACzB,UAAA;MAAA,YAAA,GAAe;AACf,WAAA,yCAAA;;QAEC,GAAA,GAAM,MAAM,CAAC,EAAE,CAAC,MAAV,CAAiB,SAAC,CAAD;iBAAO,aAAA,IAAQ;QAAf,CAAjB;QACN,IAAG,GAAG,CAAC,MAAJ,KAAc,CAAjB;UACC,YAAY,CAAC,IAAb,CACC;YAAA,EAAA,EAAI,gBAAgB,CAAC,IAArB;YACA,IAAA,EACC;cAAA,QAAA,EAAU,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAA9C;cACA,MAAA,EAAU,MAAM,CAAC,IAAI,CAAC,MAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAD9C;cAEA,OAAA,EAAU,MAAM,CAAC,IAAI,CAAC,OAFtB;aAFD;YAKA,CAAA,EAAG,MAAM,CAAC,CALV;WADD,EADD;SAAA,MAAA;AASC,eAAA,uCAAA;;YACC,YAAY,CAAC,IAAb,CACC;cAAA,EAAA,EAAI,EAAJ;cACA,IAAA,EACC;gBAAA,QAAA,EAAU,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAA9C;gBACA,MAAA,EAAU,MAAM,CAAC,IAAI,CAAC,MAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAD9C;gBAEA,OAAA,EAAU,MAAM,CAAC,IAAI,CAAC,OAFtB;eAFD;cAKA,CAAA,EAAG,MAAM,CAAC,CALV;aADD;AADD,WATD;;AAHD;AAoBA,aAAO;IAtBkB,CAhB1B;IAwCA,4BAAA,EAA8B,SAAC,OAAD;AAC7B,UAAA;MAAA,gBAAA,GAAmB;AACnB,WAAA,yCAAA;;QACC,UAAA,GAAa,gBAAiB,CAAA,gBAAgB,CAAC,MAAjB,GAA0B,CAA1B;QAC9B,IAAG,oBAAA,IAAgB,UAAU,CAAC,CAAX,KAAgB,MAAM,CAAC,CAA1C;UACC,IAAoC,MAAM,CAAC,EAAP,KAAa,gBAAgB,CAAC,IAAlE;YAAA,UAAU,CAAC,EAAE,CAAC,IAAd,CAAmB,MAAM,CAAC,EAA1B,EAAA;WADD;SAAA,MAAA;UAGC,UAAA,GACC;YAAA,EAAA,EAAM,EAAN;YACA,IAAA,EAAM,MAAM,CAAC,IADb;YAEA,CAAA,EAAM,MAAM,CAAC,CAFb;;UAGD,IAAoC,MAAM,CAAC,EAAP,KAAa,gBAAgB,CAAC,IAAlE;YAAA,UAAU,CAAC,EAAE,CAAC,IAAd,CAAmB,MAAM,CAAC,EAA1B,EAAA;;UACA,gBAAgB,CAAC,IAAjB,CAAsB,UAAtB,EARD;;AAFD;AAWA,aAAO;IAbsB,CAxC9B;IAuDA,kBAAA,EAAoB,SAAC,kBAAD,EAAqB,UAArB;AACnB,UAAA;MAAA,6EAAyB,CAAE,yBAAxB,GAAiC,CAApC;AAGC,eAAO,CAAC,kBAAD,CAAoB,CAAC,MAArB,CAA4B,gBAAgB,CAAC,kBAAjB,CAAoC,IAApC,EAAyC,UAAzC,CAA5B,EAHR;;MAIA,IAAG,0BAAH;QACC,UAAA,GAAa,CAAC,kBAAD,CAAoB,CAAC,MAArB,CAA4B,UAA5B,EADd;;MAEA,OAAA,GAAU,gBAAgB,CAAC,wBAAjB,CAA0C,UAA1C;MACV,OAAA,GAAU,gBAAgB,CAAC,eAAjB,CAAiC,OAAjC;AACV,aAAO,gBAAgB,CAAC,4BAAjB,CAA8C,OAA9C;IATY,CAvDpB;IAkEA,eAAA,EAAiB,SAAC,OAAD;AAChB,UAAA;MAAA,IAAa,OAAO,CAAC,MAAR,KAAkB,CAA/B;AAAA,eAAO,GAAP;;MAEA,iBAAA,GAAoB,CAAC,OAAO,CAAC,KAAR,CAAA,CAAD;AACpB,WAAA,yCAAA;;QACC,oBAAA,GAAuB,iBAAiB,CAAC,GAAlB,CAAA;QACvB,IAAG,4BAAH;UACC,iBAAA,GAAoB,iBAAiB,CAAC,MAAlB,CAAyB,gBAAgB,CAAC,iBAAjB,CAAmC,oBAAnC,EAAyD,MAAzD,CAAzB,EADrB;SAAA,MAAA;UAGC,iBAAiB,CAAC,IAAlB,CAAuB,MAAvB,EAHD;;AAFD;AAOA,aAAO;IAXS,CAlEjB;IA+EA,wBAAA,EAA0B,SAAA,GAAY,EAAA,GAAK,IA/E3C;IAgFA,eAAA,EAAiB,YAAA,GAAe,CAAA,GAAG,IAAH,GAAU,IAhF1C;IAkFA,iBAAA,EAAmB,SAAC,WAAD,EAAc,YAAd;AAClB,UAAA;MAAA,WAAA,GACC;QAAA,EAAA,EAAI,WAAW,CAAC,EAAhB;QACA,IAAA,EACC;UAAA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAAjB,IAA4B,IAAtC;UACA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAAjB,IAA6B,WAAW,CAAC,IAAI,CAAC,EADxD;UAEA,MAAA,EAAU,WAAW,CAAC,IAAI,CAAC,MAAjB,IAA6B,WAAW,CAAC,IAAI,CAAC,EAFxD;SAFD;QAKA,CAAA,EAAG,WAAW,CAAC,CALf;;MAMD,YAAA,GACC;QAAA,EAAA,EAAI,YAAY,CAAC,EAAjB;QACA,IAAA,EACC;UAAA,OAAA,EAAU,YAAY,CAAC,IAAI,CAAC,OAAlB,IAA6B,IAAvC;UACA,QAAA,EAAU,YAAY,CAAC,IAAI,CAAC,QAAlB,IAA8B,YAAY,CAAC,IAAI,CAAC,EAD1D;UAEA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAAlB,IAA8B,YAAY,CAAC,IAAI,CAAC,EAF1D;SAFD;QAKA,CAAA,EAAG,YAAY,CAAC,CALhB;;MAOD,IAAG,WAAW,CAAC,IAAI,CAAC,OAAjB,KAA4B,YAAY,CAAC,IAAI,CAAC,OAAjD;AACC,eAAO,CAAC,WAAD,EAAc,YAAd,EADR;;MAGA,IAAG,YAAY,CAAC,IAAI,CAAC,QAAlB,GAA6B,WAAW,CAAC,IAAI,CAAC,MAA9C,GAAuD,gBAAgB,CAAC,wBAA3E;AACC,eAAO,CAAC,WAAD,EAAc,YAAd,EADR;;MAGA,OAAA,GAAU,WAAW,CAAC;MACtB,QAAA,GAAW,YAAY,CAAC;MAExB,SAAA,mCAAqB,CAAE,gBAAX,sCAA8B,CAAE;MAC5C,UAAA,sCAAuB,CAAE,gBAAZ,uCAAgC,CAAE;MAG/C,IAAG,mBAAA,IAAe,oBAAf,IAA+B,CAAA,OAAO,CAAC,CAAR,YAAa,QAAQ,CAAC,EAAtB,QAAA,IAA2B,CAAC,OAAO,CAAC,CAAR,GAAY,OAAO,CAAC,CAAC,CAAC,MAAvB,CAA3B,CAA/B,IAA6F,SAAA,GAAY,UAAZ,GAAyB,gBAAgB,CAAC,eAA1I;AACC,eAAO;UACN;YAAA,IAAA,EACC;cAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;cACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;cAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;aADD;YAIA,EAAA,EACC;cAAA,CAAA,EAAG,OAAO,CAAC,CAAX;cACA,CAAA,EAAG,SAAA,CAAU,OAAO,CAAC,CAAlB,EAAqB,QAAQ,CAAC,CAAT,GAAa,OAAO,CAAC,CAA1C,EAA6C,QAAQ,CAAC,CAAtD,CADH;aALD;YAOA,CAAA,EAAG,YAAY,CAAC,CAPhB;WADM;UADR;OAAA,MAYK,IAAG,mBAAA,IAAe,oBAAf,IAA+B,CAAA,QAAQ,CAAC,CAAT,YAAc,OAAO,CAAC,EAAtB,QAAA,IAA2B,CAAC,QAAQ,CAAC,CAAT,GAAa,QAAQ,CAAC,CAAC,CAAC,MAAzB,CAA3B,CAA/B,IAA+F,SAAA,GAAY,UAAZ,GAAyB,gBAAgB,CAAC,eAA5I;AACJ,eAAO;UACN;YAAA,IAAA,EACC;cAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;cACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;cAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;aADD;YAIA,EAAA,EACC;cAAA,CAAA,EAAG,QAAQ,CAAC,CAAZ;cACA,CAAA,EAAG,SAAA,CAAU,QAAQ,CAAC,CAAnB,EAAsB,OAAO,CAAC,CAAR,GAAY,QAAQ,CAAC,CAA3C,EAA8C,OAAO,CAAC,CAAtD,CADH;aALD;YAOA,CAAA,EAAG,YAAY,CAAC,CAPhB;WADM;UADH;OAAA,MAYA,IAAG,mBAAA,IAAe,oBAAf,IAA+B,CAAA,OAAO,CAAC,CAAR,YAAa,QAAQ,CAAC,EAAtB,QAAA,IAA2B,CAAC,OAAO,CAAC,CAAR,GAAY,OAAO,CAAC,CAAC,CAAC,MAAvB,CAA3B,CAAlC;QACJ,MAAA,GAAS,QAAQ,CAAC,CAAT,GAAa,OAAO,CAAC;QAC9B,YAAA,GAAe,OAAO,CAAC,CAAC,CAAC,KAAV,CAAgB,MAAhB,EAAwB,MAAA,GAAS,QAAQ,CAAC,CAAC,CAAC,MAA5C;QAEf,IAAG,YAAA,KAAgB,QAAQ,CAAC,CAA5B;UACC,MAAA,GAAS,SAAA,CAAU,OAAO,CAAC,CAAlB,EAAqB,MAArB,EAA6B,QAAQ,CAAC,CAAC,CAAC,MAAxC;AACT,iBAAO;YACN;cAAA,IAAA,EACC;gBAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;gBACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;gBAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;eADD;cAIA,EAAA,EACC;gBAAA,CAAA,EAAG,OAAO,CAAC,CAAX;gBACA,CAAA,EAAG,MADH;eALD;cAOA,CAAA,EAAG,YAAY,CAAC,CAPhB;aADM;YAFR;SAAA,MAAA;AAcC,iBAAO,CAAC,WAAD,EAAc,YAAd,EAdR;SAJI;OAAA,MAqBA,IAAG,mBAAA,IAAe,oBAAf,IAA+B,OAAO,CAAC,CAAR,KAAa,QAAQ,CAAC,CAAxD;QACJ,MAAA,GAAS,OAAO,CAAC;QACjB,QAAA,GAAW,IAAC,CAAA,gBAAD,CAAkB,OAAO,CAAC,CAA1B,EAA6B,QAAQ,CAAC,CAAtC;QACX,IAAG,QAAQ,CAAC,MAAT,KAAmB,CAAtB;AACC,iBAAO;YAAC;cACP,IAAA,EACC;gBAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;gBACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;gBAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;eAFM;cAKP,EAAA,EACC;gBAAA,CAAA,EAAG,OAAO,CAAC,CAAX;gBACA,CAAA,EAAG,EADH;eANM;cAQP,CAAA,EAAG,YAAY,CAAC,CART;aAAD;YADR;SAAA,MAAA;AAYC,iBAAO,QAAQ,CAAC,GAAT,CAAa,SAAC,EAAD;YACnB,EAAE,CAAC,CAAH,IAAQ;AACR,mBAAO;cACN,IAAA,EACC;gBAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;gBACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;gBAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;eAFK;cAKN,EAAA,EAAI,EALE;cAMN,CAAA,EAAG,YAAY,CAAC,CANV;;UAFY,CAAb,EAZR;SAHI;OAAA,MAAA;AA2BJ,eAAO,CAAC,WAAD,EAAc,YAAd,EA3BH;;IA1Ea,CAlFnB;IAyLA,KAAA,EAAO,CAzLP;IA0LA,OAAA,EAAS,CAAC,CA1LV;IA2LA,SAAA,EAAW,CA3LX;IA4LA,gBAAA,EAAkB,SAAC,MAAD,EAAS,KAAT,EAAgB,QAAhB;AACjB,UAAA;;QADiC,WAAW,SAAC,KAAD,EAAQ,GAAR,GAAA;;MAC5C,KAAA,GAAQ,GAAG,CAAC,SAAJ,CAAc,MAAd,EAAsB,KAAtB;MACR,GAAG,CAAC,oBAAJ,CAAyB,KAAzB;MAEA,GAAA,GAAM;MACN,QAAA,GAAW;AACX,WAAA,uCAAA;;QACC,IAAA,GAAO,IAAK,CAAA,CAAA;QACZ,OAAA,GAAU,IAAK,CAAA,CAAA;QACf,IAAG,IAAA,KAAQ,IAAC,CAAA,KAAZ;UACC,GAAG,CAAC,IAAJ,CACC;YAAA,CAAA,EAAG,OAAH;YACA,CAAA,EAAG,QADH;WADD;UAGA,QAAA,IAAY,OAAO,CAAC,OAJrB;SAAA,MAKK,IAAG,IAAA,KAAQ,IAAC,CAAA,OAAZ;UACJ,GAAG,CAAC,IAAJ,CACC;YAAA,CAAA,EAAG,OAAH;YACA,CAAA,EAAG,QADH;WADD,EADI;SAAA,MAIA,IAAG,IAAA,KAAQ,IAAC,CAAA,SAAZ;UACJ,QAAA,IAAY,OAAO,CAAC,OADhB;SAAA,MAAA;AAGJ,gBAAM,eAHF;;AAZN;AAgBA,aAAO;IAtBU,CA5LlB;;AAPD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/UpdateTrimmer.js b/services/track-changes/app/js/UpdateTrimmer.js index 8a37e5484f..5fabe1cb9e 100644 --- a/services/track-changes/app/js/UpdateTrimmer.js +++ b/services/track-changes/app/js/UpdateTrimmer.js @@ -20,9 +20,9 @@ const logger = require('logger-sharelatex') module.exports = UpdateTrimmer = { shouldTrimUpdates(project_id, callback) { if (callback == null) { - callback = function(error, shouldTrim) {} + callback = function (error, shouldTrim) {} } - return MongoManager.getProjectMetaData(project_id, function( + return MongoManager.getProjectMetaData(project_id, function ( error, metadata ) { @@ -32,7 +32,7 @@ module.exports = UpdateTrimmer = { if (metadata != null ? metadata.preserveHistory : undefined) { return callback(null, false) } else { - return WebApiManager.getProjectDetails(project_id, function( + return WebApiManager.getProjectDetails(project_id, function ( error, details ) { @@ -43,17 +43,19 @@ module.exports = UpdateTrimmer = { if ( __guard__( details != null ? details.features : undefined, - x => x.versioning + (x) => x.versioning ) ) { return MongoManager.setProjectMetaData( project_id, { preserveHistory: true }, - function(error) { + function (error) { if (error != null) { return callback(error) } - return MongoManager.upgradeHistory(project_id, function(error) { + return MongoManager.upgradeHistory(project_id, function ( + error + ) { if (error != null) { return callback(error) } diff --git a/services/track-changes/app/js/UpdateTrimmer.js.map b/services/track-changes/app/js/UpdateTrimmer.js.map new file mode 100644 index 0000000000..72f24be153 --- /dev/null +++ b/services/track-changes/app/js/UpdateTrimmer.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "UpdateTrimmer.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/UpdateTrimmer.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,YAAA,GAAe,OAAA,CAAQ,gBAAR;;EACf,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,aAAA,GAChB;IAAA,iBAAA,EAAmB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,UAAR,GAAA;;aAC1C,YAAY,CAAC,kBAAb,CAAgC,UAAhC,EAA4C,SAAC,KAAD,EAAQ,QAAR;QAC3C,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,uBAAG,QAAQ,CAAE,wBAAb;AACC,iBAAO,QAAA,CAAS,IAAT,EAAe,KAAf,EADR;SAAA,MAAA;iBAGC,aAAa,CAAC,iBAAd,CAAgC,UAAhC,EAA4C,SAAC,KAAD,EAAQ,OAAR;AAC3C,gBAAA;YAAA,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,MAAM,CAAC,GAAP,CAAW;cAAA,UAAA,EAAY,UAAZ;cAAwB,OAAA,EAAS,OAAjC;aAAX,EAAqD,aAArD;YACA,4DAAoB,CAAE,4BAAtB;qBACC,YAAY,CAAC,kBAAb,CAAgC,UAAhC,EAA4C;gBAAA,eAAA,EAAiB,IAAjB;eAA5C,EAAmE,SAAC,KAAD;gBAClE,IAA0B,aAA1B;AAAA,yBAAO,QAAA,CAAS,KAAT,EAAP;;uBACA,YAAY,CAAC,cAAb,CAA4B,UAA5B,EAAwC,SAAC,KAAD;kBACvC,IAA0B,aAA1B;AAAA,2BAAO,QAAA,CAAS,KAAT,EAAP;;yBACA,QAAA,CAAS,IAAT,EAAe,KAAf;gBAFuC,CAAxC;cAFkE,CAAnE,EADD;aAAA,MAAA;qBAOC,QAAA,CAAS,IAAT,EAAe,IAAf,EAPD;;UAH2C,CAA5C,EAHD;;MAF2C,CAA5C;IADkB,CAAnB;;AALD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js index 89da68a631..811e9ba1ca 100644 --- a/services/track-changes/app/js/UpdatesManager.js +++ b/services/track-changes/app/js/UpdatesManager.js @@ -38,7 +38,7 @@ module.exports = UpdatesManager = { ) { let i if (callback == null) { - callback = function(error) {} + callback = function (error) {} } const { length } = rawUpdates if (length === 0) { @@ -50,7 +50,7 @@ module.exports = UpdatesManager = { const op = rawUpdates[i] if (i > 0) { const thisVersion = op != null ? op.v : undefined - const prevVersion = __guard__(rawUpdates[i - 1], x => x.v) + const prevVersion = __guard__(rawUpdates[i - 1], (x) => x.v) if (!(prevVersion < thisVersion)) { logger.error( { @@ -69,7 +69,7 @@ module.exports = UpdatesManager = { // FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it // CORRECTION: we do use it to log the time in case of error - return MongoManager.peekLastCompressedUpdate(doc_id, function( + return MongoManager.peekLastCompressedUpdate(doc_id, function ( error, lastCompressedUpdate, lastVersion @@ -105,7 +105,7 @@ module.exports = UpdatesManager = { lastCompressedUpdate != null ? lastCompressedUpdate.meta : undefined, - x1 => x1.end_ts + (x1) => x1.end_ts ) const last_timestamp = ts != null ? new Date(ts) : 'unknown time' error = new Error( @@ -179,7 +179,7 @@ module.exports = UpdatesManager = { lastCompressedUpdate, compressedUpdates, temporary, - function(error, result) { + function (error, result) { if (error != null) { return callback(error) } @@ -206,9 +206,9 @@ module.exports = UpdatesManager = { // Check whether the updates are temporary (per-project property) _prepareProjectForUpdates(project_id, callback) { if (callback == null) { - callback = function(error, temporary) {} + callback = function (error, temporary) {} } - return UpdateTrimmer.shouldTrimUpdates(project_id, function( + return UpdateTrimmer.shouldTrimUpdates(project_id, function ( error, temporary ) { @@ -222,9 +222,9 @@ module.exports = UpdatesManager = { // Check for project id on document history (per-document property) _prepareDocForUpdates(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } - return MongoManager.backportProjectId(project_id, doc_id, function(error) { + return MongoManager.backportProjectId(project_id, doc_id, function (error) { if (error != null) { return callback(error) } @@ -237,18 +237,18 @@ module.exports = UpdatesManager = { processUncompressedUpdates(project_id, doc_id, temporary, callback) { // get the updates as strings from redis (so we can delete them after they are applied) if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return RedisManager.getOldestDocUpdates( doc_id, UpdatesManager.REDIS_READ_BATCH_SIZE, - function(error, docUpdates) { + function (error, docUpdates) { if (error != null) { return callback(error) } const { length } = docUpdates // parse the redis strings into ShareJs updates - return RedisManager.expandDocUpdates(docUpdates, function( + return RedisManager.expandDocUpdates(docUpdates, function ( error, rawUpdates ) { @@ -268,7 +268,7 @@ module.exports = UpdatesManager = { doc_id, rawUpdates, temporary, - function(error) { + function (error) { if (error != null) { return callback(error) } @@ -281,7 +281,7 @@ module.exports = UpdatesManager = { project_id, doc_id, docUpdates, - function(error) { + function (error) { if (error != null) { return callback(error) } @@ -320,9 +320,9 @@ module.exports = UpdatesManager = { // Process updates for a doc when we flush it individually processUncompressedUpdatesWithLock(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } - return UpdatesManager._prepareProjectForUpdates(project_id, function( + return UpdatesManager._prepareProjectForUpdates(project_id, function ( error, temporary ) { @@ -346,9 +346,9 @@ module.exports = UpdatesManager = { callback ) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } - return UpdatesManager._prepareDocForUpdates(project_id, doc_id, function( + return UpdatesManager._prepareDocForUpdates(project_id, doc_id, function ( error ) { if (error != null) { @@ -356,7 +356,7 @@ module.exports = UpdatesManager = { } return LockManager.runWithLock( keys.historyLock({ doc_id }), - releaseLock => + (releaseLock) => UpdatesManager.processUncompressedUpdates( project_id, doc_id, @@ -371,23 +371,23 @@ module.exports = UpdatesManager = { // Process all updates for a project, only check project-level information once processUncompressedUpdatesForProject(project_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } - return RedisManager.getDocIdsWithHistoryOps(project_id, function( + return RedisManager.getDocIdsWithHistoryOps(project_id, function ( error, doc_ids ) { if (error != null) { return callback(error) } - return UpdatesManager._prepareProjectForUpdates(project_id, function( + return UpdatesManager._prepareProjectForUpdates(project_id, function ( error, temporary ) { const jobs = [] for (const doc_id of Array.from(doc_ids)) { - ;(doc_id => - jobs.push(cb => + ;((doc_id) => + jobs.push((cb) => UpdatesManager._processUncompressedUpdatesForDocWithLock( project_id, doc_id, @@ -404,9 +404,9 @@ module.exports = UpdatesManager = { // flush all outstanding changes flushAll(limit, callback) { if (callback == null) { - callback = function(error, result) {} + callback = function (error, result) {} } - return RedisManager.getProjectIdsWithHistoryOps(function( + return RedisManager.getProjectIdsWithHistoryOps(function ( error, project_ids ) { @@ -426,15 +426,15 @@ module.exports = UpdatesManager = { const selectedProjects = limit < 0 ? project_ids : project_ids.slice(0, limit) for (project_id of Array.from(selectedProjects)) { - ;(project_id => - jobs.push(cb => + ;((project_id) => + jobs.push((cb) => UpdatesManager.processUncompressedUpdatesForProject( project_id, - err => cb(null, { failed: err != null, project_id }) + (err) => cb(null, { failed: err != null, project_id }) ) ))(project_id) } - return async.series(jobs, function(error, result) { + return async.series(jobs, function (error, result) { let x if (error != null) { return callback(error) @@ -468,16 +468,16 @@ module.exports = UpdatesManager = { getDanglingUpdates(callback) { if (callback == null) { - callback = function(error, doc_ids) {} + callback = function (error, doc_ids) {} } - return RedisManager.getAllDocIdsWithHistoryOps(function( + return RedisManager.getAllDocIdsWithHistoryOps(function ( error, all_doc_ids ) { if (error != null) { return callback(error) } - return RedisManager.getProjectIdsWithHistoryOps(function( + return RedisManager.getProjectIdsWithHistoryOps(function ( error, all_project_ids ) { @@ -485,14 +485,14 @@ module.exports = UpdatesManager = { return callback(error) } // function to get doc_ids for each project - const task = cb => + const task = (cb) => async.concatSeries( all_project_ids, RedisManager.getDocIdsWithHistoryOps, cb ) // find the dangling doc ids - return task(function(error, project_doc_ids) { + return task(function (error, project_doc_ids) { const dangling_doc_ids = _.difference(all_doc_ids, project_doc_ids) logger.log( { all_doc_ids, all_project_ids, project_doc_ids, dangling_doc_ids }, @@ -509,12 +509,12 @@ module.exports = UpdatesManager = { options = {} } if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } return UpdatesManager.processUncompressedUpdatesWithLock( project_id, doc_id, - function(error) { + function (error) { if (error != null) { return callback(error) } @@ -524,7 +524,7 @@ module.exports = UpdatesManager = { doc_id, options.from, options.to, - function(error, updates) { + function (error, updates) { if (error != null) { return callback(error) } @@ -540,16 +540,16 @@ module.exports = UpdatesManager = { options = {} } if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } - return UpdatesManager.getDocUpdates(project_id, doc_id, options, function( + return UpdatesManager.getDocUpdates(project_id, doc_id, options, function ( error, updates ) { if (error != null) { return callback(error) } - return UpdatesManager.fillUserInfo(updates, function(error, updates) { + return UpdatesManager.fillUserInfo(updates, function (error, updates) { if (error != null) { return callback(error) } @@ -563,7 +563,7 @@ module.exports = UpdatesManager = { options = {} } if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } if (!options.min_count) { options.min_count = 25 @@ -573,11 +573,11 @@ module.exports = UpdatesManager = { let nextBeforeTimestamp = null return UpdatesManager.processUncompressedUpdatesForProject( project_id, - function(error) { + function (error) { if (error != null) { return callback(error) } - return PackManager.makeProjectIterator(project_id, before, function( + return PackManager.makeProjectIterator(project_id, before, function ( err, iterator ) { @@ -590,8 +590,8 @@ module.exports = UpdatesManager = { // console.log "checking iterator.done", iterator.done() summarizedUpdates.length < options.min_count && !iterator.done(), - cb => - iterator.next(function(err, partialUpdates) { + (cb) => + iterator.next(function (err, partialUpdates) { if (err != null) { return callback(err) } @@ -612,19 +612,19 @@ module.exports = UpdatesManager = { () => // finally done all updates // console.log 'summarized Updates', summarizedUpdates - UpdatesManager.fillSummarizedUserInfo(summarizedUpdates, function( - err, - results - ) { - if (err != null) { - return callback(err) + UpdatesManager.fillSummarizedUserInfo( + summarizedUpdates, + function (err, results) { + if (err != null) { + return callback(err) + } + return callback( + null, + results, + !iterator.done() ? nextBeforeTimestamp : undefined + ) } - return callback( - null, - results, - !iterator.done() ? nextBeforeTimestamp : undefined - ) - }) + ) ) }) } @@ -633,14 +633,14 @@ module.exports = UpdatesManager = { fetchUserInfo(users, callback) { if (callback == null) { - callback = function(error, fetchedUserInfo) {} + callback = function (error, fetchedUserInfo) {} } const jobs = [] const fetchedUserInfo = {} for (const user_id in users) { - ;(user_id => - jobs.push(callback => - WebApiManager.getUserInfo(user_id, function(error, userInfo) { + ;((user_id) => + jobs.push((callback) => + WebApiManager.getUserInfo(user_id, function (error, userInfo) { if (error != null) { return callback(error) } @@ -650,7 +650,7 @@ module.exports = UpdatesManager = { ))(user_id) } - return async.series(jobs, function(err) { + return async.series(jobs, function (err) { if (err != null) { return callback(err) } @@ -661,7 +661,7 @@ module.exports = UpdatesManager = { fillUserInfo(updates, callback) { let update, user_id if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } const users = {} for (update of Array.from(updates)) { @@ -671,7 +671,7 @@ module.exports = UpdatesManager = { } } - return UpdatesManager.fetchUserInfo(users, function( + return UpdatesManager.fetchUserInfo(users, function ( error, fetchedUserInfo ) { @@ -692,7 +692,7 @@ module.exports = UpdatesManager = { fillSummarizedUserInfo(updates, callback) { let update, user_id, user_ids if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } const users = {} for (update of Array.from(updates)) { @@ -704,7 +704,7 @@ module.exports = UpdatesManager = { } } - return UpdatesManager.fetchUserInfo(users, function( + return UpdatesManager.fetchUserInfo(users, function ( error, fetchedUserInfo ) { diff --git a/services/track-changes/app/js/UpdatesManager.js.map b/services/track-changes/app/js/UpdatesManager.js.map new file mode 100644 index 0000000000..32c9cdd909 --- /dev/null +++ b/services/track-changes/app/js/UpdatesManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "UpdatesManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/UpdatesManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,YAAA,GAAe,OAAA,CAAQ,gBAAR;;EACf,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,YAAA,GAAe,OAAA,CAAQ,gBAAR;;EACf,gBAAA,GAAmB,OAAA,CAAQ,oBAAR;;EACnB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,IAAA,GAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;;EAE3B,MAAM,CAAC,OAAP,GAAiB,cAAA,GAChB;IAAA,yBAAA,EAA2B,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,SAAjC,EAA4C,QAA5C;AAC1B,UAAA;;QADsE,WAAW,SAAC,KAAD,GAAA;;MACjF,MAAA,GAAS,UAAU,CAAC;MACpB,IAAG,MAAA,KAAU,CAAb;AACC,eAAO,QAAA,CAAA,EADR;;AAIA,WAAA,oDAAA;;cAA6B,CAAA,GAAI;;;QAChC,WAAA,gBAAc,EAAE,CAAE;QAClB,WAAA,0CAA6B,CAAE;QAC/B,IAAG,CAAI,CAAC,WAAA,GAAc,WAAf,CAAP;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,UAAA,EAAY,UAAZ;YAAwB,MAAA,EAAQ,MAAhC;YAAwC,UAAA,EAAW,UAAnD;YAA+D,SAAA,EAAW,SAA1E;YAAqF,WAAA,EAAY,WAAjG;YAA8G,WAAA,EAAY,WAA1H;WAAb,EAAoJ,0BAApJ,EADD;;AAHD;aAQA,YAAY,CAAC,wBAAb,CAAsC,MAAtC,EAA8C,SAAC,KAAD,EAAQ,oBAAR,EAA8B,WAA9B;AAQ7C,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QAGA,IAAG,mBAAH;UACC,gBAAA,GAAmB;UACnB,UAAA,GAAa,UAAU,CAAC,KAAX,CAAiB,CAAjB;AACb,iBAAM,uBAAA,IAAmB,UAAW,CAAA,CAAA,CAAE,CAAC,CAAd,IAAmB,WAA5C;YACC,gBAAgB,CAAC,IAAjB,CAAsB,UAAU,CAAC,KAAX,CAAA,CAAtB;UADD;UAEA,IAAG,gBAAgB,CAAC,MAApB;YACC,MAAM,CAAC,KAAP,CAAa;cAAA,UAAA,EAAY,UAAZ;cAAwB,MAAA,EAAQ,MAAhC;cAAwC,gBAAA,EAAkB,gBAA1D;cAA4E,SAAA,EAAW,SAAvF;cAAkG,WAAA,EAAa,WAA/G;aAAb,EAAyI,mCAAzI,EADD;;UAGA,IAAG,uBAAA,IAAmB,UAAW,CAAA,CAAA,CAAE,CAAC,CAAd,KAAmB,WAAA,GAAc,CAAvD;YACC,EAAA,mFAA+B,CAAE;YACjC,cAAA,GAAoB,UAAH,GAAY,IAAI,IAAJ,CAAS,EAAT,CAAZ,GAA8B;YAC/C,KAAA,GAAQ,IAAI,KAAJ,CAAU,mCAAA,GAAoC,UAAW,CAAA,CAAA,CAAE,CAAC,CAAlD,GAAoD,0CAApD,GAA8F,WAA9F,GAA0G,QAA1G,GAAkH,cAA5H;YACR,MAAM,CAAC,KAAP,CAAa;cAAA,GAAA,EAAK,KAAL;cAAY,MAAA,EAAQ,MAApB;cAA4B,UAAA,EAAY,UAAxC;cAAoD,WAAA,EAAa,EAAjE;cAAqE,SAAA,EAAW,SAAhF;cAA2F,oBAAA,EAAsB,oBAAjH;aAAb,EAAoJ,2BAApJ;YACA,kDAAwB,CAAE,yBAAvB,IAA2C,UAAW,CAAA,CAAA,CAAE,CAAC,CAAd,GAAkB,WAAA,GAAc,CAA9E;cAEC,oBAAA,GAAuB,KAFxB;aAAA,MAAA;AAIC,qBAAO,QAAA,CAAS,KAAT,EAJR;aALD;WARD;;QAmBA,IAAG,UAAU,CAAC,MAAX,KAAqB,CAAxB;AACC,iBAAO,QAAA,CAAA,EADR;;QAKA,oBAAA,GAAuB,CAAA,GAAI,IAAJ,GAAW;AAClC,aAAA,8CAAA;;UACC,OAAA;;AAAW;AAAA;iBAAA,wCAAA;;yDAAK,CAAE,gBAAN,iCAAoB,CAAE;AAAvB;;;UACX,IAAA,GAAO,CAAC,CAAC,GAAF,CAAM,OAAN;UACP,IAAG,IAAA,GAAO,oBAAV;YACC,KAAA,GAAQ,IAAI,KAAJ,CAAU,+CAAA,GAAgD,oBAA1D;YACR,MAAM,CAAC,KAAP,CAAa;cAAA,GAAA,EAAK,KAAL;cAAY,MAAA,EAAQ,MAApB;cAA4B,UAAA,EAAY,UAAxC;cAAoD,IAAA,EAAM,IAA1D;cAAgE,SAAA,EAAW,SAA3E;aAAb,EAAmG,sBAAnG;YACA,SAAS,CAAC,EAAV,GAAe,GAHhB;;AAHD;QAQA,iBAAA,GAAoB,gBAAgB,CAAC,kBAAjB,CAAoC,IAApC,EAA0C,UAA1C;eACpB,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,oBAAxD,EAA8E,iBAA9E,EAAiG,SAAjG,EAA4G,SAAC,KAAD,EAAQ,MAAR;UAC3G,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACA,IAAmH,cAAnH;YAAA,MAAM,CAAC,GAAP,CAAW;cAAC,YAAA,UAAD;cAAa,QAAA,MAAb;cAAqB,MAAA,iCAAQ,oBAAoB,CAAE,UAAnD;cAAsD,KAAA,EAAO,MAAM,CAAC,CAApE;aAAX,EAAmF,4BAAnF,EAAA;;iBACA,QAAA,CAAA;QAH2G,CAA5G;MA7C6C,CAA9C;IAd0B,CAA3B;IAiEA,yBAAA,EAA2B,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,SAAR,GAAA;;aAClD,aAAa,CAAC,iBAAd,CAAgC,UAAhC,EAA4C,SAAC,KAAD,EAAQ,SAAR;QAC3C,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,SAAf;MAF2C,CAA5C;IAD0B,CAjE3B;IAuEA,qBAAA,EAAuB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;;QAAqB,WAAW,SAAC,KAAD,GAAA;;aACtD,YAAY,CAAC,iBAAb,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,KAAD;QAClD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT;MAFkD,CAAnD;IADsB,CAvEvB;IA6EA,qBAAA,EAAuB,GA7EvB;IA8EA,0BAAA,EAA4B,SAAC,UAAD,EAAa,MAAb,EAAqB,SAArB,EAAgC,QAAhC;;QAAgC,WAAW,SAAC,KAAD,GAAA;;aAEtE,YAAY,CAAC,mBAAb,CAAiC,MAAjC,EAAyC,cAAc,CAAC,qBAAxD,EAA+E,SAAC,KAAD,EAAQ,UAAR;AAC9E,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,MAAA,GAAS,UAAU,CAAC;eAEpB,YAAY,CAAC,gBAAb,CAA8B,UAA9B,EAA0C,SAAC,KAAD,EAAQ,UAAR;UACzC,IAAG,aAAH;YACC,MAAM,CAAC,GAAP,CAAW;cAAA,UAAA,EAAY,UAAZ;cAAwB,MAAA,EAAQ,MAAhC;cAAwC,UAAA,EAAY,UAApD;aAAX,EAA2E,4BAA3E;AACA,mBAAO,QAAA,CAAS,KAAT,EAFR;;UAGA,MAAM,CAAC,GAAP,CAAW;YAAA,UAAA,EAAY,UAAZ;YAAwB,MAAA,EAAQ,MAAhC;YAAwC,UAAA,EAAY,UAApD;WAAX,EAA2E,kCAA3E;iBACA,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,UAA7D,EAAyE,SAAzE,EAAoF,SAAC,KAAD;YACnF,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,MAAM,CAAC,GAAP,CAAW;cAAA,UAAA,EAAY,UAAZ;cAAwB,MAAA,EAAQ,MAAhC;aAAX,EAAmD,kCAAnD;mBAEA,YAAY,CAAC,uBAAb,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,UAAzD,EAAqE,SAAC,KAAD;cACpE,IAA0B,aAA1B;AAAA,uBAAO,QAAA,CAAS,KAAT,EAAP;;cACA,IAAG,MAAA,KAAU,cAAc,CAAC,qBAA5B;gBAEC,MAAM,CAAC,GAAP,CAAW;kBAAA,UAAA,EAAY,UAAZ;kBAAwB,MAAA,EAAQ,MAAhC;iBAAX,EAAmD,+BAAnD;uBACA,UAAA,CAAW,SAAA;yBACV,cAAc,CAAC,0BAAf,CAA0C,UAA1C,EAAsD,MAAtD,EAA8D,SAA9D,EAAyE,QAAzE;gBADU,CAAX,EAEE,CAFF,EAHD;eAAA,MAAA;gBAOC,MAAM,CAAC,GAAP,CAAW;kBAAA,UAAA,EAAY,UAAZ;kBAAwB,MAAA,EAAQ,MAAhC;iBAAX,EAAmD,2BAAnD;uBACA,QAAA,CAAA,EARD;;YAFoE,CAArE;UAJmF,CAApF;QALyC,CAA1C;MAJ8E,CAA/E;IAF2B,CA9E5B;IA0GA,kCAAA,EAAoC,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;;QAAqB,WAAW,SAAC,KAAD,GAAA;;aACnE,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,SAAC,KAAD,EAAQ,SAAR;QACpD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,cAAc,CAAC,yCAAf,CAAyD,UAAzD,EAAqE,MAArE,EAA6E,SAA7E,EAAwF,QAAxF;MAFoD,CAArD;IADmC,CA1GpC;IAiHA,yCAAA,EAA2C,SAAC,UAAD,EAAa,MAAb,EAAqB,SAArB,EAAgC,QAAhC;;QAAgC,WAAW,SAAC,KAAD,GAAA;;aACrF,cAAc,CAAC,qBAAf,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,SAAC,KAAD;QACxD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,WAAW,CAAC,WAAZ,CACC,IAAI,CAAC,WAAL,CAAiB;UAAC,QAAA,MAAD;SAAjB,CADD,EAEC,SAAC,WAAD;iBACC,cAAc,CAAC,0BAAf,CAA0C,UAA1C,EAAsD,MAAtD,EAA8D,SAA9D,EAAyE,WAAzE;QADD,CAFD,EAIC,QAJD;MAFwD,CAAzD;IAD0C,CAjH3C;IA4HA,oCAAA,EAAsC,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,GAAA;;aAC7D,YAAY,CAAC,uBAAb,CAAqC,UAArC,EAAiD,SAAC,KAAD,EAAQ,OAAR;QAChD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,SAAC,KAAD,EAAQ,SAAR;AACpD,cAAA;UAAA,IAAA,GAAO;eAEH,SAAC,MAAD;mBACF,IAAI,CAAC,IAAL,CAAU,SAAC,EAAD;qBACT,cAAc,CAAC,yCAAf,CAAyD,UAAzD,EAAqE,MAArE,EAA6E,SAA7E,EAAwF,EAAxF;YADS,CAAV;UADE;AADJ,eAAA,yCAAA;;eACK;AADL;iBAIA,KAAK,CAAC,aAAN,CAAoB,IAApB,EAA0B,CAA1B,EAA6B,QAA7B;QANoD,CAArD;MAFgD,CAAjD;IADqC,CA5HtC;IAwIA,QAAA,EAAU,SAAC,KAAD,EAAQ,QAAR;;QAAQ,WAAW,SAAC,KAAD,EAAQ,MAAR,GAAA;;aAC5B,YAAY,CAAC,2BAAb,CAAyC,SAAC,KAAD,EAAQ,WAAR;AACxC,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,MAAM,CAAC,GAAP,CAAW;UAAC,KAAA,wBAAO,WAAW,CAAE,eAArB;UAA6B,WAAA,EAAa,WAA1C;SAAX,EAAmE,gBAAnE;QACA,IAAA,GAAO;QACP,WAAA,GAAc,CAAC,CAAC,OAAF,CAAU,WAAV;QACd,gBAAA,GAAsB,KAAA,GAAQ,CAAX,GAAkB,WAAlB,GAAmC,WAAY;aAE9D,SAAC,UAAD;iBACF,IAAI,CAAC,IAAL,CAAU,SAAC,EAAD;mBACT,cAAc,CAAC,oCAAf,CAAoD,UAApD,EAAgE,SAAC,GAAD;AAC/D,qBAAO,EAAA,CAAG,IAAH,EAAS;gBAAC,MAAA,EAAQ,WAAT;gBAAe,UAAA,EAAY,UAA3B;eAAT;YADwD,CAAhE;UADS,CAAV;QADE;AADJ,aAAA,kDAAA;;aACK;AADL;eAKA,KAAK,CAAC,MAAN,CAAa,IAAb,EAAmB,SAAC,KAAD,EAAQ,MAAR;AAClB,cAAA;UAAA,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACA,cAAA;;AAAkB;iBAAA,0CAAA;;kBAAkC,CAAC,CAAC;8BAApC,CAAC,CAAC;;AAAF;;;UAClB,iBAAA;;AAAqB;iBAAA,0CAAA;;kBAAkC,CAAI,CAAC,CAAC;8BAAxC,CAAC,CAAC;;AAAF;;;iBACrB,QAAA,CAAS,IAAT,EAAe;YAAC,MAAA,EAAQ,cAAT;YAAyB,SAAA,EAAW,iBAApC;YAAuD,GAAA,EAAK,WAA5D;WAAf;QAJkB,CAAnB;MAXwC,CAAzC;IADS,CAxIV;IA0JA,kBAAA,EAAoB,SAAC,QAAD;;QAAC,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAC/B,YAAY,CAAC,0BAAb,CAAwC,SAAC,KAAD,EAAQ,WAAR;QACvC,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,YAAY,CAAC,2BAAb,CAAyC,SAAC,KAAD,EAAQ,eAAR;AACxC,cAAA;UAAA,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UAEA,IAAA,GAAO,SAAC,EAAD;mBAAQ,KAAK,CAAC,YAAN,CAAmB,eAAnB,EAAoC,YAAY,CAAC,uBAAjD,EAA0E,EAA1E;UAAR;iBAEP,IAAA,CAAK,SAAC,KAAD,EAAQ,eAAR;AACJ,gBAAA;YAAA,gBAAA,GAAmB,CAAC,CAAC,UAAF,CAAa,WAAb,EAA0B,eAA1B;YACnB,MAAM,CAAC,GAAP,CAAW;cAAC,WAAA,EAAa,WAAd;cAA2B,eAAA,EAAiB,eAA5C;cAA6D,eAAA,EAAiB,eAA9E;cAA+F,gBAAA,EAAkB,gBAAjH;aAAX,EAA+I,+BAA/I;mBACA,QAAA,CAAS,IAAT,EAAe,gBAAf;UAHI,CAAL;QALwC,CAAzC;MAFuC,CAAxC;IADmB,CA1JpB;IAuKA,aAAA,EAAe,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAAmC,QAAnC;;QAAqB,UAAU;;;QAAI,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAC5D,cAAc,CAAC,kCAAf,CAAkD,UAAlD,EAA8D,MAA9D,EAAsE,SAAC,KAAD;QACrE,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eAEA,WAAW,CAAC,oBAAZ,CAAiC,UAAjC,EAA6C,MAA7C,EAAqD,OAAO,CAAC,IAA7D,EAAmE,OAAO,CAAC,EAA3E,EAA+E,SAAC,KAAD,EAAQ,OAAR;UAC9E,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT,EAAe,OAAf;QAF8E,CAA/E;MAHqE,CAAtE;IADc,CAvKf;IA+KA,yBAAA,EAA2B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAAmC,QAAnC;;QAAqB,UAAU;;;QAAI,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aACxE,cAAc,CAAC,aAAf,CAA6B,UAA7B,EAAyC,MAAzC,EAAiD,OAAjD,EAA0D,SAAC,KAAD,EAAQ,OAAR;QACzD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,cAAc,CAAC,YAAf,CAA4B,OAA5B,EAAqC,SAAC,KAAD,EAAQ,OAAR;UACpC,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT,EAAe,OAAf;QAFoC,CAArC;MAFyD,CAA1D;IAD0B,CA/K3B;IAsLA,2BAAA,EAA6B,SAAC,UAAD,EAAa,OAAb,EAA2B,QAA3B;AAC5B,UAAA;;QADyC,UAAU;;;QAAI,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAClE,OAAO,CAAC,cAAR,OAAO,CAAC,YAAc;MACtB,iBAAA,GAAoB;MACpB,MAAA,GAAS,OAAO,CAAC;MACjB,mBAAA,GAAsB;aACtB,cAAc,CAAC,oCAAf,CAAoD,UAApD,EAAgE,SAAC,KAAD;QAC/D,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,WAAW,CAAC,mBAAZ,CAAgC,UAAhC,EAA4C,MAA5C,EAAoD,SAAC,GAAD,EAAM,QAAN;UACnD,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;iBAEA,KAAK,CAAC,MAAN,CAAa,SAAA;AAEZ,mBAAO,iBAAiB,CAAC,MAAlB,GAA2B,OAAO,CAAC,SAAnC,IAAiD,CAAI,QAAQ,CAAC,IAAT,CAAA;UAFhD,CAAb,EAGE,SAAC,EAAD;mBACD,QAAQ,CAAC,IAAT,CAAc,SAAC,GAAD,EAAM,cAAN;cACb,IAAwB,WAAxB;AAAA,uBAAO,QAAA,CAAS,GAAT,EAAP;;cAEA,IAAe,cAAc,CAAC,MAAf,KAAyB,CAAxC;AAAA,uBAAO,EAAA,CAAA,EAAP;;cACA,mBAAA,GAAsB,cAAe,CAAA,cAAc,CAAC,MAAf,GAAwB,CAAxB,CAA0B,CAAC,IAAI,CAAC;cAErE,iBAAA,GAAoB,cAAc,CAAC,iBAAf,CAAiC,cAAjC,EAAiD,iBAAjD;qBACpB,EAAA,CAAA;YAPa,CAAd;UADC,CAHF,EAYE,SAAA;mBAGD,cAAc,CAAC,sBAAf,CAAsC,iBAAtC,EAAyD,SAAC,GAAD,EAAM,OAAN;cACxD,IAAwB,WAAxB;AAAA,uBAAO,QAAA,CAAS,GAAT,EAAP;;qBACA,QAAA,CAAS,IAAT,EAAe,OAAf,EAA2B,CAAI,QAAQ,CAAC,IAAT,CAAA,CAAP,GAA4B,mBAA5B,GAAqD,MAA7E;YAFwD,CAAzD;UAHC,CAZF;QAHmD,CAApD;MAF+D,CAAhE;IAL4B,CAtL7B;IAmNA,aAAA,EAAe,SAAC,KAAD,EAAQ,QAAR;AACd,UAAA;;QADsB,WAAW,SAAC,KAAD,EAAQ,eAAR,GAAA;;MACjC,IAAA,GAAO;MACP,eAAA,GAAkB;WAEd,SAAC,OAAD;eACF,IAAI,CAAC,IAAL,CAAU,SAAC,QAAD;iBACT,aAAa,CAAC,WAAd,CAA0B,OAA1B,EAAmC,SAAC,KAAD,EAAQ,QAAR;YAClC,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,eAAgB,CAAA,OAAA,CAAhB,GAA2B;mBAC3B,QAAA,CAAA;UAHkC,CAAnC;QADS,CAAV;MADE;AADJ,WAAA,gBAAA;WACK;AADL;aAQA,KAAK,CAAC,MAAN,CAAa,IAAb,EAAmB,SAAC,GAAD;QAClB,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,eAAf;MAFkB,CAAnB;IAXc,CAnNf;IAkOA,YAAA,EAAc,SAAC,OAAD,EAAU,QAAV;AACb,UAAA;;QADuB,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAClC,KAAA,GAAQ;AACR,WAAA,yCAAA;;QACC,OAAA,GAAU,MAAM,CAAC,IAAI,CAAC;QACtB,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;UACC,KAAM,CAAA,OAAA,CAAN,GAAiB,KADlB;;AAFD;aAKA,cAAc,CAAC,aAAf,CAA6B,KAA7B,EAAoC,SAAC,KAAD,EAAQ,eAAR;AACnC,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;AACA,aAAA,2CAAA;;UACC,OAAA,GAAU,MAAM,CAAC,IAAI,CAAC;UACtB,OAAO,MAAM,CAAC,IAAI,CAAC;UACnB,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;YACC,MAAM,CAAC,IAAI,CAAC,IAAZ,GAAmB,eAAgB,CAAA,OAAA,EADpC;;AAHD;eAKA,QAAA,CAAS,IAAT,EAAe,OAAf;MAPmC,CAApC;IAPa,CAlOd;IAkPA,sBAAA,EAAwB,SAAC,OAAD,EAAU,QAAV;AACvB,UAAA;;QADiC,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAC5C,KAAA,GAAQ;AACR,WAAA,yCAAA;;QACC,QAAA,GAAW,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB;AACnC,aAAA,4CAAA;;UACC,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;YACC,KAAM,CAAA,OAAA,CAAN,GAAiB,KADlB;;AADD;AAFD;aAMA,cAAc,CAAC,aAAf,CAA6B,KAA7B,EAAoC,SAAC,KAAD,EAAQ,eAAR;AACnC,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;AACA,aAAA,2CAAA;;UACC,QAAA,GAAW,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB;UACnC,MAAM,CAAC,IAAI,CAAC,KAAZ,GAAoB;UACpB,OAAO,MAAM,CAAC,IAAI,CAAC;AACnB,eAAA,4CAAA;;YACC,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;cACC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAlB,CAAuB,eAAgB,CAAA,OAAA,CAAvC,EADD;aAAA,MAAA;cAGC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAlB,CAAuB,IAAvB,EAHD;;AADD;AAJD;eASA,QAAA,CAAS,IAAT,EAAe,OAAf;MAXmC,CAApC;IARuB,CAlPxB;IAuQA,YAAA,EAAc,SAAC,OAAD;MACb,IAAI,eAAJ;AACC,eAAO,MADR;OAAA,MAAA;AAGC,eAAO,CAAC,CAAC,OAAO,CAAC,KAAR,CAAc,gBAAd,EAHV;;IADa,CAvQd;IA6QA,6BAAA,EAA+B,WAAA,GAAc,CAAA,GAAI,EAAJ,GAAS,IA7QtD;IA8QA,oBAAA,EAAsB,EA9QtB;IA+QA,iBAAA,EAAmB,SAAC,OAAD,EAAU,yBAAV;AAClB,UAAA;;QAD4B,4BAA4B;;MACxD,iBAAA,GAAoB,yBAAyB,CAAC,KAA1B,CAAA;MACpB,0BAAA,GAA6B;AAC7B,WAAA,yCAAA;;QACC,cAAA,GAAiB,iBAAkB,CAAA,iBAAiB,CAAC,MAAlB,GAA2B,CAA3B;QACnC,YAAA,GAAe;QAQf,IAAG,0BAAH;UACC,YAAA,GAAe,MADhB;SAAA,MAEK,IAAG,cAAA,IAAmB,cAAc,CAAC,IAAI,CAAC,MAApB,GAA6B,MAAM,CAAC,IAAI,CAAC,QAAzC,GAAoD,IAAC,CAAA,6BAA3E;UAGJ,YAAA,GAAe,KAHX;;QAKL,WAAA,GAAc;AACd;AAAA,aAAA,uCAAA;;UACC,IAAG,cAAA,IAAU,EAAE,CAAC,CAAC,CAAC,MAAL,GAAc,IAAC,CAAA,oBAA5B;YACC,WAAA,GAAc,KADf;;AADD;QAIA,0BAAA,GAA6B;QAE7B,IAAG,YAAH;UAGC,cAAc,CAAC,IAAI,CAAC,QAApB,GAA+B,CAAC,CAAC,KAAF,CAAQ,cAAc,CAAC,IAAI,CAAC,QAA5B,EAAsC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAb,CAAtC;UAE/B,MAAA,GAAS,MAAM,CAAC,MAAM,CAAC,QAAd,CAAA;UACT,GAAA,GAAM,cAAc,CAAC,IAAK,CAAA,MAAA;UAC1B,IAAG,WAAH;YACC,GAAG,CAAC,KAAJ,GAAY,IAAI,CAAC,GAAL,CAAS,GAAG,CAAC,KAAb,EAAoB,MAAM,CAAC,CAA3B;YACZ,GAAG,CAAC,GAAJ,GAAU,IAAI,CAAC,GAAL,CAAS,GAAG,CAAC,GAAb,EAAkB,MAAM,CAAC,CAAzB,EAFX;WAAA,MAAA;YAIC,cAAc,CAAC,IAAK,CAAA,MAAA,CAApB,GACC;cAAA,KAAA,EAAO,MAAM,CAAC,CAAd;cACA,GAAA,EAAK,MAAM,CAAC,CADZ;cALF;;UAQA,cAAc,CAAC,IAAI,CAAC,QAApB,GAA+B,IAAI,CAAC,GAAL,CAAS,cAAc,CAAC,IAAI,CAAC,QAA7B,EAAuC,MAAM,CAAC,IAAI,CAAC,QAAnD;UAC/B,cAAc,CAAC,IAAI,CAAC,MAApB,GAA+B,IAAI,CAAC,GAAL,CAAS,cAAc,CAAC,IAAI,CAAC,MAA7B,EAAqC,MAAM,CAAC,IAAI,CAAC,MAAjD,EAhBhC;SAAA,MAAA;UAkBC,SAAA,GACC;YAAA,IAAA,EACC;cAAA,QAAA,EAAU,EAAV;cACA,QAAA,EAAU,MAAM,CAAC,IAAI,CAAC,QADtB;cAEA,MAAA,EAAQ,MAAM,CAAC,IAAI,CAAC,MAFpB;aADD;YAIA,IAAA,EAAM,EAJN;;UAMD,SAAS,CAAC,IAAK,CAAA,MAAM,CAAC,MAAM,CAAC,QAAd,CAAA,CAAA,CAAf,GACC;YAAA,KAAA,EAAO,MAAM,CAAC,CAAd;YACA,GAAA,EAAK,MAAM,CAAC,CADZ;;UAED,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAxB,CAA6B,MAAM,CAAC,IAAI,CAAC,OAAzC;UACA,iBAAiB,CAAC,IAAlB,CAAuB,SAAvB,EA7BD;;AAxBD;AAuDA,aAAO;IA1DW,CA/QnB;;AAdD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/WebApiManager.js b/services/track-changes/app/js/WebApiManager.js index 937b767ab2..2ab3bdfda1 100644 --- a/services/track-changes/app/js/WebApiManager.js +++ b/services/track-changes/app/js/WebApiManager.js @@ -26,7 +26,7 @@ const MAX_HTTP_REQUEST_LENGTH = 15000 // 15 seconds module.exports = WebApiManager = { sendRequest(url, callback) { if (callback == null) { - callback = function(error, body) {} + callback = function (error, body) {} } return request.get( { @@ -39,7 +39,7 @@ module.exports = WebApiManager = { sendImmediately: true } }, - function(error, res, body) { + function (error, res, body) { if (error != null) { return callback(error) } @@ -61,11 +61,11 @@ module.exports = WebApiManager = { getUserInfo(user_id, callback) { if (callback == null) { - callback = function(error, userInfo) {} + callback = function (error, userInfo) {} } const url = `/user/${user_id}/personal_info` logger.log({ user_id }, 'getting user info from web') - return WebApiManager.sendRequest(url, function(error, body) { + return WebApiManager.sendRequest(url, function (error, body) { let user if (error != null) { logger.error({ err: error, user_id, url }, 'error accessing web') @@ -93,11 +93,11 @@ module.exports = WebApiManager = { getProjectDetails(project_id, callback) { if (callback == null) { - callback = function(error, details) {} + callback = function (error, details) {} } const url = `/project/${project_id}/details` logger.log({ project_id }, 'getting project details from web') - return WebApiManager.sendRequest(url, function(error, body) { + return WebApiManager.sendRequest(url, function (error, body) { let project if (error != null) { logger.error({ err: error, project_id, url }, 'error accessing web') diff --git a/services/track-changes/app/js/WebApiManager.js.map b/services/track-changes/app/js/WebApiManager.js.map new file mode 100644 index 0000000000..1ef0953f13 --- /dev/null +++ b/services/track-changes/app/js/WebApiManager.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "WebApiManager.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/WebApiManager.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,OAAA,GAAU,OAAA,CAAQ,cAAR;;EACV,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EAGX,uBAAA,GAA0B;;EAO1B,MAAM,CAAC,OAAP,GAAiB,aAAA,GAChB;IAAA,WAAA,EAAa,SAAC,GAAD,EAAM,QAAN;;QAAM,WAAW,SAAC,KAAD,EAAQ,IAAR,GAAA;;aAC7B,OAAO,CAAC,GAAR,CAAY;QACX,GAAA,EAAK,EAAA,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAArB,GAA2B,GADrB;QAEX,OAAA,EAAS,uBAFE;QAGX,WAAA,EAAa,CAHF;QAIX,IAAA,EACC;UAAA,IAAA,EAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAxB;UACA,IAAA,EAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IADxB;UAEA,eAAA,EAAiB,IAFjB;SALU;OAAZ,EAQG,SAAC,KAAD,EAAQ,GAAR,EAAa,IAAb;QACF,IAAG,aAAH;AACC,iBAAO,QAAA,CAAS,KAAT,EADR;;QAEA,IAAG,GAAG,CAAC,UAAJ,KAAkB,GAArB;UACC,MAAM,CAAC,GAAP,CAAW;YAAA,GAAA,EAAK,GAAL;WAAX,EAAqB,sBAArB;AACA,iBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EAFR;;QAGA,IAAG,GAAG,CAAC,UAAJ,IAAkB,GAAlB,IAA0B,GAAG,CAAC,UAAJ,GAAiB,GAA9C;AACC,iBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EADR;SAAA,MAAA;UAGC,KAAA,GAAQ,IAAI,KAAJ,CAAU,0CAAA,GAA2C,GAAG,CAAC,UAA/C,GAA0D,cAA1D,GAAwE,GAAG,CAAC,QAA5E,GAAqF,GAA/F;iBACR,QAAA,CAAS,KAAT,EAJD;;MANE,CARH;IADY,CAAb;IAqBA,WAAA,EAAa,SAAC,OAAD,EAAU,QAAV;AACZ,UAAA;;QADsB,WAAW,SAAC,KAAD,EAAQ,QAAR,GAAA;;MACjC,GAAA,GAAM,QAAA,GAAS,OAAT,GAAiB;MACvB,MAAM,CAAC,GAAP,CAAW;QAAA,OAAA,EAAS,OAAT;OAAX,EAA6B,4BAA7B;aACA,aAAa,CAAC,WAAd,CAA0B,GAA1B,EAA+B,SAAC,KAAD,EAAQ,IAAR;AAC9B,YAAA;QAAA,IAAG,aAAH;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,OAAA,EAAS,OAArB;YAA8B,GAAA,EAAK,GAAnC;WAAb,EAAqD,qBAArD;AACA,iBAAO,QAAA,CAAS,KAAT,EAFR;;QAIA,IAAG,IAAA,KAAQ,IAAX;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,OAAA,EAAS,OAAT;YAAkB,GAAA,EAAK,GAAvB;WAAb,EAAyC,eAAzC;AACA,iBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EAFR;;AAGA;UACC,IAAA,GAAO,IAAI,CAAC,KAAL,CAAW,IAAX,EADR;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,KAAT,EAHR;;eAIA,QAAA,CAAS,IAAT,EAAe;UACd,EAAA,EAAI,IAAI,CAAC,EADK;UAEd,KAAA,EAAO,IAAI,CAAC,KAFE;UAGd,UAAA,EAAY,IAAI,CAAC,UAHH;UAId,SAAA,EAAW,IAAI,CAAC,SAJF;SAAf;MAZ8B,CAA/B;IAHY,CArBb;IA2CA,iBAAA,EAAmB,SAAC,UAAD,EAAa,QAAb;AAClB,UAAA;;QAD+B,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAC1C,GAAA,GAAM,WAAA,GAAY,UAAZ,GAAuB;MAC7B,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;OAAX,EAAmC,kCAAnC;aACA,aAAa,CAAC,WAAd,CAA0B,GAA1B,EAA+B,SAAC,KAAD,EAAQ,IAAR;AAC9B,YAAA;QAAA,IAAG,aAAH;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAY,UAAxB;YAAoC,GAAA,EAAK,GAAzC;WAAb,EAA2D,qBAA3D;AACA,iBAAO,QAAA,CAAS,KAAT,EAFR;;AAIA;UACC,OAAA,GAAU,IAAI,CAAC,KAAL,CAAW,IAAX,EADX;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,KAAT,EAHR;;eAIA,QAAA,CAAS,IAAT,EAAe,OAAf;MAT8B,CAA/B;IAHkB,CA3CnB;;AAbD" +} \ No newline at end of file diff --git a/services/track-changes/app/js/mongojs.js.map b/services/track-changes/app/js/mongojs.js.map new file mode 100644 index 0000000000..9c2dbd94d8 --- /dev/null +++ b/services/track-changes/app/js/mongojs.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "mongojs.js", + "sourceRoot": "../..", + "sources": [ + "app/coffee/mongojs.coffee" + ], + "names": [], + "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,IAAA,GAAO,OAAA,CAAQ,MAAR;;EACP,EAAA,GAAK,OAAA,CAAQ,QAAQ,CAAC,KAAK,CAAC,GAAvB,EAA4B,CAAC,YAAD,EAAe,wBAAf,EAAyC,iBAAzC,CAA5B;;EACL,MAAM,CAAC,OAAP,GACC;IAAA,EAAA,EAAI,EAAJ;IACA,QAAA,EAAU,OAAO,CAAC,QADlB;IAEA,IAAA,EAAM,IAAI,IAAI,CAAC,QAAT,CAAA,CAFN;;AALD" +} \ No newline at end of file diff --git a/services/track-changes/app/lib/diff_match_patch.js b/services/track-changes/app/lib/diff_match_patch.js index 112130e097..c4864d7bde 100644 --- a/services/track-changes/app/lib/diff_match_patch.js +++ b/services/track-changes/app/lib/diff_match_patch.js @@ -28,48 +28,44 @@ * @constructor */ function diff_match_patch() { - // Defaults. // Redefine these in your program to override the defaults. // Number of seconds to map a diff before giving up (0 for infinity). - this.Diff_Timeout = 1.0; + this.Diff_Timeout = 1.0 // Cost of an empty edit operation in terms of edit characters. - this.Diff_EditCost = 4; + this.Diff_EditCost = 4 // At what point is no match declared (0.0 = perfection, 1.0 = very loose). - this.Match_Threshold = 0.5; + this.Match_Threshold = 0.5 // How far to search for a match (0 = exact location, 1000+ = broad match). // A match this many characters away from the expected location will add // 1.0 to the score (0.0 is a perfect match). - this.Match_Distance = 1000; + this.Match_Distance = 1000 // When deleting a large block of text (over ~64 characters), how close do // the contents have to be to match the expected contents. (0.0 = perfection, // 1.0 = very loose). Note that Match_Threshold controls how closely the // end points of a delete need to match. - this.Patch_DeleteThreshold = 0.5; + this.Patch_DeleteThreshold = 0.5 // Chunk size for context length. - this.Patch_Margin = 4; + this.Patch_Margin = 4 // The number of bits in an int. - this.Match_MaxBits = 32; + this.Match_MaxBits = 32 } - // DIFF FUNCTIONS - /** * The data structure representing a diff is an array of tuples: * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] * which means: delete 'Hello', add 'Goodbye' and keep ' world.' */ -var DIFF_DELETE = -1; -var DIFF_INSERT = 1; -var DIFF_EQUAL = 0; +var DIFF_DELETE = -1 +var DIFF_INSERT = 1 +var DIFF_EQUAL = 0 /** @typedef {{0: number, 1: string}} */ -diff_match_patch.Diff; - +diff_match_patch.Diff /** * Find the differences between two texts. Simplifies the problem by stripping @@ -84,62 +80,65 @@ diff_match_patch.Diff; * instead. * @return {!Array.} Array of diff tuples. */ -diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines, - opt_deadline) { +diff_match_patch.prototype.diff_main = function ( + text1, + text2, + opt_checklines, + opt_deadline +) { // Set a deadline by which time the diff must be complete. - if (typeof opt_deadline == 'undefined') { + if (typeof opt_deadline === 'undefined') { if (this.Diff_Timeout <= 0) { - opt_deadline = Number.MAX_VALUE; + opt_deadline = Number.MAX_VALUE } else { - opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000; + opt_deadline = new Date().getTime() + this.Diff_Timeout * 1000 } } - var deadline = opt_deadline; + var deadline = opt_deadline // Check for null inputs. if (text1 == null || text2 == null) { - throw new Error('Null input. (diff_main)'); + throw new Error('Null input. (diff_main)') } // Check for equality (speedup). if (text1 == text2) { if (text1) { - return [[DIFF_EQUAL, text1]]; + return [[DIFF_EQUAL, text1]] } - return []; + return [] } - if (typeof opt_checklines == 'undefined') { - opt_checklines = true; + if (typeof opt_checklines === 'undefined') { + opt_checklines = true } - var checklines = opt_checklines; + var checklines = opt_checklines // Trim off common prefix (speedup). - var commonlength = this.diff_commonPrefix(text1, text2); - var commonprefix = text1.substring(0, commonlength); - text1 = text1.substring(commonlength); - text2 = text2.substring(commonlength); + var commonlength = this.diff_commonPrefix(text1, text2) + var commonprefix = text1.substring(0, commonlength) + text1 = text1.substring(commonlength) + text2 = text2.substring(commonlength) // Trim off common suffix (speedup). - commonlength = this.diff_commonSuffix(text1, text2); - var commonsuffix = text1.substring(text1.length - commonlength); - text1 = text1.substring(0, text1.length - commonlength); - text2 = text2.substring(0, text2.length - commonlength); + commonlength = this.diff_commonSuffix(text1, text2) + var commonsuffix = text1.substring(text1.length - commonlength) + text1 = text1.substring(0, text1.length - commonlength) + text2 = text2.substring(0, text2.length - commonlength) // Compute the diff on the middle block. - var diffs = this.diff_compute_(text1, text2, checklines, deadline); + var diffs = this.diff_compute_(text1, text2, checklines, deadline) // Restore the prefix and suffix. if (commonprefix) { - diffs.unshift([DIFF_EQUAL, commonprefix]); + diffs.unshift([DIFF_EQUAL, commonprefix]) } if (commonsuffix) { - diffs.push([DIFF_EQUAL, commonsuffix]); + diffs.push([DIFF_EQUAL, commonsuffix]) } - this.diff_cleanupMerge(diffs); - return diffs; -}; - + this.diff_cleanupMerge(diffs) + return diffs +} /** * Find the differences between two texts. Assumes that the texts do not @@ -153,64 +152,72 @@ diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines, * @return {!Array.} Array of diff tuples. * @private */ -diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines, - deadline) { - var diffs; +diff_match_patch.prototype.diff_compute_ = function ( + text1, + text2, + checklines, + deadline +) { + var diffs if (!text1) { // Just add some text (speedup). - return [[DIFF_INSERT, text2]]; + return [[DIFF_INSERT, text2]] } if (!text2) { // Just delete some text (speedup). - return [[DIFF_DELETE, text1]]; + return [[DIFF_DELETE, text1]] } - var longtext = text1.length > text2.length ? text1 : text2; - var shorttext = text1.length > text2.length ? text2 : text1; - var i = longtext.indexOf(shorttext); + var longtext = text1.length > text2.length ? text1 : text2 + var shorttext = text1.length > text2.length ? text2 : text1 + var i = longtext.indexOf(shorttext) if (i != -1) { // Shorter text is inside the longer text (speedup). - diffs = [[DIFF_INSERT, longtext.substring(0, i)], - [DIFF_EQUAL, shorttext], - [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + diffs = [ + [DIFF_INSERT, longtext.substring(0, i)], + [DIFF_EQUAL, shorttext], + [DIFF_INSERT, longtext.substring(i + shorttext.length)] + ] // Swap insertions for deletions if diff is reversed. if (text1.length > text2.length) { - diffs[0][0] = diffs[2][0] = DIFF_DELETE; + diffs[0][0] = diffs[2][0] = DIFF_DELETE } - return diffs; + return diffs } if (shorttext.length == 1) { // Single character string. // After the previous speedup, the character can't be an equality. - return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + return [ + [DIFF_DELETE, text1], + [DIFF_INSERT, text2] + ] } // Check to see if the problem can be split in two. - var hm = this.diff_halfMatch_(text1, text2); + var hm = this.diff_halfMatch_(text1, text2) if (hm) { // A half-match was found, sort out the return data. - var text1_a = hm[0]; - var text1_b = hm[1]; - var text2_a = hm[2]; - var text2_b = hm[3]; - var mid_common = hm[4]; + var text1_a = hm[0] + var text1_b = hm[1] + var text2_a = hm[2] + var text2_b = hm[3] + var mid_common = hm[4] // Send both pairs off for separate processing. - var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline); - var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline); + var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline) + var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline) // Merge the results. - return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); + return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b) } if (checklines && text1.length > 100 && text2.length > 100) { - return this.diff_lineMode_(text1, text2, deadline); + return this.diff_lineMode_(text1, text2, deadline) } - return this.diff_bisect_(text1, text2, deadline); -}; - + return this.diff_bisect_(text1, text2, deadline) +} /** * Do a quick line-level diff on both strings, then rediff the parts for @@ -222,64 +229,65 @@ diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines, * @return {!Array.} Array of diff tuples. * @private */ -diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) { +diff_match_patch.prototype.diff_lineMode_ = function (text1, text2, deadline) { // Scan the text on a line-by-line basis first. - var a = this.diff_linesToChars_(text1, text2); - text1 = a.chars1; - text2 = a.chars2; - var linearray = a.lineArray; + var a = this.diff_linesToChars_(text1, text2) + text1 = a.chars1 + text2 = a.chars2 + var linearray = a.lineArray - var diffs = this.diff_main(text1, text2, false, deadline); + var diffs = this.diff_main(text1, text2, false, deadline) // Convert the diff back to original text. - this.diff_charsToLines_(diffs, linearray); + this.diff_charsToLines_(diffs, linearray) // Eliminate freak matches (e.g. blank lines) - this.diff_cleanupSemantic(diffs); + this.diff_cleanupSemantic(diffs) // Rediff any replacement blocks, this time character-by-character. // Add a dummy entry at the end. - diffs.push([DIFF_EQUAL, '']); - var pointer = 0; - var count_delete = 0; - var count_insert = 0; - var text_delete = ''; - var text_insert = ''; + diffs.push([DIFF_EQUAL, '']) + var pointer = 0 + var count_delete = 0 + var count_insert = 0 + var text_delete = '' + var text_insert = '' while (pointer < diffs.length) { switch (diffs[pointer][0]) { case DIFF_INSERT: - count_insert++; - text_insert += diffs[pointer][1]; - break; + count_insert++ + text_insert += diffs[pointer][1] + break case DIFF_DELETE: - count_delete++; - text_delete += diffs[pointer][1]; - break; + count_delete++ + text_delete += diffs[pointer][1] + break case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if (count_delete >= 1 && count_insert >= 1) { // Delete the offending records and add the merged ones. - diffs.splice(pointer - count_delete - count_insert, - count_delete + count_insert); - pointer = pointer - count_delete - count_insert; - var a = this.diff_main(text_delete, text_insert, false, deadline); + diffs.splice( + pointer - count_delete - count_insert, + count_delete + count_insert + ) + pointer = pointer - count_delete - count_insert + var a = this.diff_main(text_delete, text_insert, false, deadline) for (var j = a.length - 1; j >= 0; j--) { - diffs.splice(pointer, 0, a[j]); + diffs.splice(pointer, 0, a[j]) } - pointer = pointer + a.length; + pointer = pointer + a.length } - count_insert = 0; - count_delete = 0; - text_delete = ''; - text_insert = ''; - break; + count_insert = 0 + count_delete = 0 + text_delete = '' + text_insert = '' + break } - pointer++; + pointer++ } - diffs.pop(); // Remove the dummy entry at the end. - - return diffs; -}; + diffs.pop() // Remove the dummy entry at the end. + return diffs +} /** * Find the 'middle snake' of a diff, split the problem in two @@ -291,69 +299,72 @@ diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) { * @return {!Array.} Array of diff tuples. * @private */ -diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) { +diff_match_patch.prototype.diff_bisect_ = function (text1, text2, deadline) { // Cache the text lengths to prevent multiple calls. - var text1_length = text1.length; - var text2_length = text2.length; - var max_d = Math.ceil((text1_length + text2_length) / 2); - var v_offset = max_d; - var v_length = 2 * max_d; - var v1 = new Array(v_length); - var v2 = new Array(v_length); + var text1_length = text1.length + var text2_length = text2.length + var max_d = Math.ceil((text1_length + text2_length) / 2) + var v_offset = max_d + var v_length = 2 * max_d + var v1 = new Array(v_length) + var v2 = new Array(v_length) // Setting all elements to -1 is faster in Chrome & Firefox than mixing // integers and undefined. for (var x = 0; x < v_length; x++) { - v1[x] = -1; - v2[x] = -1; + v1[x] = -1 + v2[x] = -1 } - v1[v_offset + 1] = 0; - v2[v_offset + 1] = 0; - var delta = text1_length - text2_length; + v1[v_offset + 1] = 0 + v2[v_offset + 1] = 0 + var delta = text1_length - text2_length // If the total number of characters is odd, then the front path will collide // with the reverse path. - var front = (delta % 2 != 0); + var front = delta % 2 != 0 // Offsets for start and end of k loop. // Prevents mapping of space beyond the grid. - var k1start = 0; - var k1end = 0; - var k2start = 0; - var k2end = 0; + var k1start = 0 + var k1end = 0 + var k2start = 0 + var k2end = 0 for (var d = 0; d < max_d; d++) { // Bail out if deadline is reached. - if ((new Date()).getTime() > deadline) { - break; + if (new Date().getTime() > deadline) { + break } // Walk the front path one step. for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { - var k1_offset = v_offset + k1; - var x1; + var k1_offset = v_offset + k1 + var x1 if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { - x1 = v1[k1_offset + 1]; + x1 = v1[k1_offset + 1] } else { - x1 = v1[k1_offset - 1] + 1; + x1 = v1[k1_offset - 1] + 1 } - var y1 = x1 - k1; - while (x1 < text1_length && y1 < text2_length && - text1.charAt(x1) == text2.charAt(y1)) { - x1++; - y1++; + var y1 = x1 - k1 + while ( + x1 < text1_length && + y1 < text2_length && + text1.charAt(x1) == text2.charAt(y1) + ) { + x1++ + y1++ } - v1[k1_offset] = x1; + v1[k1_offset] = x1 if (x1 > text1_length) { // Ran off the right of the graph. - k1end += 2; + k1end += 2 } else if (y1 > text2_length) { // Ran off the bottom of the graph. - k1start += 2; + k1start += 2 } else if (front) { - var k2_offset = v_offset + delta - k1; + var k2_offset = v_offset + delta - k1 if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { // Mirror x2 onto top-left coordinate system. - var x2 = text1_length - v2[k2_offset]; + var x2 = text1_length - v2[k2_offset] if (x1 >= x2) { // Overlap detected. - return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline) } } } @@ -361,37 +372,40 @@ diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) { // Walk the reverse path one step. for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { - var k2_offset = v_offset + k2; - var x2; + var k2_offset = v_offset + k2 + var x2 if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { - x2 = v2[k2_offset + 1]; + x2 = v2[k2_offset + 1] } else { - x2 = v2[k2_offset - 1] + 1; + x2 = v2[k2_offset - 1] + 1 } - var y2 = x2 - k2; - while (x2 < text1_length && y2 < text2_length && - text1.charAt(text1_length - x2 - 1) == - text2.charAt(text2_length - y2 - 1)) { - x2++; - y2++; + var y2 = x2 - k2 + while ( + x2 < text1_length && + y2 < text2_length && + text1.charAt(text1_length - x2 - 1) == + text2.charAt(text2_length - y2 - 1) + ) { + x2++ + y2++ } - v2[k2_offset] = x2; + v2[k2_offset] = x2 if (x2 > text1_length) { // Ran off the left of the graph. - k2end += 2; + k2end += 2 } else if (y2 > text2_length) { // Ran off the top of the graph. - k2start += 2; + k2start += 2 } else if (!front) { - var k1_offset = v_offset + delta - k2; + var k1_offset = v_offset + delta - k2 if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { - var x1 = v1[k1_offset]; - var y1 = v_offset + x1 - k1_offset; + var x1 = v1[k1_offset] + var y1 = v_offset + x1 - k1_offset // Mirror x2 onto top-left coordinate system. - x2 = text1_length - x2; + x2 = text1_length - x2 if (x1 >= x2) { // Overlap detected. - return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline) } } } @@ -399,9 +413,11 @@ diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) { } // Diff took too long and hit the deadline or // number of diffs equals number of characters, no commonality at all. - return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; -}; - + return [ + [DIFF_DELETE, text1], + [DIFF_INSERT, text2] + ] +} /** * Given the location of the 'middle snake', split the diff in two parts @@ -414,20 +430,24 @@ diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) { * @return {!Array.} Array of diff tuples. * @private */ -diff_match_patch.prototype.diff_bisectSplit_ = function(text1, text2, x, y, - deadline) { - var text1a = text1.substring(0, x); - var text2a = text2.substring(0, y); - var text1b = text1.substring(x); - var text2b = text2.substring(y); +diff_match_patch.prototype.diff_bisectSplit_ = function ( + text1, + text2, + x, + y, + deadline +) { + var text1a = text1.substring(0, x) + var text2a = text2.substring(0, y) + var text1b = text1.substring(x) + var text2b = text2.substring(y) // Compute both diffs serially. - var diffs = this.diff_main(text1a, text2a, false, deadline); - var diffsb = this.diff_main(text1b, text2b, false, deadline); - - return diffs.concat(diffsb); -}; + var diffs = this.diff_main(text1a, text2a, false, deadline) + var diffsb = this.diff_main(text1b, text2b, false, deadline) + return diffs.concat(diffsb) +} /** * Split two texts into an array of strings. Reduce the texts to a string of @@ -440,13 +460,13 @@ diff_match_patch.prototype.diff_bisectSplit_ = function(text1, text2, x, y, * The zeroth element of the array of unique strings is intentionally blank. * @private */ -diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) { - var lineArray = []; // e.g. lineArray[4] == 'Hello\n' - var lineHash = {}; // e.g. lineHash['Hello\n'] == 4 +diff_match_patch.prototype.diff_linesToChars_ = function (text1, text2) { + var lineArray = [] // e.g. lineArray[4] == 'Hello\n' + var lineHash = {} // e.g. lineHash['Hello\n'] == 4 // '\x00' is a valid character, but various debuggers don't like it. // So we'll insert a junk entry to avoid generating a null character. - lineArray[0] = ''; + lineArray[0] = '' /** * Split a text into an array of strings. Reduce the texts to a string of @@ -457,39 +477,41 @@ diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) { * @private */ function diff_linesToCharsMunge_(text) { - var chars = ''; + var chars = '' // Walk the text, pulling out a substring for each line. // text.split('\n') would would temporarily double our memory footprint. // Modifying text would create many large strings to garbage collect. - var lineStart = 0; - var lineEnd = -1; + var lineStart = 0 + var lineEnd = -1 // Keeping our own length variable is faster than looking it up. - var lineArrayLength = lineArray.length; + var lineArrayLength = lineArray.length while (lineEnd < text.length - 1) { - lineEnd = text.indexOf('\n', lineStart); + lineEnd = text.indexOf('\n', lineStart) if (lineEnd == -1) { - lineEnd = text.length - 1; + lineEnd = text.length - 1 } - var line = text.substring(lineStart, lineEnd + 1); - lineStart = lineEnd + 1; + var line = text.substring(lineStart, lineEnd + 1) + lineStart = lineEnd + 1 - if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : - (lineHash[line] !== undefined)) { - chars += String.fromCharCode(lineHash[line]); + if ( + lineHash.hasOwnProperty + ? lineHash.hasOwnProperty(line) + : lineHash[line] !== undefined + ) { + chars += String.fromCharCode(lineHash[line]) } else { - chars += String.fromCharCode(lineArrayLength); - lineHash[line] = lineArrayLength; - lineArray[lineArrayLength++] = line; + chars += String.fromCharCode(lineArrayLength) + lineHash[line] = lineArrayLength + lineArray[lineArrayLength++] = line } } - return chars; + return chars } - var chars1 = diff_linesToCharsMunge_(text1); - var chars2 = diff_linesToCharsMunge_(text2); - return {chars1: chars1, chars2: chars2, lineArray: lineArray}; -}; - + var chars1 = diff_linesToCharsMunge_(text1) + var chars2 = diff_linesToCharsMunge_(text2) + return { chars1: chars1, chars2: chars2, lineArray: lineArray } +} /** * Rehydrate the text in a diff from a string of line hashes to real lines of @@ -498,17 +520,16 @@ diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) { * @param {!Array.} lineArray Array of unique strings. * @private */ -diff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) { +diff_match_patch.prototype.diff_charsToLines_ = function (diffs, lineArray) { for (var x = 0; x < diffs.length; x++) { - var chars = diffs[x][1]; - var text = []; + var chars = diffs[x][1] + var text = [] for (var y = 0; y < chars.length; y++) { - text[y] = lineArray[chars.charCodeAt(y)]; + text[y] = lineArray[chars.charCodeAt(y)] } - diffs[x][1] = text.join(''); + diffs[x][1] = text.join('') } -}; - +} /** * Determine the common prefix of two strings. @@ -517,30 +538,31 @@ diff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) { * @return {number} The number of characters common to the start of each * string. */ -diff_match_patch.prototype.diff_commonPrefix = function(text1, text2) { +diff_match_patch.prototype.diff_commonPrefix = function (text1, text2) { // Quick check for common null cases. if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { - return 0; + return 0 } // Binary search. // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - var pointermin = 0; - var pointermax = Math.min(text1.length, text2.length); - var pointermid = pointermax; - var pointerstart = 0; + var pointermin = 0 + var pointermax = Math.min(text1.length, text2.length) + var pointermid = pointermax + var pointerstart = 0 while (pointermin < pointermid) { - if (text1.substring(pointerstart, pointermid) == - text2.substring(pointerstart, pointermid)) { - pointermin = pointermid; - pointerstart = pointermin; + if ( + text1.substring(pointerstart, pointermid) == + text2.substring(pointerstart, pointermid) + ) { + pointermin = pointermid + pointerstart = pointermin } else { - pointermax = pointermid; + pointermax = pointermid } - pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin) } - return pointermid; -}; - + return pointermid +} /** * Determine the common suffix of two strings. @@ -548,31 +570,35 @@ diff_match_patch.prototype.diff_commonPrefix = function(text1, text2) { * @param {string} text2 Second string. * @return {number} The number of characters common to the end of each string. */ -diff_match_patch.prototype.diff_commonSuffix = function(text1, text2) { +diff_match_patch.prototype.diff_commonSuffix = function (text1, text2) { // Quick check for common null cases. - if (!text1 || !text2 || - text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { - return 0; + if ( + !text1 || + !text2 || + text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1) + ) { + return 0 } // Binary search. // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - var pointermin = 0; - var pointermax = Math.min(text1.length, text2.length); - var pointermid = pointermax; - var pointerend = 0; + var pointermin = 0 + var pointermax = Math.min(text1.length, text2.length) + var pointermid = pointermax + var pointerend = 0 while (pointermin < pointermid) { - if (text1.substring(text1.length - pointermid, text1.length - pointerend) == - text2.substring(text2.length - pointermid, text2.length - pointerend)) { - pointermin = pointermid; - pointerend = pointermin; + if ( + text1.substring(text1.length - pointermid, text1.length - pointerend) == + text2.substring(text2.length - pointermid, text2.length - pointerend) + ) { + pointermin = pointermid + pointerend = pointermin } else { - pointermax = pointermid; + pointermax = pointermid } - pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin) } - return pointermid; -}; - + return pointermid +} /** * Determine if the suffix of one string is the prefix of another. @@ -582,46 +608,47 @@ diff_match_patch.prototype.diff_commonSuffix = function(text1, text2) { * string and the start of the second string. * @private */ -diff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) { +diff_match_patch.prototype.diff_commonOverlap_ = function (text1, text2) { // Cache the text lengths to prevent multiple calls. - var text1_length = text1.length; - var text2_length = text2.length; + var text1_length = text1.length + var text2_length = text2.length // Eliminate the null case. if (text1_length == 0 || text2_length == 0) { - return 0; + return 0 } // Truncate the longer string. if (text1_length > text2_length) { - text1 = text1.substring(text1_length - text2_length); + text1 = text1.substring(text1_length - text2_length) } else if (text1_length < text2_length) { - text2 = text2.substring(0, text1_length); + text2 = text2.substring(0, text1_length) } - var text_length = Math.min(text1_length, text2_length); + var text_length = Math.min(text1_length, text2_length) // Quick check for the worst case. if (text1 == text2) { - return text_length; + return text_length } // Start by looking for a single character match // and increase length until no match is found. // Performance analysis: http://neil.fraser.name/news/2010/11/04/ - var best = 0; - var length = 1; + var best = 0 + var length = 1 while (true) { - var pattern = text1.substring(text_length - length); - var found = text2.indexOf(pattern); + var pattern = text1.substring(text_length - length) + var found = text2.indexOf(pattern) if (found == -1) { - return best; + return best } - length += found; - if (found == 0 || text1.substring(text_length - length) == - text2.substring(0, length)) { - best = length; - length++; + length += found + if ( + found == 0 || + text1.substring(text_length - length) == text2.substring(0, length) + ) { + best = length + length++ } } -}; - +} /** * Do the two texts share a substring which is at least half the length of the @@ -634,17 +661,17 @@ diff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) { * text2 and the common middle. Or null if there was no match. * @private */ -diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) { +diff_match_patch.prototype.diff_halfMatch_ = function (text1, text2) { if (this.Diff_Timeout <= 0) { // Don't risk returning a non-optimal diff if we have unlimited time. - return null; + return null } - var longtext = text1.length > text2.length ? text1 : text2; - var shorttext = text1.length > text2.length ? text2 : text1; + var longtext = text1.length > text2.length ? text1 : text2 + var shorttext = text1.length > text2.length ? text2 : text1 if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { - return null; // Pointless. + return null // Pointless. } - var dmp = this; // 'this' becomes 'window' in a closure. + var dmp = this // 'this' becomes 'window' in a closure. /** * Does a substring of shorttext exist within longtext such that the substring @@ -660,132 +687,153 @@ diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) { */ function diff_halfMatchI_(longtext, shorttext, i) { // Start with a 1/4 length substring at position i as a seed. - var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); - var j = -1; - var best_common = ''; - var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)) + var j = -1 + var best_common = '' + var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b while ((j = shorttext.indexOf(seed, j + 1)) != -1) { - var prefixLength = dmp.diff_commonPrefix(longtext.substring(i), - shorttext.substring(j)); - var suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i), - shorttext.substring(0, j)); + var prefixLength = dmp.diff_commonPrefix( + longtext.substring(i), + shorttext.substring(j) + ) + var suffixLength = dmp.diff_commonSuffix( + longtext.substring(0, i), + shorttext.substring(0, j) + ) if (best_common.length < suffixLength + prefixLength) { - best_common = shorttext.substring(j - suffixLength, j) + - shorttext.substring(j, j + prefixLength); - best_longtext_a = longtext.substring(0, i - suffixLength); - best_longtext_b = longtext.substring(i + prefixLength); - best_shorttext_a = shorttext.substring(0, j - suffixLength); - best_shorttext_b = shorttext.substring(j + prefixLength); + best_common = + shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength) + best_longtext_a = longtext.substring(0, i - suffixLength) + best_longtext_b = longtext.substring(i + prefixLength) + best_shorttext_a = shorttext.substring(0, j - suffixLength) + best_shorttext_b = shorttext.substring(j + prefixLength) } } if (best_common.length * 2 >= longtext.length) { - return [best_longtext_a, best_longtext_b, - best_shorttext_a, best_shorttext_b, best_common]; + return [ + best_longtext_a, + best_longtext_b, + best_shorttext_a, + best_shorttext_b, + best_common + ] } else { - return null; + return null } } // First check if the second quarter is the seed for a half-match. - var hm1 = diff_halfMatchI_(longtext, shorttext, - Math.ceil(longtext.length / 4)); + var hm1 = diff_halfMatchI_( + longtext, + shorttext, + Math.ceil(longtext.length / 4) + ) // Check again based on the third quarter. - var hm2 = diff_halfMatchI_(longtext, shorttext, - Math.ceil(longtext.length / 2)); - var hm; + var hm2 = diff_halfMatchI_( + longtext, + shorttext, + Math.ceil(longtext.length / 2) + ) + var hm if (!hm1 && !hm2) { - return null; + return null } else if (!hm2) { - hm = hm1; + hm = hm1 } else if (!hm1) { - hm = hm2; + hm = hm2 } else { // Both matched. Select the longest. - hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + hm = hm1[4].length > hm2[4].length ? hm1 : hm2 } // A half-match was found, sort out the return data. - var text1_a, text1_b, text2_a, text2_b; + var text1_a, text1_b, text2_a, text2_b if (text1.length > text2.length) { - text1_a = hm[0]; - text1_b = hm[1]; - text2_a = hm[2]; - text2_b = hm[3]; + text1_a = hm[0] + text1_b = hm[1] + text2_a = hm[2] + text2_b = hm[3] } else { - text2_a = hm[0]; - text2_b = hm[1]; - text1_a = hm[2]; - text1_b = hm[3]; + text2_a = hm[0] + text2_b = hm[1] + text1_a = hm[2] + text1_b = hm[3] } - var mid_common = hm[4]; - return [text1_a, text1_b, text2_a, text2_b, mid_common]; -}; - + var mid_common = hm[4] + return [text1_a, text1_b, text2_a, text2_b, mid_common] +} /** * Reduce the number of edits by eliminating semantically trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ -diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) { - var changes = false; - var equalities = []; // Stack of indices where equalities are found. - var equalitiesLength = 0; // Keeping our own length var is faster in JS. +diff_match_patch.prototype.diff_cleanupSemantic = function (diffs) { + var changes = false + var equalities = [] // Stack of indices where equalities are found. + var equalitiesLength = 0 // Keeping our own length var is faster in JS. /** @type {?string} */ - var lastequality = null; + var lastequality = null // Always equal to diffs[equalities[equalitiesLength - 1]][1] - var pointer = 0; // Index of current position. + var pointer = 0 // Index of current position. // Number of characters that changed prior to the equality. - var length_insertions1 = 0; - var length_deletions1 = 0; + var length_insertions1 = 0 + var length_deletions1 = 0 // Number of characters that changed after the equality. - var length_insertions2 = 0; - var length_deletions2 = 0; + var length_insertions2 = 0 + var length_deletions2 = 0 while (pointer < diffs.length) { - if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found. - equalities[equalitiesLength++] = pointer; - length_insertions1 = length_insertions2; - length_deletions1 = length_deletions2; - length_insertions2 = 0; - length_deletions2 = 0; - lastequality = diffs[pointer][1]; - } else { // An insertion or deletion. + if (diffs[pointer][0] == DIFF_EQUAL) { + // Equality found. + equalities[equalitiesLength++] = pointer + length_insertions1 = length_insertions2 + length_deletions1 = length_deletions2 + length_insertions2 = 0 + length_deletions2 = 0 + lastequality = diffs[pointer][1] + } else { + // An insertion or deletion. if (diffs[pointer][0] == DIFF_INSERT) { - length_insertions2 += diffs[pointer][1].length; + length_insertions2 += diffs[pointer][1].length } else { - length_deletions2 += diffs[pointer][1].length; + length_deletions2 += diffs[pointer][1].length } // Eliminate an equality that is smaller or equal to the edits on both // sides of it. - if (lastequality && (lastequality.length <= - Math.max(length_insertions1, length_deletions1)) && - (lastequality.length <= Math.max(length_insertions2, - length_deletions2))) { + if ( + lastequality && + lastequality.length <= + Math.max(length_insertions1, length_deletions1) && + lastequality.length <= Math.max(length_insertions2, length_deletions2) + ) { // Duplicate record. - diffs.splice(equalities[equalitiesLength - 1], 0, - [DIFF_DELETE, lastequality]); + diffs.splice(equalities[equalitiesLength - 1], 0, [ + DIFF_DELETE, + lastequality + ]) // Change second copy to insert. - diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT // Throw away the equality we just deleted. - equalitiesLength--; + equalitiesLength-- // Throw away the previous equality (it needs to be reevaluated). - equalitiesLength--; - pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; - length_insertions1 = 0; // Reset the counters. - length_deletions1 = 0; - length_insertions2 = 0; - length_deletions2 = 0; - lastequality = null; - changes = true; + equalitiesLength-- + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1 + length_insertions1 = 0 // Reset the counters. + length_deletions1 = 0 + length_insertions2 = 0 + length_deletions2 = 0 + lastequality = null + changes = true } } - pointer++; + pointer++ } // Normalize the diff. if (changes) { - this.diff_cleanupMerge(diffs); + this.diff_cleanupMerge(diffs) } - this.diff_cleanupSemanticLossless(diffs); + this.diff_cleanupSemanticLossless(diffs) // Find any overlaps between deletions and insertions. // e.g: abcxxxxxxdef @@ -793,47 +841,59 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) { // e.g: xxxabcdefxxx // -> defxxxabc // Only extract an overlap if it is as big as the edit ahead or behind it. - pointer = 1; + pointer = 1 while (pointer < diffs.length) { - if (diffs[pointer - 1][0] == DIFF_DELETE && - diffs[pointer][0] == DIFF_INSERT) { - var deletion = diffs[pointer - 1][1]; - var insertion = diffs[pointer][1]; - var overlap_length1 = this.diff_commonOverlap_(deletion, insertion); - var overlap_length2 = this.diff_commonOverlap_(insertion, deletion); + if ( + diffs[pointer - 1][0] == DIFF_DELETE && + diffs[pointer][0] == DIFF_INSERT + ) { + var deletion = diffs[pointer - 1][1] + var insertion = diffs[pointer][1] + var overlap_length1 = this.diff_commonOverlap_(deletion, insertion) + var overlap_length2 = this.diff_commonOverlap_(insertion, deletion) if (overlap_length1 >= overlap_length2) { - if (overlap_length1 >= deletion.length / 2 || - overlap_length1 >= insertion.length / 2) { + if ( + overlap_length1 >= deletion.length / 2 || + overlap_length1 >= insertion.length / 2 + ) { // Overlap found. Insert an equality and trim the surrounding edits. - diffs.splice(pointer, 0, - [DIFF_EQUAL, insertion.substring(0, overlap_length1)]); - diffs[pointer - 1][1] = - deletion.substring(0, deletion.length - overlap_length1); - diffs[pointer + 1][1] = insertion.substring(overlap_length1); - pointer++; + diffs.splice(pointer, 0, [ + DIFF_EQUAL, + insertion.substring(0, overlap_length1) + ]) + diffs[pointer - 1][1] = deletion.substring( + 0, + deletion.length - overlap_length1 + ) + diffs[pointer + 1][1] = insertion.substring(overlap_length1) + pointer++ } } else { - if (overlap_length2 >= deletion.length / 2 || - overlap_length2 >= insertion.length / 2) { + if ( + overlap_length2 >= deletion.length / 2 || + overlap_length2 >= insertion.length / 2 + ) { // Reverse overlap found. // Insert an equality and swap and trim the surrounding edits. - diffs.splice(pointer, 0, - [DIFF_EQUAL, deletion.substring(0, overlap_length2)]); - diffs[pointer - 1][0] = DIFF_INSERT; - diffs[pointer - 1][1] = - insertion.substring(0, insertion.length - overlap_length2); - diffs[pointer + 1][0] = DIFF_DELETE; - diffs[pointer + 1][1] = - deletion.substring(overlap_length2); - pointer++; + diffs.splice(pointer, 0, [ + DIFF_EQUAL, + deletion.substring(0, overlap_length2) + ]) + diffs[pointer - 1][0] = DIFF_INSERT + diffs[pointer - 1][1] = insertion.substring( + 0, + insertion.length - overlap_length2 + ) + diffs[pointer + 1][0] = DIFF_DELETE + diffs[pointer + 1][1] = deletion.substring(overlap_length2) + pointer++ } } - pointer++; + pointer++ } - pointer++; + pointer++ } -}; - +} /** * Look for single edits surrounded on both sides by equalities @@ -841,7 +901,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) { * e.g: The cat came. -> The cat came. * @param {!Array.} diffs Array of diff tuples. */ -diff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) { +diff_match_patch.prototype.diff_cleanupSemanticLossless = function (diffs) { /** * Given two strings, compute a score representing whether the internal * boundary falls on logical boundaries. @@ -855,7 +915,7 @@ diff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) { function diff_cleanupSemanticScore_(one, two) { if (!one || !two) { // Edges are the best. - return 6; + return 6 } // Each port of this function behaves slightly differently due to @@ -863,150 +923,158 @@ diff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) { // 'whitespace'. Since this function's purpose is largely cosmetic, // the choice has been made to use each language's native features // rather than force total conformity. - var char1 = one.charAt(one.length - 1); - var char2 = two.charAt(0); - var nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_); - var nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_); - var whitespace1 = nonAlphaNumeric1 && - char1.match(diff_match_patch.whitespaceRegex_); - var whitespace2 = nonAlphaNumeric2 && - char2.match(diff_match_patch.whitespaceRegex_); - var lineBreak1 = whitespace1 && - char1.match(diff_match_patch.linebreakRegex_); - var lineBreak2 = whitespace2 && - char2.match(diff_match_patch.linebreakRegex_); - var blankLine1 = lineBreak1 && - one.match(diff_match_patch.blanklineEndRegex_); - var blankLine2 = lineBreak2 && - two.match(diff_match_patch.blanklineStartRegex_); + var char1 = one.charAt(one.length - 1) + var char2 = two.charAt(0) + var nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_) + var nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_) + var whitespace1 = + nonAlphaNumeric1 && char1.match(diff_match_patch.whitespaceRegex_) + var whitespace2 = + nonAlphaNumeric2 && char2.match(diff_match_patch.whitespaceRegex_) + var lineBreak1 = + whitespace1 && char1.match(diff_match_patch.linebreakRegex_) + var lineBreak2 = + whitespace2 && char2.match(diff_match_patch.linebreakRegex_) + var blankLine1 = + lineBreak1 && one.match(diff_match_patch.blanklineEndRegex_) + var blankLine2 = + lineBreak2 && two.match(diff_match_patch.blanklineStartRegex_) if (blankLine1 || blankLine2) { // Five points for blank lines. - return 5; + return 5 } else if (lineBreak1 || lineBreak2) { // Four points for line breaks. - return 4; + return 4 } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { // Three points for end of sentences. - return 3; + return 3 } else if (whitespace1 || whitespace2) { // Two points for whitespace. - return 2; + return 2 } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { // One point for non-alphanumeric. - return 1; + return 1 } - return 0; + return 0 } - var pointer = 1; + var pointer = 1 // Intentionally ignore the first and last element (don't need checking). while (pointer < diffs.length - 1) { - if (diffs[pointer - 1][0] == DIFF_EQUAL && - diffs[pointer + 1][0] == DIFF_EQUAL) { + if ( + diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL + ) { // This is a single edit surrounded by equalities. - var equality1 = diffs[pointer - 1][1]; - var edit = diffs[pointer][1]; - var equality2 = diffs[pointer + 1][1]; + var equality1 = diffs[pointer - 1][1] + var edit = diffs[pointer][1] + var equality2 = diffs[pointer + 1][1] // First, shift the edit as far left as possible. - var commonOffset = this.diff_commonSuffix(equality1, edit); + var commonOffset = this.diff_commonSuffix(equality1, edit) if (commonOffset) { - var commonString = edit.substring(edit.length - commonOffset); - equality1 = equality1.substring(0, equality1.length - commonOffset); - edit = commonString + edit.substring(0, edit.length - commonOffset); - equality2 = commonString + equality2; + var commonString = edit.substring(edit.length - commonOffset) + equality1 = equality1.substring(0, equality1.length - commonOffset) + edit = commonString + edit.substring(0, edit.length - commonOffset) + equality2 = commonString + equality2 } // Second, step character by character right, looking for the best fit. - var bestEquality1 = equality1; - var bestEdit = edit; - var bestEquality2 = equality2; - var bestScore = diff_cleanupSemanticScore_(equality1, edit) + - diff_cleanupSemanticScore_(edit, equality2); + var bestEquality1 = equality1 + var bestEdit = edit + var bestEquality2 = equality2 + var bestScore = + diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2) while (edit.charAt(0) === equality2.charAt(0)) { - equality1 += edit.charAt(0); - edit = edit.substring(1) + equality2.charAt(0); - equality2 = equality2.substring(1); - var score = diff_cleanupSemanticScore_(equality1, edit) + - diff_cleanupSemanticScore_(edit, equality2); + equality1 += edit.charAt(0) + edit = edit.substring(1) + equality2.charAt(0) + equality2 = equality2.substring(1) + var score = + diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2) // The >= encourages trailing rather than leading whitespace on edits. if (score >= bestScore) { - bestScore = score; - bestEquality1 = equality1; - bestEdit = edit; - bestEquality2 = equality2; + bestScore = score + bestEquality1 = equality1 + bestEdit = edit + bestEquality2 = equality2 } } if (diffs[pointer - 1][1] != bestEquality1) { // We have an improvement, save it back to the diff. if (bestEquality1) { - diffs[pointer - 1][1] = bestEquality1; + diffs[pointer - 1][1] = bestEquality1 } else { - diffs.splice(pointer - 1, 1); - pointer--; + diffs.splice(pointer - 1, 1) + pointer-- } - diffs[pointer][1] = bestEdit; + diffs[pointer][1] = bestEdit if (bestEquality2) { - diffs[pointer + 1][1] = bestEquality2; + diffs[pointer + 1][1] = bestEquality2 } else { - diffs.splice(pointer + 1, 1); - pointer--; + diffs.splice(pointer + 1, 1) + pointer-- } } } - pointer++; + pointer++ } -}; +} // Define some regex patterns for matching boundaries. -diff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/; -diff_match_patch.whitespaceRegex_ = /\s/; -diff_match_patch.linebreakRegex_ = /[\r\n]/; -diff_match_patch.blanklineEndRegex_ = /\n\r?\n$/; -diff_match_patch.blanklineStartRegex_ = /^\r?\n\r?\n/; +diff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/ +diff_match_patch.whitespaceRegex_ = /\s/ +diff_match_patch.linebreakRegex_ = /[\r\n]/ +diff_match_patch.blanklineEndRegex_ = /\n\r?\n$/ +diff_match_patch.blanklineStartRegex_ = /^\r?\n\r?\n/ /** * Reduce the number of edits by eliminating operationally trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ -diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) { - var changes = false; - var equalities = []; // Stack of indices where equalities are found. - var equalitiesLength = 0; // Keeping our own length var is faster in JS. +diff_match_patch.prototype.diff_cleanupEfficiency = function (diffs) { + var changes = false + var equalities = [] // Stack of indices where equalities are found. + var equalitiesLength = 0 // Keeping our own length var is faster in JS. /** @type {?string} */ - var lastequality = null; + var lastequality = null // Always equal to diffs[equalities[equalitiesLength - 1]][1] - var pointer = 0; // Index of current position. + var pointer = 0 // Index of current position. // Is there an insertion operation before the last equality. - var pre_ins = false; + var pre_ins = false // Is there a deletion operation before the last equality. - var pre_del = false; + var pre_del = false // Is there an insertion operation after the last equality. - var post_ins = false; + var post_ins = false // Is there a deletion operation after the last equality. - var post_del = false; + var post_del = false while (pointer < diffs.length) { - if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found. - if (diffs[pointer][1].length < this.Diff_EditCost && - (post_ins || post_del)) { + if (diffs[pointer][0] == DIFF_EQUAL) { + // Equality found. + if ( + diffs[pointer][1].length < this.Diff_EditCost && + (post_ins || post_del) + ) { // Candidate found. - equalities[equalitiesLength++] = pointer; - pre_ins = post_ins; - pre_del = post_del; - lastequality = diffs[pointer][1]; + equalities[equalitiesLength++] = pointer + pre_ins = post_ins + pre_del = post_del + lastequality = diffs[pointer][1] } else { // Not a candidate, and can never become one. - equalitiesLength = 0; - lastequality = null; + equalitiesLength = 0 + lastequality = null } - post_ins = post_del = false; - } else { // An insertion or deletion. + post_ins = post_del = false + } else { + // An insertion or deletion. if (diffs[pointer][0] == DIFF_DELETE) { - post_del = true; + post_del = true } else { - post_ins = true; + post_ins = true } /* * Five types to be split: @@ -1016,164 +1084,198 @@ diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) { * AXCD * ABXC */ - if (lastequality && ((pre_ins && pre_del && post_ins && post_del) || - ((lastequality.length < this.Diff_EditCost / 2) && - (pre_ins + pre_del + post_ins + post_del) == 3))) { + if ( + lastequality && + ((pre_ins && pre_del && post_ins && post_del) || + (lastequality.length < this.Diff_EditCost / 2 && + pre_ins + pre_del + post_ins + post_del == 3)) + ) { // Duplicate record. - diffs.splice(equalities[equalitiesLength - 1], 0, - [DIFF_DELETE, lastequality]); + diffs.splice(equalities[equalitiesLength - 1], 0, [ + DIFF_DELETE, + lastequality + ]) // Change second copy to insert. - diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; - equalitiesLength--; // Throw away the equality we just deleted; - lastequality = null; + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT + equalitiesLength-- // Throw away the equality we just deleted; + lastequality = null if (pre_ins && pre_del) { // No changes made which could affect previous entry, keep going. - post_ins = post_del = true; - equalitiesLength = 0; + post_ins = post_del = true + equalitiesLength = 0 } else { - equalitiesLength--; // Throw away the previous equality. - pointer = equalitiesLength > 0 ? - equalities[equalitiesLength - 1] : -1; - post_ins = post_del = false; + equalitiesLength-- // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1 + post_ins = post_del = false } - changes = true; + changes = true } } - pointer++; + pointer++ } if (changes) { - this.diff_cleanupMerge(diffs); + this.diff_cleanupMerge(diffs) } -}; - +} /** * Reorder and merge like edit sections. Merge equalities. * Any edit section can move as long as it doesn't cross an equality. * @param {!Array.} diffs Array of diff tuples. */ -diff_match_patch.prototype.diff_cleanupMerge = function(diffs) { - diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. - var pointer = 0; - var count_delete = 0; - var count_insert = 0; - var text_delete = ''; - var text_insert = ''; - var commonlength; +diff_match_patch.prototype.diff_cleanupMerge = function (diffs) { + diffs.push([DIFF_EQUAL, '']) // Add a dummy entry at the end. + var pointer = 0 + var count_delete = 0 + var count_insert = 0 + var text_delete = '' + var text_insert = '' + var commonlength while (pointer < diffs.length) { switch (diffs[pointer][0]) { case DIFF_INSERT: - count_insert++; - text_insert += diffs[pointer][1]; - pointer++; - break; + count_insert++ + text_insert += diffs[pointer][1] + pointer++ + break case DIFF_DELETE: - count_delete++; - text_delete += diffs[pointer][1]; - pointer++; - break; + count_delete++ + text_delete += diffs[pointer][1] + pointer++ + break case DIFF_EQUAL: // Upon reaching an equality, check for prior redundancies. if (count_delete + count_insert > 1) { if (count_delete !== 0 && count_insert !== 0) { // Factor out any common prefixies. - commonlength = this.diff_commonPrefix(text_insert, text_delete); + commonlength = this.diff_commonPrefix(text_insert, text_delete) if (commonlength !== 0) { - if ((pointer - count_delete - count_insert) > 0 && - diffs[pointer - count_delete - count_insert - 1][0] == - DIFF_EQUAL) { - diffs[pointer - count_delete - count_insert - 1][1] += - text_insert.substring(0, commonlength); + if ( + pointer - count_delete - count_insert > 0 && + diffs[pointer - count_delete - count_insert - 1][0] == + DIFF_EQUAL + ) { + diffs[ + pointer - count_delete - count_insert - 1 + ][1] += text_insert.substring(0, commonlength) } else { - diffs.splice(0, 0, [DIFF_EQUAL, - text_insert.substring(0, commonlength)]); - pointer++; + diffs.splice(0, 0, [ + DIFF_EQUAL, + text_insert.substring(0, commonlength) + ]) + pointer++ } - text_insert = text_insert.substring(commonlength); - text_delete = text_delete.substring(commonlength); + text_insert = text_insert.substring(commonlength) + text_delete = text_delete.substring(commonlength) } // Factor out any common suffixies. - commonlength = this.diff_commonSuffix(text_insert, text_delete); + commonlength = this.diff_commonSuffix(text_insert, text_delete) if (commonlength !== 0) { - diffs[pointer][1] = text_insert.substring(text_insert.length - - commonlength) + diffs[pointer][1]; - text_insert = text_insert.substring(0, text_insert.length - - commonlength); - text_delete = text_delete.substring(0, text_delete.length - - commonlength); + diffs[pointer][1] = + text_insert.substring(text_insert.length - commonlength) + + diffs[pointer][1] + text_insert = text_insert.substring( + 0, + text_insert.length - commonlength + ) + text_delete = text_delete.substring( + 0, + text_delete.length - commonlength + ) } } // Delete the offending records and add the merged ones. if (count_delete === 0) { - diffs.splice(pointer - count_insert, - count_delete + count_insert, [DIFF_INSERT, text_insert]); + diffs.splice(pointer - count_insert, count_delete + count_insert, [ + DIFF_INSERT, + text_insert + ]) } else if (count_insert === 0) { - diffs.splice(pointer - count_delete, - count_delete + count_insert, [DIFF_DELETE, text_delete]); + diffs.splice(pointer - count_delete, count_delete + count_insert, [ + DIFF_DELETE, + text_delete + ]) } else { - diffs.splice(pointer - count_delete - count_insert, - count_delete + count_insert, [DIFF_DELETE, text_delete], - [DIFF_INSERT, text_insert]); + diffs.splice( + pointer - count_delete - count_insert, + count_delete + count_insert, + [DIFF_DELETE, text_delete], + [DIFF_INSERT, text_insert] + ) } - pointer = pointer - count_delete - count_insert + - (count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; + pointer = + pointer - + count_delete - + count_insert + + (count_delete ? 1 : 0) + + (count_insert ? 1 : 0) + + 1 } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { // Merge this equality with the previous one. - diffs[pointer - 1][1] += diffs[pointer][1]; - diffs.splice(pointer, 1); + diffs[pointer - 1][1] += diffs[pointer][1] + diffs.splice(pointer, 1) } else { - pointer++; + pointer++ } - count_insert = 0; - count_delete = 0; - text_delete = ''; - text_insert = ''; - break; + count_insert = 0 + count_delete = 0 + text_delete = '' + text_insert = '' + break } } if (diffs[diffs.length - 1][1] === '') { - diffs.pop(); // Remove the dummy entry at the end. + diffs.pop() // Remove the dummy entry at the end. } // Second pass: look for single edits surrounded on both sides by equalities // which can be shifted sideways to eliminate an equality. // e.g: ABAC -> ABAC - var changes = false; - pointer = 1; + var changes = false + pointer = 1 // Intentionally ignore the first and last element (don't need checking). while (pointer < diffs.length - 1) { - if (diffs[pointer - 1][0] == DIFF_EQUAL && - diffs[pointer + 1][0] == DIFF_EQUAL) { + if ( + diffs[pointer - 1][0] == DIFF_EQUAL && + diffs[pointer + 1][0] == DIFF_EQUAL + ) { // This is a single edit surrounded by equalities. - if (diffs[pointer][1].substring(diffs[pointer][1].length - - diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { + if ( + diffs[pointer][1].substring( + diffs[pointer][1].length - diffs[pointer - 1][1].length + ) == diffs[pointer - 1][1] + ) { // Shift the edit over the previous equality. - diffs[pointer][1] = diffs[pointer - 1][1] + - diffs[pointer][1].substring(0, diffs[pointer][1].length - - diffs[pointer - 1][1].length); - diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; - diffs.splice(pointer - 1, 1); - changes = true; - } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == - diffs[pointer + 1][1]) { - // Shift the edit over the next equality. - diffs[pointer - 1][1] += diffs[pointer + 1][1]; diffs[pointer][1] = - diffs[pointer][1].substring(diffs[pointer + 1][1].length) + - diffs[pointer + 1][1]; - diffs.splice(pointer + 1, 1); - changes = true; + diffs[pointer - 1][1] + + diffs[pointer][1].substring( + 0, + diffs[pointer][1].length - diffs[pointer - 1][1].length + ) + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1] + diffs.splice(pointer - 1, 1) + changes = true + } else if ( + diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == + diffs[pointer + 1][1] + ) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1] + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1] + diffs.splice(pointer + 1, 1) + changes = true } } - pointer++; + pointer++ } // If shifts were made, the diff needs reordering and another shift sweep. if (changes) { - this.diff_cleanupMerge(diffs); + this.diff_cleanupMerge(diffs) } -}; - +} /** * loc is a location in text1, compute and return the equivalent location in @@ -1183,97 +1285,99 @@ diff_match_patch.prototype.diff_cleanupMerge = function(diffs) { * @param {number} loc Location within text1. * @return {number} Location within text2. */ -diff_match_patch.prototype.diff_xIndex = function(diffs, loc) { - var chars1 = 0; - var chars2 = 0; - var last_chars1 = 0; - var last_chars2 = 0; - var x; +diff_match_patch.prototype.diff_xIndex = function (diffs, loc) { + var chars1 = 0 + var chars2 = 0 + var last_chars1 = 0 + var last_chars2 = 0 + var x for (x = 0; x < diffs.length; x++) { - if (diffs[x][0] !== DIFF_INSERT) { // Equality or deletion. - chars1 += diffs[x][1].length; + if (diffs[x][0] !== DIFF_INSERT) { + // Equality or deletion. + chars1 += diffs[x][1].length } - if (diffs[x][0] !== DIFF_DELETE) { // Equality or insertion. - chars2 += diffs[x][1].length; + if (diffs[x][0] !== DIFF_DELETE) { + // Equality or insertion. + chars2 += diffs[x][1].length } - if (chars1 > loc) { // Overshot the location. - break; + if (chars1 > loc) { + // Overshot the location. + break } - last_chars1 = chars1; - last_chars2 = chars2; + last_chars1 = chars1 + last_chars2 = chars2 } // Was the location was deleted? if (diffs.length != x && diffs[x][0] === DIFF_DELETE) { - return last_chars2; + return last_chars2 } // Add the remaining character length. - return last_chars2 + (loc - last_chars1); -}; - + return last_chars2 + (loc - last_chars1) +} /** * Convert a diff array into a pretty HTML report. * @param {!Array.} diffs Array of diff tuples. * @return {string} HTML representation. */ -diff_match_patch.prototype.diff_prettyHtml = function(diffs) { - var html = []; - var pattern_amp = /&/g; - var pattern_lt = //g; - var pattern_para = /\n/g; +diff_match_patch.prototype.diff_prettyHtml = function (diffs) { + var html = [] + var pattern_amp = /&/g + var pattern_lt = //g + var pattern_para = /\n/g for (var x = 0; x < diffs.length; x++) { - var op = diffs[x][0]; // Operation (insert, delete, equal) - var data = diffs[x][1]; // Text of change. - var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') - .replace(pattern_gt, '>').replace(pattern_para, '¶
'); + var op = diffs[x][0] // Operation (insert, delete, equal) + var data = diffs[x][1] // Text of change. + var text = data + .replace(pattern_amp, '&') + .replace(pattern_lt, '<') + .replace(pattern_gt, '>') + .replace(pattern_para, '¶
') switch (op) { case DIFF_INSERT: - html[x] = '' + text + ''; - break; + html[x] = '' + text + '' + break case DIFF_DELETE: - html[x] = '' + text + ''; - break; + html[x] = '' + text + '' + break case DIFF_EQUAL: - html[x] = '' + text + ''; - break; + html[x] = '' + text + '' + break } } - return html.join(''); -}; - + return html.join('') +} /** * Compute and return the source text (all equalities and deletions). * @param {!Array.} diffs Array of diff tuples. * @return {string} Source text. */ -diff_match_patch.prototype.diff_text1 = function(diffs) { - var text = []; +diff_match_patch.prototype.diff_text1 = function (diffs) { + var text = [] for (var x = 0; x < diffs.length; x++) { if (diffs[x][0] !== DIFF_INSERT) { - text[x] = diffs[x][1]; + text[x] = diffs[x][1] } } - return text.join(''); -}; - + return text.join('') +} /** * Compute and return the destination text (all equalities and insertions). * @param {!Array.} diffs Array of diff tuples. * @return {string} Destination text. */ -diff_match_patch.prototype.diff_text2 = function(diffs) { - var text = []; +diff_match_patch.prototype.diff_text2 = function (diffs) { + var text = [] for (var x = 0; x < diffs.length; x++) { if (diffs[x][0] !== DIFF_DELETE) { - text[x] = diffs[x][1]; + text[x] = diffs[x][1] } } - return text.join(''); -}; - + return text.join('') +} /** * Compute the Levenshtein distance; the number of inserted, deleted or @@ -1281,32 +1385,31 @@ diff_match_patch.prototype.diff_text2 = function(diffs) { * @param {!Array.} diffs Array of diff tuples. * @return {number} Number of changes. */ -diff_match_patch.prototype.diff_levenshtein = function(diffs) { - var levenshtein = 0; - var insertions = 0; - var deletions = 0; +diff_match_patch.prototype.diff_levenshtein = function (diffs) { + var levenshtein = 0 + var insertions = 0 + var deletions = 0 for (var x = 0; x < diffs.length; x++) { - var op = diffs[x][0]; - var data = diffs[x][1]; + var op = diffs[x][0] + var data = diffs[x][1] switch (op) { case DIFF_INSERT: - insertions += data.length; - break; + insertions += data.length + break case DIFF_DELETE: - deletions += data.length; - break; + deletions += data.length + break case DIFF_EQUAL: // A deletion and an insertion is one substitution. - levenshtein += Math.max(insertions, deletions); - insertions = 0; - deletions = 0; - break; + levenshtein += Math.max(insertions, deletions) + insertions = 0 + deletions = 0 + break } } - levenshtein += Math.max(insertions, deletions); - return levenshtein; -}; - + levenshtein += Math.max(insertions, deletions) + return levenshtein +} /** * Crush the diff into an encoded string which describes the operations @@ -1316,24 +1419,23 @@ diff_match_patch.prototype.diff_levenshtein = function(diffs) { * @param {!Array.} diffs Array of diff tuples. * @return {string} Delta text. */ -diff_match_patch.prototype.diff_toDelta = function(diffs) { - var text = []; +diff_match_patch.prototype.diff_toDelta = function (diffs) { + var text = [] for (var x = 0; x < diffs.length; x++) { switch (diffs[x][0]) { case DIFF_INSERT: - text[x] = '+' + encodeURI(diffs[x][1]); - break; + text[x] = '+' + encodeURI(diffs[x][1]) + break case DIFF_DELETE: - text[x] = '-' + diffs[x][1].length; - break; + text[x] = '-' + diffs[x][1].length + break case DIFF_EQUAL: - text[x] = '=' + diffs[x][1].length; - break; + text[x] = '=' + diffs[x][1].length + break } } - return text.join('\t').replace(/%20/g, ' '); -}; - + return text.join('\t').replace(/%20/g, ' ') +} /** * Given the original text1, and an encoded string which describes the @@ -1343,58 +1445,62 @@ diff_match_patch.prototype.diff_toDelta = function(diffs) { * @return {!Array.} Array of diff tuples. * @throws {!Error} If invalid input. */ -diff_match_patch.prototype.diff_fromDelta = function(text1, delta) { - var diffs = []; - var diffsLength = 0; // Keeping our own length var is faster in JS. - var pointer = 0; // Cursor in text1 - var tokens = delta.split(/\t/g); +diff_match_patch.prototype.diff_fromDelta = function (text1, delta) { + var diffs = [] + var diffsLength = 0 // Keeping our own length var is faster in JS. + var pointer = 0 // Cursor in text1 + var tokens = delta.split(/\t/g) for (var x = 0; x < tokens.length; x++) { // Each token begins with a one character parameter which specifies the // operation of this token (delete, insert, equality). - var param = tokens[x].substring(1); + var param = tokens[x].substring(1) switch (tokens[x].charAt(0)) { case '+': try { - diffs[diffsLength++] = [DIFF_INSERT, decodeURI(param)]; + diffs[diffsLength++] = [DIFF_INSERT, decodeURI(param)] } catch (ex) { // Malformed URI sequence. - throw new Error('Illegal escape in diff_fromDelta: ' + param); + throw new Error('Illegal escape in diff_fromDelta: ' + param) } - break; + break case '-': - // Fall through. + // Fall through. case '=': - var n = parseInt(param, 10); + var n = parseInt(param, 10) if (isNaN(n) || n < 0) { - throw new Error('Invalid number in diff_fromDelta: ' + param); + throw new Error('Invalid number in diff_fromDelta: ' + param) } - var text = text1.substring(pointer, pointer += n); + var text = text1.substring(pointer, (pointer += n)) if (tokens[x].charAt(0) == '=') { - diffs[diffsLength++] = [DIFF_EQUAL, text]; + diffs[diffsLength++] = [DIFF_EQUAL, text] } else { - diffs[diffsLength++] = [DIFF_DELETE, text]; + diffs[diffsLength++] = [DIFF_DELETE, text] } - break; + break default: // Blank tokens are ok (from a trailing \t). // Anything else is an error. if (tokens[x]) { - throw new Error('Invalid diff operation in diff_fromDelta: ' + - tokens[x]); + throw new Error( + 'Invalid diff operation in diff_fromDelta: ' + tokens[x] + ) } } } if (pointer != text1.length) { - throw new Error('Delta length (' + pointer + - ') does not equal source text length (' + text1.length + ').'); + throw new Error( + 'Delta length (' + + pointer + + ') does not equal source text length (' + + text1.length + + ').' + ) } - return diffs; -}; - + return diffs +} // MATCH FUNCTIONS - /** * Locate the best instance of 'pattern' in 'text' near 'loc'. * @param {string} text The text to search. @@ -1402,28 +1508,27 @@ diff_match_patch.prototype.diff_fromDelta = function(text1, delta) { * @param {number} loc The location to search around. * @return {number} Best match index or -1. */ -diff_match_patch.prototype.match_main = function(text, pattern, loc) { +diff_match_patch.prototype.match_main = function (text, pattern, loc) { // Check for null inputs. if (text == null || pattern == null || loc == null) { - throw new Error('Null input. (match_main)'); + throw new Error('Null input. (match_main)') } - loc = Math.max(0, Math.min(loc, text.length)); + loc = Math.max(0, Math.min(loc, text.length)) if (text == pattern) { // Shortcut (potentially not guaranteed by the algorithm) - return 0; + return 0 } else if (!text.length) { // Nothing to match. - return -1; + return -1 } else if (text.substring(loc, loc + pattern.length) == pattern) { // Perfect match at the perfect spot! (Includes case of null pattern) - return loc; + return loc } else { // Do a fuzzy compare. - return this.match_bitap_(text, pattern, loc); + return this.match_bitap_(text, pattern, loc) } -}; - +} /** * Locate the best instance of 'pattern' in 'text' near 'loc' using the @@ -1434,15 +1539,15 @@ diff_match_patch.prototype.match_main = function(text, pattern, loc) { * @return {number} Best match index or -1. * @private */ -diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) { +diff_match_patch.prototype.match_bitap_ = function (text, pattern, loc) { if (pattern.length > this.Match_MaxBits) { - throw new Error('Pattern too long for this browser.'); + throw new Error('Pattern too long for this browser.') } // Initialise the alphabet. - var s = this.match_alphabet_(pattern); + var s = this.match_alphabet_(pattern) - var dmp = this; // 'this' becomes 'window' in a closure. + var dmp = this // 'this' becomes 'window' in a closure. /** * Compute and return the score for a match with e errors and x location. @@ -1453,95 +1558,99 @@ diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) { * @private */ function match_bitapScore_(e, x) { - var accuracy = e / pattern.length; - var proximity = Math.abs(loc - x); + var accuracy = e / pattern.length + var proximity = Math.abs(loc - x) if (!dmp.Match_Distance) { // Dodge divide by zero error. - return proximity ? 1.0 : accuracy; + return proximity ? 1.0 : accuracy } - return accuracy + (proximity / dmp.Match_Distance); + return accuracy + proximity / dmp.Match_Distance } // Highest score beyond which we give up. - var score_threshold = this.Match_Threshold; + var score_threshold = this.Match_Threshold // Is there a nearby exact match? (speedup) - var best_loc = text.indexOf(pattern, loc); + var best_loc = text.indexOf(pattern, loc) if (best_loc != -1) { - score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold); + score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold) // What about in the other direction? (speedup) - best_loc = text.lastIndexOf(pattern, loc + pattern.length); + best_loc = text.lastIndexOf(pattern, loc + pattern.length) if (best_loc != -1) { - score_threshold = - Math.min(match_bitapScore_(0, best_loc), score_threshold); + score_threshold = Math.min( + match_bitapScore_(0, best_loc), + score_threshold + ) } } // Initialise the bit arrays. - var matchmask = 1 << (pattern.length - 1); - best_loc = -1; + var matchmask = 1 << (pattern.length - 1) + best_loc = -1 - var bin_min, bin_mid; - var bin_max = pattern.length + text.length; - var last_rd; + var bin_min, bin_mid + var bin_max = pattern.length + text.length + var last_rd for (var d = 0; d < pattern.length; d++) { // Scan for the best match; each iteration allows for one more error. // Run a binary search to determine how far from 'loc' we can stray at this // error level. - bin_min = 0; - bin_mid = bin_max; + bin_min = 0 + bin_mid = bin_max while (bin_min < bin_mid) { if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) { - bin_min = bin_mid; + bin_min = bin_mid } else { - bin_max = bin_mid; + bin_max = bin_mid } - bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min); + bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min) } // Use the result from this iteration as the maximum for the next. - bin_max = bin_mid; - var start = Math.max(1, loc - bin_mid + 1); - var finish = Math.min(loc + bin_mid, text.length) + pattern.length; + bin_max = bin_mid + var start = Math.max(1, loc - bin_mid + 1) + var finish = Math.min(loc + bin_mid, text.length) + pattern.length - var rd = Array(finish + 2); - rd[finish + 1] = (1 << d) - 1; + var rd = Array(finish + 2) + rd[finish + 1] = (1 << d) - 1 for (var j = finish; j >= start; j--) { // The alphabet (s) is a sparse hash, so the following line generates // warnings. - var charMatch = s[text.charAt(j - 1)]; - if (d === 0) { // First pass: exact match. - rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; - } else { // Subsequent passes: fuzzy match. - rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) | - (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | - last_rd[j + 1]; + var charMatch = s[text.charAt(j - 1)] + if (d === 0) { + // First pass: exact match. + rd[j] = ((rd[j + 1] << 1) | 1) & charMatch + } else { + // Subsequent passes: fuzzy match. + rd[j] = + (((rd[j + 1] << 1) | 1) & charMatch) | + (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | + last_rd[j + 1] } if (rd[j] & matchmask) { - var score = match_bitapScore_(d, j - 1); + var score = match_bitapScore_(d, j - 1) // This match will almost certainly be better than any existing match. // But check anyway. if (score <= score_threshold) { // Told you so. - score_threshold = score; - best_loc = j - 1; + score_threshold = score + best_loc = j - 1 if (best_loc > loc) { // When passing loc, don't exceed our current distance from loc. - start = Math.max(1, 2 * loc - best_loc); + start = Math.max(1, 2 * loc - best_loc) } else { // Already passed loc, downhill from here on in. - break; + break } } } } // No hope for a (better) match at greater error levels. if (match_bitapScore_(d + 1, loc) > score_threshold) { - break; + break } - last_rd = rd; + last_rd = rd } - return best_loc; -}; - + return best_loc +} /** * Initialise the alphabet for the Bitap algorithm. @@ -1549,21 +1658,19 @@ diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) { * @return {!Object} Hash of character locations. * @private */ -diff_match_patch.prototype.match_alphabet_ = function(pattern) { - var s = {}; +diff_match_patch.prototype.match_alphabet_ = function (pattern) { + var s = {} for (var i = 0; i < pattern.length; i++) { - s[pattern.charAt(i)] = 0; + s[pattern.charAt(i)] = 0 } for (var i = 0; i < pattern.length; i++) { - s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1); + s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1) } - return s; -}; - + return s +} // PATCH FUNCTIONS - /** * Increase the context until it is unique, * but don't let the pattern expand beyond Match_MaxBits. @@ -1571,45 +1678,49 @@ diff_match_patch.prototype.match_alphabet_ = function(pattern) { * @param {string} text Source text. * @private */ -diff_match_patch.prototype.patch_addContext_ = function(patch, text) { +diff_match_patch.prototype.patch_addContext_ = function (patch, text) { if (text.length == 0) { - return; + return } - var pattern = text.substring(patch.start2, patch.start2 + patch.length1); - var padding = 0; + var pattern = text.substring(patch.start2, patch.start2 + patch.length1) + var padding = 0 // Look for the first and last matches of pattern in text. If two different // matches are found, increase the pattern length. - while (text.indexOf(pattern) != text.lastIndexOf(pattern) && - pattern.length < this.Match_MaxBits - this.Patch_Margin - - this.Patch_Margin) { - padding += this.Patch_Margin; - pattern = text.substring(patch.start2 - padding, - patch.start2 + patch.length1 + padding); + while ( + text.indexOf(pattern) != text.lastIndexOf(pattern) && + pattern.length < this.Match_MaxBits - this.Patch_Margin - this.Patch_Margin + ) { + padding += this.Patch_Margin + pattern = text.substring( + patch.start2 - padding, + patch.start2 + patch.length1 + padding + ) } // Add one chunk for good luck. - padding += this.Patch_Margin; + padding += this.Patch_Margin // Add the prefix. - var prefix = text.substring(patch.start2 - padding, patch.start2); + var prefix = text.substring(patch.start2 - padding, patch.start2) if (prefix) { - patch.diffs.unshift([DIFF_EQUAL, prefix]); + patch.diffs.unshift([DIFF_EQUAL, prefix]) } // Add the suffix. - var suffix = text.substring(patch.start2 + patch.length1, - patch.start2 + patch.length1 + padding); + var suffix = text.substring( + patch.start2 + patch.length1, + patch.start2 + patch.length1 + padding + ) if (suffix) { - patch.diffs.push([DIFF_EQUAL, suffix]); + patch.diffs.push([DIFF_EQUAL, suffix]) } // Roll back the start points. - patch.start1 -= prefix.length; - patch.start2 -= prefix.length; + patch.start1 -= prefix.length + patch.start2 -= prefix.length // Extend the lengths. - patch.length1 += prefix.length + suffix.length; - patch.length2 += prefix.length + suffix.length; -}; - + patch.length1 += prefix.length + suffix.length + patch.length2 += prefix.length + suffix.length +} /** * Compute a list of patches to turn text1 into text2. @@ -1633,143 +1744,161 @@ diff_match_patch.prototype.patch_addContext_ = function(patch, text) { * for text1 to text2 (method 4) or undefined (methods 1,2,3). * @return {!Array.} Array of Patch objects. */ -diff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) { - var text1, diffs; - if (typeof a == 'string' && typeof opt_b == 'string' && - typeof opt_c == 'undefined') { +diff_match_patch.prototype.patch_make = function (a, opt_b, opt_c) { + var text1, diffs + if ( + typeof a === 'string' && + typeof opt_b === 'string' && + typeof opt_c === 'undefined' + ) { // Method 1: text1, text2 // Compute diffs from text1 and text2. - text1 = /** @type {string} */(a); - diffs = this.diff_main(text1, /** @type {string} */(opt_b), true); + text1 = /** @type {string} */ (a) + diffs = this.diff_main(text1, /** @type {string} */ (opt_b), true) if (diffs.length > 2) { - this.diff_cleanupSemantic(diffs); - this.diff_cleanupEfficiency(diffs); + this.diff_cleanupSemantic(diffs) + this.diff_cleanupEfficiency(diffs) } - } else if (a && typeof a == 'object' && typeof opt_b == 'undefined' && - typeof opt_c == 'undefined') { + } else if ( + a && + typeof a === 'object' && + typeof opt_b === 'undefined' && + typeof opt_c === 'undefined' + ) { // Method 2: diffs // Compute text1 from diffs. - diffs = /** @type {!Array.} */(a); - text1 = this.diff_text1(diffs); - } else if (typeof a == 'string' && opt_b && typeof opt_b == 'object' && - typeof opt_c == 'undefined') { + diffs = /** @type {!Array.} */ (a) + text1 = this.diff_text1(diffs) + } else if ( + typeof a === 'string' && + opt_b && + typeof opt_b === 'object' && + typeof opt_c === 'undefined' + ) { // Method 3: text1, diffs - text1 = /** @type {string} */(a); - diffs = /** @type {!Array.} */(opt_b); - } else if (typeof a == 'string' && typeof opt_b == 'string' && - opt_c && typeof opt_c == 'object') { + text1 = /** @type {string} */ (a) + diffs = /** @type {!Array.} */ (opt_b) + } else if ( + typeof a === 'string' && + typeof opt_b === 'string' && + opt_c && + typeof opt_c === 'object' + ) { // Method 4: text1, text2, diffs // text2 is not used. - text1 = /** @type {string} */(a); - diffs = /** @type {!Array.} */(opt_c); + text1 = /** @type {string} */ (a) + diffs = /** @type {!Array.} */ (opt_c) } else { - throw new Error('Unknown call format to patch_make.'); + throw new Error('Unknown call format to patch_make.') } if (diffs.length === 0) { - return []; // Get rid of the null case. + return [] // Get rid of the null case. } - var patches = []; - var patch = new diff_match_patch.patch_obj(); - var patchDiffLength = 0; // Keeping our own length var is faster in JS. - var char_count1 = 0; // Number of characters into the text1 string. - var char_count2 = 0; // Number of characters into the text2 string. + var patches = [] + var patch = new diff_match_patch.patch_obj() + var patchDiffLength = 0 // Keeping our own length var is faster in JS. + var char_count1 = 0 // Number of characters into the text1 string. + var char_count2 = 0 // Number of characters into the text2 string. // Start with text1 (prepatch_text) and apply the diffs until we arrive at // text2 (postpatch_text). We recreate the patches one by one to determine // context info. - var prepatch_text = text1; - var postpatch_text = text1; + var prepatch_text = text1 + var postpatch_text = text1 for (var x = 0; x < diffs.length; x++) { - var diff_type = diffs[x][0]; - var diff_text = diffs[x][1]; + var diff_type = diffs[x][0] + var diff_text = diffs[x][1] if (!patchDiffLength && diff_type !== DIFF_EQUAL) { // A new patch starts here. - patch.start1 = char_count1; - patch.start2 = char_count2; + patch.start1 = char_count1 + patch.start2 = char_count2 } switch (diff_type) { case DIFF_INSERT: - patch.diffs[patchDiffLength++] = diffs[x]; - patch.length2 += diff_text.length; - postpatch_text = postpatch_text.substring(0, char_count2) + diff_text + - postpatch_text.substring(char_count2); - break; + patch.diffs[patchDiffLength++] = diffs[x] + patch.length2 += diff_text.length + postpatch_text = + postpatch_text.substring(0, char_count2) + + diff_text + + postpatch_text.substring(char_count2) + break case DIFF_DELETE: - patch.length1 += diff_text.length; - patch.diffs[patchDiffLength++] = diffs[x]; - postpatch_text = postpatch_text.substring(0, char_count2) + - postpatch_text.substring(char_count2 + - diff_text.length); - break; + patch.length1 += diff_text.length + patch.diffs[patchDiffLength++] = diffs[x] + postpatch_text = + postpatch_text.substring(0, char_count2) + + postpatch_text.substring(char_count2 + diff_text.length) + break case DIFF_EQUAL: - if (diff_text.length <= 2 * this.Patch_Margin && - patchDiffLength && diffs.length != x + 1) { + if ( + diff_text.length <= 2 * this.Patch_Margin && + patchDiffLength && + diffs.length != x + 1 + ) { // Small equality inside a patch. - patch.diffs[patchDiffLength++] = diffs[x]; - patch.length1 += diff_text.length; - patch.length2 += diff_text.length; + patch.diffs[patchDiffLength++] = diffs[x] + patch.length1 += diff_text.length + patch.length2 += diff_text.length } else if (diff_text.length >= 2 * this.Patch_Margin) { // Time for a new patch. if (patchDiffLength) { - this.patch_addContext_(patch, prepatch_text); - patches.push(patch); - patch = new diff_match_patch.patch_obj(); - patchDiffLength = 0; + this.patch_addContext_(patch, prepatch_text) + patches.push(patch) + patch = new diff_match_patch.patch_obj() + patchDiffLength = 0 // Unlike Unidiff, our patch lists have a rolling context. // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff // Update prepatch text & pos to reflect the application of the // just completed patch. - prepatch_text = postpatch_text; - char_count1 = char_count2; + prepatch_text = postpatch_text + char_count1 = char_count2 } } - break; + break } // Update the current character count. if (diff_type !== DIFF_INSERT) { - char_count1 += diff_text.length; + char_count1 += diff_text.length } if (diff_type !== DIFF_DELETE) { - char_count2 += diff_text.length; + char_count2 += diff_text.length } } // Pick up the leftover patch if not empty. if (patchDiffLength) { - this.patch_addContext_(patch, prepatch_text); - patches.push(patch); + this.patch_addContext_(patch, prepatch_text) + patches.push(patch) } - return patches; -}; - + return patches +} /** * Given an array of patches, return another array that is identical. * @param {!Array.} patches Array of Patch objects. * @return {!Array.} Array of Patch objects. */ -diff_match_patch.prototype.patch_deepCopy = function(patches) { +diff_match_patch.prototype.patch_deepCopy = function (patches) { // Making deep copies is hard in JavaScript. - var patchesCopy = []; + var patchesCopy = [] for (var x = 0; x < patches.length; x++) { - var patch = patches[x]; - var patchCopy = new diff_match_patch.patch_obj(); - patchCopy.diffs = []; + var patch = patches[x] + var patchCopy = new diff_match_patch.patch_obj() + patchCopy.diffs = [] for (var y = 0; y < patch.diffs.length; y++) { - patchCopy.diffs[y] = patch.diffs[y].slice(); + patchCopy.diffs[y] = patch.diffs[y].slice() } - patchCopy.start1 = patch.start1; - patchCopy.start2 = patch.start2; - patchCopy.length1 = patch.length1; - patchCopy.length2 = patch.length2; - patchesCopy[x] = patchCopy; + patchCopy.start1 = patch.start1 + patchCopy.start2 = patch.start2 + patchCopy.length1 = patch.length1 + patchCopy.length2 = patch.length2 + patchesCopy[x] = patchCopy } - return patchesCopy; -}; - + return patchesCopy +} /** * Merge a set of patches onto the text. Return a patched text, as well @@ -1779,94 +1908,108 @@ diff_match_patch.prototype.patch_deepCopy = function(patches) { * @return {!Array.>} Two element Array, containing the * new text and an array of boolean values. */ -diff_match_patch.prototype.patch_apply = function(patches, text) { +diff_match_patch.prototype.patch_apply = function (patches, text) { if (patches.length == 0) { - return [text, []]; + return [text, []] } // Deep copy the patches so that no changes are made to originals. - patches = this.patch_deepCopy(patches); + patches = this.patch_deepCopy(patches) - var nullPadding = this.patch_addPadding(patches); - text = nullPadding + text + nullPadding; + var nullPadding = this.patch_addPadding(patches) + text = nullPadding + text + nullPadding - this.patch_splitMax(patches); + this.patch_splitMax(patches) // delta keeps track of the offset between the expected and actual location // of the previous patch. If there are patches expected at positions 10 and // 20, but the first patch was found at 12, delta is 2 and the second patch // has an effective expected position of 22. - var delta = 0; - var results = []; + var delta = 0 + var results = [] for (var x = 0; x < patches.length; x++) { - var expected_loc = patches[x].start2 + delta; - var text1 = this.diff_text1(patches[x].diffs); - var start_loc; - var end_loc = -1; + var expected_loc = patches[x].start2 + delta + var text1 = this.diff_text1(patches[x].diffs) + var start_loc + var end_loc = -1 if (text1.length > this.Match_MaxBits) { // patch_splitMax will only provide an oversized pattern in the case of // a monster delete. - start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits), - expected_loc); + start_loc = this.match_main( + text, + text1.substring(0, this.Match_MaxBits), + expected_loc + ) if (start_loc != -1) { - end_loc = this.match_main(text, - text1.substring(text1.length - this.Match_MaxBits), - expected_loc + text1.length - this.Match_MaxBits); + end_loc = this.match_main( + text, + text1.substring(text1.length - this.Match_MaxBits), + expected_loc + text1.length - this.Match_MaxBits + ) if (end_loc == -1 || start_loc >= end_loc) { // Can't find valid trailing context. Drop this patch. - start_loc = -1; + start_loc = -1 } } } else { - start_loc = this.match_main(text, text1, expected_loc); + start_loc = this.match_main(text, text1, expected_loc) } if (start_loc == -1) { // No match found. :( - results[x] = false; + results[x] = false // Subtract the delta for this failed patch from subsequent patches. - delta -= patches[x].length2 - patches[x].length1; + delta -= patches[x].length2 - patches[x].length1 } else { // Found a match. :) - results[x] = true; - delta = start_loc - expected_loc; - var text2; + results[x] = true + delta = start_loc - expected_loc + var text2 if (end_loc == -1) { - text2 = text.substring(start_loc, start_loc + text1.length); + text2 = text.substring(start_loc, start_loc + text1.length) } else { - text2 = text.substring(start_loc, end_loc + this.Match_MaxBits); + text2 = text.substring(start_loc, end_loc + this.Match_MaxBits) } if (text1 == text2) { // Perfect match, just shove the replacement text in. - text = text.substring(0, start_loc) + - this.diff_text2(patches[x].diffs) + - text.substring(start_loc + text1.length); + text = + text.substring(0, start_loc) + + this.diff_text2(patches[x].diffs) + + text.substring(start_loc + text1.length) } else { // Imperfect match. Run a diff to get a framework of equivalent // indices. - var diffs = this.diff_main(text1, text2, false); - if (text1.length > this.Match_MaxBits && - this.diff_levenshtein(diffs) / text1.length > - this.Patch_DeleteThreshold) { + var diffs = this.diff_main(text1, text2, false) + if ( + text1.length > this.Match_MaxBits && + this.diff_levenshtein(diffs) / text1.length > + this.Patch_DeleteThreshold + ) { // The end points match, but the content is unacceptably bad. - results[x] = false; + results[x] = false } else { - this.diff_cleanupSemanticLossless(diffs); - var index1 = 0; - var index2; + this.diff_cleanupSemanticLossless(diffs) + var index1 = 0 + var index2 for (var y = 0; y < patches[x].diffs.length; y++) { - var mod = patches[x].diffs[y]; + var mod = patches[x].diffs[y] if (mod[0] !== DIFF_EQUAL) { - index2 = this.diff_xIndex(diffs, index1); + index2 = this.diff_xIndex(diffs, index1) } - if (mod[0] === DIFF_INSERT) { // Insertion - text = text.substring(0, start_loc + index2) + mod[1] + - text.substring(start_loc + index2); - } else if (mod[0] === DIFF_DELETE) { // Deletion - text = text.substring(0, start_loc + index2) + - text.substring(start_loc + this.diff_xIndex(diffs, - index1 + mod[1].length)); + if (mod[0] === DIFF_INSERT) { + // Insertion + text = + text.substring(0, start_loc + index2) + + mod[1] + + text.substring(start_loc + index2) + } else if (mod[0] === DIFF_DELETE) { + // Deletion + text = + text.substring(0, start_loc + index2) + + text.substring( + start_loc + this.diff_xIndex(diffs, index1 + mod[1].length) + ) } if (mod[0] !== DIFF_DELETE) { - index1 += mod[1].length; + index1 += mod[1].length } } } @@ -1874,10 +2017,9 @@ diff_match_patch.prototype.patch_apply = function(patches, text) { } } // Strip the padding off. - text = text.substring(nullPadding.length, text.length - nullPadding.length); - return [text, results]; -}; - + text = text.substring(nullPadding.length, text.length - nullPadding.length) + return [text, results] +} /** * Add some padding on text start and end so that edges can match something. @@ -1885,58 +2027,57 @@ diff_match_patch.prototype.patch_apply = function(patches, text) { * @param {!Array.} patches Array of Patch objects. * @return {string} The padding string added to each side. */ -diff_match_patch.prototype.patch_addPadding = function(patches) { - var paddingLength = this.Patch_Margin; - var nullPadding = ''; +diff_match_patch.prototype.patch_addPadding = function (patches) { + var paddingLength = this.Patch_Margin + var nullPadding = '' for (var x = 1; x <= paddingLength; x++) { - nullPadding += String.fromCharCode(x); + nullPadding += String.fromCharCode(x) } // Bump all the patches forward. for (var x = 0; x < patches.length; x++) { - patches[x].start1 += paddingLength; - patches[x].start2 += paddingLength; + patches[x].start1 += paddingLength + patches[x].start2 += paddingLength } // Add some padding on start of first diff. - var patch = patches[0]; - var diffs = patch.diffs; + var patch = patches[0] + var diffs = patch.diffs if (diffs.length == 0 || diffs[0][0] != DIFF_EQUAL) { // Add nullPadding equality. - diffs.unshift([DIFF_EQUAL, nullPadding]); - patch.start1 -= paddingLength; // Should be 0. - patch.start2 -= paddingLength; // Should be 0. - patch.length1 += paddingLength; - patch.length2 += paddingLength; + diffs.unshift([DIFF_EQUAL, nullPadding]) + patch.start1 -= paddingLength // Should be 0. + patch.start2 -= paddingLength // Should be 0. + patch.length1 += paddingLength + patch.length2 += paddingLength } else if (paddingLength > diffs[0][1].length) { // Grow first equality. - var extraLength = paddingLength - diffs[0][1].length; - diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1]; - patch.start1 -= extraLength; - patch.start2 -= extraLength; - patch.length1 += extraLength; - patch.length2 += extraLength; + var extraLength = paddingLength - diffs[0][1].length + diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1] + patch.start1 -= extraLength + patch.start2 -= extraLength + patch.length1 += extraLength + patch.length2 += extraLength } // Add some padding on end of last diff. - patch = patches[patches.length - 1]; - diffs = patch.diffs; + patch = patches[patches.length - 1] + diffs = patch.diffs if (diffs.length == 0 || diffs[diffs.length - 1][0] != DIFF_EQUAL) { // Add nullPadding equality. - diffs.push([DIFF_EQUAL, nullPadding]); - patch.length1 += paddingLength; - patch.length2 += paddingLength; + diffs.push([DIFF_EQUAL, nullPadding]) + patch.length1 += paddingLength + patch.length2 += paddingLength } else if (paddingLength > diffs[diffs.length - 1][1].length) { // Grow last equality. - var extraLength = paddingLength - diffs[diffs.length - 1][1].length; - diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength); - patch.length1 += extraLength; - patch.length2 += extraLength; + var extraLength = paddingLength - diffs[diffs.length - 1][1].length + diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength) + patch.length1 += extraLength + patch.length2 += extraLength } - return nullPadding; -}; - + return nullPadding +} /** * Look through the patches and break up any which are longer than the maximum @@ -1944,106 +2085,115 @@ diff_match_patch.prototype.patch_addPadding = function(patches) { * Intended to be called only from within patch_apply. * @param {!Array.} patches Array of Patch objects. */ -diff_match_patch.prototype.patch_splitMax = function(patches) { - var patch_size = this.Match_MaxBits; +diff_match_patch.prototype.patch_splitMax = function (patches) { + var patch_size = this.Match_MaxBits for (var x = 0; x < patches.length; x++) { if (patches[x].length1 <= patch_size) { - continue; + continue } - var bigpatch = patches[x]; + var bigpatch = patches[x] // Remove the big old patch. - patches.splice(x--, 1); - var start1 = bigpatch.start1; - var start2 = bigpatch.start2; - var precontext = ''; + patches.splice(x--, 1) + var start1 = bigpatch.start1 + var start2 = bigpatch.start2 + var precontext = '' while (bigpatch.diffs.length !== 0) { // Create one of several smaller patches. - var patch = new diff_match_patch.patch_obj(); - var empty = true; - patch.start1 = start1 - precontext.length; - patch.start2 = start2 - precontext.length; + var patch = new diff_match_patch.patch_obj() + var empty = true + patch.start1 = start1 - precontext.length + patch.start2 = start2 - precontext.length if (precontext !== '') { - patch.length1 = patch.length2 = precontext.length; - patch.diffs.push([DIFF_EQUAL, precontext]); + patch.length1 = patch.length2 = precontext.length + patch.diffs.push([DIFF_EQUAL, precontext]) } - while (bigpatch.diffs.length !== 0 && - patch.length1 < patch_size - this.Patch_Margin) { - var diff_type = bigpatch.diffs[0][0]; - var diff_text = bigpatch.diffs[0][1]; + while ( + bigpatch.diffs.length !== 0 && + patch.length1 < patch_size - this.Patch_Margin + ) { + var diff_type = bigpatch.diffs[0][0] + var diff_text = bigpatch.diffs[0][1] if (diff_type === DIFF_INSERT) { // Insertions are harmless. - patch.length2 += diff_text.length; - start2 += diff_text.length; - patch.diffs.push(bigpatch.diffs.shift()); - empty = false; - } else if (diff_type === DIFF_DELETE && patch.diffs.length == 1 && - patch.diffs[0][0] == DIFF_EQUAL && - diff_text.length > 2 * patch_size) { + patch.length2 += diff_text.length + start2 += diff_text.length + patch.diffs.push(bigpatch.diffs.shift()) + empty = false + } else if ( + diff_type === DIFF_DELETE && + patch.diffs.length == 1 && + patch.diffs[0][0] == DIFF_EQUAL && + diff_text.length > 2 * patch_size + ) { // This is a large deletion. Let it pass in one chunk. - patch.length1 += diff_text.length; - start1 += diff_text.length; - empty = false; - patch.diffs.push([diff_type, diff_text]); - bigpatch.diffs.shift(); + patch.length1 += diff_text.length + start1 += diff_text.length + empty = false + patch.diffs.push([diff_type, diff_text]) + bigpatch.diffs.shift() } else { // Deletion or equality. Only take as much as we can stomach. - diff_text = diff_text.substring(0, - patch_size - patch.length1 - this.Patch_Margin); - patch.length1 += diff_text.length; - start1 += diff_text.length; + diff_text = diff_text.substring( + 0, + patch_size - patch.length1 - this.Patch_Margin + ) + patch.length1 += diff_text.length + start1 += diff_text.length if (diff_type === DIFF_EQUAL) { - patch.length2 += diff_text.length; - start2 += diff_text.length; + patch.length2 += diff_text.length + start2 += diff_text.length } else { - empty = false; + empty = false } - patch.diffs.push([diff_type, diff_text]); + patch.diffs.push([diff_type, diff_text]) if (diff_text == bigpatch.diffs[0][1]) { - bigpatch.diffs.shift(); + bigpatch.diffs.shift() } else { - bigpatch.diffs[0][1] = - bigpatch.diffs[0][1].substring(diff_text.length); + bigpatch.diffs[0][1] = bigpatch.diffs[0][1].substring( + diff_text.length + ) } } } // Compute the head context for the next patch. - precontext = this.diff_text2(patch.diffs); - precontext = - precontext.substring(precontext.length - this.Patch_Margin); + precontext = this.diff_text2(patch.diffs) + precontext = precontext.substring(precontext.length - this.Patch_Margin) // Append the end context for this patch. - var postcontext = this.diff_text1(bigpatch.diffs) - .substring(0, this.Patch_Margin); + var postcontext = this.diff_text1(bigpatch.diffs).substring( + 0, + this.Patch_Margin + ) if (postcontext !== '') { - patch.length1 += postcontext.length; - patch.length2 += postcontext.length; - if (patch.diffs.length !== 0 && - patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) { - patch.diffs[patch.diffs.length - 1][1] += postcontext; + patch.length1 += postcontext.length + patch.length2 += postcontext.length + if ( + patch.diffs.length !== 0 && + patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL + ) { + patch.diffs[patch.diffs.length - 1][1] += postcontext } else { - patch.diffs.push([DIFF_EQUAL, postcontext]); + patch.diffs.push([DIFF_EQUAL, postcontext]) } } if (!empty) { - patches.splice(++x, 0, patch); + patches.splice(++x, 0, patch) } } } -}; - +} /** * Take a list of patches and return a textual representation. * @param {!Array.} patches Array of Patch objects. * @return {string} Text representation of patches. */ -diff_match_patch.prototype.patch_toText = function(patches) { - var text = []; +diff_match_patch.prototype.patch_toText = function (patches) { + var text = [] for (var x = 0; x < patches.length; x++) { - text[x] = patches[x]; + text[x] = patches[x] } - return text.join(''); -}; - + return text.join('') +} /** * Parse a textual representation of patches and return a list of Patch objects. @@ -2051,94 +2201,92 @@ diff_match_patch.prototype.patch_toText = function(patches) { * @return {!Array.} Array of Patch objects. * @throws {!Error} If invalid input. */ -diff_match_patch.prototype.patch_fromText = function(textline) { - var patches = []; +diff_match_patch.prototype.patch_fromText = function (textline) { + var patches = [] if (!textline) { - return patches; + return patches } - var text = textline.split('\n'); - var textPointer = 0; - var patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/; + var text = textline.split('\n') + var textPointer = 0 + var patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/ while (textPointer < text.length) { - var m = text[textPointer].match(patchHeader); + var m = text[textPointer].match(patchHeader) if (!m) { - throw new Error('Invalid patch string: ' + text[textPointer]); + throw new Error('Invalid patch string: ' + text[textPointer]) } - var patch = new diff_match_patch.patch_obj(); - patches.push(patch); - patch.start1 = parseInt(m[1], 10); + var patch = new diff_match_patch.patch_obj() + patches.push(patch) + patch.start1 = parseInt(m[1], 10) if (m[2] === '') { - patch.start1--; - patch.length1 = 1; + patch.start1-- + patch.length1 = 1 } else if (m[2] == '0') { - patch.length1 = 0; + patch.length1 = 0 } else { - patch.start1--; - patch.length1 = parseInt(m[2], 10); + patch.start1-- + patch.length1 = parseInt(m[2], 10) } - patch.start2 = parseInt(m[3], 10); + patch.start2 = parseInt(m[3], 10) if (m[4] === '') { - patch.start2--; - patch.length2 = 1; + patch.start2-- + patch.length2 = 1 } else if (m[4] == '0') { - patch.length2 = 0; + patch.length2 = 0 } else { - patch.start2--; - patch.length2 = parseInt(m[4], 10); + patch.start2-- + patch.length2 = parseInt(m[4], 10) } - textPointer++; + textPointer++ while (textPointer < text.length) { - var sign = text[textPointer].charAt(0); + var sign = text[textPointer].charAt(0) try { - var line = decodeURI(text[textPointer].substring(1)); + var line = decodeURI(text[textPointer].substring(1)) } catch (ex) { // Malformed URI sequence. - throw new Error('Illegal escape in patch_fromText: ' + line); + throw new Error('Illegal escape in patch_fromText: ' + line) } if (sign == '-') { // Deletion. - patch.diffs.push([DIFF_DELETE, line]); + patch.diffs.push([DIFF_DELETE, line]) } else if (sign == '+') { // Insertion. - patch.diffs.push([DIFF_INSERT, line]); + patch.diffs.push([DIFF_INSERT, line]) } else if (sign == ' ') { // Minor equality. - patch.diffs.push([DIFF_EQUAL, line]); + patch.diffs.push([DIFF_EQUAL, line]) } else if (sign == '@') { // Start of next patch. - break; + break } else if (sign === '') { // Blank line? Whatever. } else { // WTF? - throw new Error('Invalid patch mode "' + sign + '" in: ' + line); + throw new Error('Invalid patch mode "' + sign + '" in: ' + line) } - textPointer++; + textPointer++ } } - return patches; -}; - + return patches +} /** * Class representing one patch operation. * @constructor */ -diff_match_patch.patch_obj = function() { +diff_match_patch.patch_obj = function () { /** @type {!Array.} */ - this.diffs = []; + this.diffs = [] /** @type {?number} */ - this.start1 = null; + this.start1 = null /** @type {?number} */ - this.start2 = null; + this.start2 = null /** @type {number} */ - this.length1 = 0; + this.length1 = 0 /** @type {number} */ - this.length2 = 0; -}; - + this.length2 = 0 +} /** * Emmulate GNU diff's format. @@ -2146,48 +2294,47 @@ diff_match_patch.patch_obj = function() { * Indicies are printed as 1-based, not 0-based. * @return {string} The GNU diff string. */ -diff_match_patch.patch_obj.prototype.toString = function() { - var coords1, coords2; +diff_match_patch.patch_obj.prototype.toString = function () { + var coords1, coords2 if (this.length1 === 0) { - coords1 = this.start1 + ',0'; + coords1 = this.start1 + ',0' } else if (this.length1 == 1) { - coords1 = this.start1 + 1; + coords1 = this.start1 + 1 } else { - coords1 = (this.start1 + 1) + ',' + this.length1; + coords1 = this.start1 + 1 + ',' + this.length1 } if (this.length2 === 0) { - coords2 = this.start2 + ',0'; + coords2 = this.start2 + ',0' } else if (this.length2 == 1) { - coords2 = this.start2 + 1; + coords2 = this.start2 + 1 } else { - coords2 = (this.start2 + 1) + ',' + this.length2; + coords2 = this.start2 + 1 + ',' + this.length2 } - var text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\n']; - var op; + var text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\n'] + var op // Escape the body of the patch with %xx notation. for (var x = 0; x < this.diffs.length; x++) { switch (this.diffs[x][0]) { case DIFF_INSERT: - op = '+'; - break; + op = '+' + break case DIFF_DELETE: - op = '-'; - break; + op = '-' + break case DIFF_EQUAL: - op = ' '; - break; + op = ' ' + break } - text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\n'; + text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\n' } - return text.join('').replace(/%20/g, ' '); -}; - + return text.join('').replace(/%20/g, ' ') +} // Export these global variables so that they survive Google's JS compiler. // In a browser, 'this' will be 'window'. // Users of node.js should 'require' the uncompressed version since Google's // JS compiler may break the following exports for non-browser environments. -this['diff_match_patch'] = diff_match_patch; -this['DIFF_DELETE'] = DIFF_DELETE; -this['DIFF_INSERT'] = DIFF_INSERT; -this['DIFF_EQUAL'] = DIFF_EQUAL; +this.diff_match_patch = diff_match_patch +this.DIFF_DELETE = DIFF_DELETE +this.DIFF_INSERT = DIFF_INSERT +this.DIFF_EQUAL = DIFF_EQUAL diff --git a/services/track-changes/config/settings.defaults.js b/services/track-changes/config/settings.defaults.js index 543feea917..2cee6df6af 100755 --- a/services/track-changes/config/settings.defaults.js +++ b/services/track-changes/config/settings.defaults.js @@ -17,19 +17,19 @@ module.exports = { }, apis: { documentupdater: { - url: `http://${process.env.DOCUMENT_UPDATER_HOST || + url: `http://${ + process.env.DOCUMENT_UPDATER_HOST || process.env.DOCUPDATER_HOST || - 'localhost'}:3003` + 'localhost' + }:3003` }, docstore: { url: `http://${process.env.DOCSTORE_HOST || 'localhost'}:3016` }, web: { - url: `http://${process.env.WEB_API_HOST || - process.env.WEB_HOST || - 'localhost'}:${process.env.WEB_API_PORT || - process.env.WEB_PORT || - 3000}`, + url: `http://${ + process.env.WEB_API_HOST || process.env.WEB_HOST || 'localhost' + }:${process.env.WEB_API_PORT || process.env.WEB_PORT || 3000}`, user: process.env.WEB_API_USER || 'sharelatex', pass: process.env.WEB_API_PASSWORD || 'password' } diff --git a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js index c20703d71f..d6d6b44726 100644 --- a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js @@ -24,13 +24,13 @@ const TrackChangesApp = require('./helpers/TrackChangesApp') const TrackChangesClient = require('./helpers/TrackChangesClient') const MockWebApi = require('./helpers/MockWebApi') -describe('Appending doc ops to the history', function() { - before(function(done) { +describe('Appending doc ops to the history', function () { + before(function (done) { return TrackChangesApp.ensureRunning(done) }) - describe('when the history does not exist yet', function() { - before(function(done) { + describe('when the history does not exist yet', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -55,7 +55,7 @@ describe('Appending doc ops to the history', function() { v: 5 } ], - error => { + (error) => { if (error != null) { throw error } @@ -75,7 +75,7 @@ describe('Appending doc ops to the history', function() { return null }) - it('should insert the compressed op into mongo', function() { + it('should insert the compressed op into mongo', function () { return expect(this.updates[0].pack[0].op).to.deep.equal([ { p: 3, @@ -84,21 +84,21 @@ describe('Appending doc ops to the history', function() { ]) }) - it('should insert the correct version number into mongo', function() { + it('should insert the correct version number into mongo', function () { return expect(this.updates[0].v).to.equal(5) }) - it('should store the doc id', function() { + it('should store the doc id', function () { return expect(this.updates[0].doc_id.toString()).to.equal(this.doc_id) }) - it('should store the project id', function() { + it('should store the project id', function () { return expect(this.updates[0].project_id.toString()).to.equal( this.project_id ) }) - return it('should clear the doc from the DocsWithHistoryOps set', function(done) { + return it('should clear the doc from the DocsWithHistoryOps set', function (done) { rclient.sismember( `DocsWithHistoryOps:${this.project_id}`, this.doc_id, @@ -111,8 +111,8 @@ describe('Appending doc ops to the history', function() { }) }) - describe('when the history has already been started', function() { - beforeEach(function(done) { + describe('when the history has already been started', function () { + beforeEach(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -137,7 +137,7 @@ describe('Appending doc ops to the history', function() { v: 5 } ], - error => { + (error) => { if (error != null) { throw error } @@ -156,8 +156,8 @@ describe('Appending doc ops to the history', function() { return null }) - describe('when the updates are recent and from the same user', function() { - beforeEach(function(done) { + describe('when the updates are recent and from the same user', function () { + beforeEach(function (done) { TrackChangesClient.pushRawUpdates( this.project_id, this.doc_id, @@ -178,7 +178,7 @@ describe('Appending doc ops to the history', function() { v: 8 } ], - error => { + (error) => { if (error != null) { throw error } @@ -198,7 +198,7 @@ describe('Appending doc ops to the history', function() { return null }) - it('should combine all the updates into one pack', function() { + it('should combine all the updates into one pack', function () { return expect(this.updates[0].pack[1].op).to.deep.equal([ { p: 6, @@ -207,13 +207,13 @@ describe('Appending doc ops to the history', function() { ]) }) - return it('should insert the correct version number into mongo', function() { + return it('should insert the correct version number into mongo', function () { return expect(this.updates[0].v_end).to.equal(8) }) }) - return describe('when the updates are far apart', function() { - beforeEach(function(done) { + return describe('when the updates are far apart', function () { + beforeEach(function (done) { const oneDay = 24 * 60 * 60 * 1000 TrackChangesClient.pushRawUpdates( this.project_id, @@ -235,7 +235,7 @@ describe('Appending doc ops to the history', function() { v: 8 } ], - error => { + (error) => { if (error != null) { throw error } @@ -255,7 +255,7 @@ describe('Appending doc ops to the history', function() { return null }) - return it('should combine the updates into one pack', function() { + return it('should combine the updates into one pack', function () { expect(this.updates[0].pack[0].op).to.deep.equal([ { p: 3, @@ -272,8 +272,8 @@ describe('Appending doc ops to the history', function() { }) }) - describe('when the updates need processing in batches', function() { - before(function(done) { + describe('when the updates need processing in batches', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -293,7 +293,7 @@ describe('Appending doc ops to the history', function() { this.project_id, this.doc_id, updates, - error => { + (error) => { if (error != null) { throw error } @@ -313,17 +313,17 @@ describe('Appending doc ops to the history', function() { return null }) - it('should concat the compressed op into mongo', function() { + it('should concat the compressed op into mongo', function () { return expect(this.updates[0].pack.length).to.deep.equal(3) }) // batch size is 100 - return it('should insert the correct version number into mongo', function() { + return it('should insert the correct version number into mongo', function () { return expect(this.updates[0].v_end).to.equal(250) }) }) - describe('when there are multiple ops in each update', function() { - before(function(done) { + describe('when there are multiple ops in each update', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -352,7 +352,7 @@ describe('Appending doc ops to the history', function() { v: 4 } ], - error => { + (error) => { if (error != null) { throw error } @@ -372,7 +372,7 @@ describe('Appending doc ops to the history', function() { return null }) - it('should insert the compressed ops into mongo', function() { + it('should insert the compressed ops into mongo', function () { expect(this.updates[0].pack[0].op).to.deep.equal([ { p: 3, @@ -387,14 +387,14 @@ describe('Appending doc ops to the history', function() { ]) }) - return it('should insert the correct version numbers into mongo', function() { + return it('should insert the correct version numbers into mongo', function () { expect(this.updates[0].pack[0].v).to.equal(3) return expect(this.updates[0].pack[1].v).to.equal(4) }) }) - describe('when there is a no-op update', function() { - before(function(done) { + describe('when there is a no-op update', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -415,7 +415,7 @@ describe('Appending doc ops to the history', function() { v: 4 } ], - error => { + (error) => { if (error != null) { throw error } @@ -435,11 +435,11 @@ describe('Appending doc ops to the history', function() { return null }) - it('should insert the compressed no-op into mongo', function() { + it('should insert the compressed no-op into mongo', function () { return expect(this.updates[0].pack[0].op).to.deep.equal([]) }) - it('should insert the compressed next update into mongo', function() { + it('should insert the compressed next update into mongo', function () { return expect(this.updates[0].pack[1].op).to.deep.equal([ { p: 3, @@ -448,14 +448,14 @@ describe('Appending doc ops to the history', function() { ]) }) - return it('should insert the correct version numbers into mongo', function() { + return it('should insert the correct version numbers into mongo', function () { expect(this.updates[0].pack[0].v).to.equal(3) return expect(this.updates[0].pack[1].v).to.equal(4) }) }) - describe('when there is a comment update', function() { - before(function(done) { + describe('when there is a comment update', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -473,7 +473,7 @@ describe('Appending doc ops to the history', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } @@ -493,19 +493,19 @@ describe('Appending doc ops to the history', function() { return null }) - it('should ignore the comment op', function() { + it('should ignore the comment op', function () { return expect(this.updates[0].pack[0].op).to.deep.equal([ { d: 'bar', p: 6 } ]) }) - return it('should insert the correct version numbers into mongo', function() { + return it('should insert the correct version numbers into mongo', function () { return expect(this.updates[0].pack[0].v).to.equal(3) }) }) - describe('when the project has versioning enabled', function() { - before(function(done) { + describe('when the project has versioning enabled', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -521,7 +521,7 @@ describe('Appending doc ops to the history', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } @@ -541,13 +541,13 @@ describe('Appending doc ops to the history', function() { return null }) - return it('should not add a expiresAt entry in the update in mongo', function() { + return it('should not add a expiresAt entry in the update in mongo', function () { return expect(this.updates[0].expiresAt).to.be.undefined }) }) - return describe('when the project does not have versioning enabled', function() { - before(function(done) { + return describe('when the project does not have versioning enabled', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -563,7 +563,7 @@ describe('Appending doc ops to the history', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } @@ -583,7 +583,7 @@ describe('Appending doc ops to the history', function() { return null }) - return it('should add a expiresAt entry in the update in mongo', function() { + return it('should add a expiresAt entry in the update in mongo', function () { return expect(this.updates[0].expiresAt).to.exist }) }) diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index d66ed60940..8ceb1b0ba7 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -30,15 +30,15 @@ const TrackChangesClient = require('./helpers/TrackChangesClient') const MockDocStoreApi = require('./helpers/MockDocStoreApi') const MockWebApi = require('./helpers/MockWebApi') -describe('Archiving updates', function() { - before(function(done) { +describe('Archiving updates', function () { + before(function (done) { if ( __guard__( __guard__( Settings != null ? Settings.trackchanges : undefined, - x1 => x1.s3 + (x1) => x1.s3 ), - x => x.key.length + (x) => x.key.length ) < 1 ) { const message = new Error('s3 keys not setup, this test setup will fail') @@ -48,7 +48,7 @@ describe('Archiving updates', function() { return TrackChangesClient.waitForS3(done) }) - before(function(done) { + before(function (done) { this.now = Date.now() this.to = this.now this.user_id = ObjectId().toString() @@ -104,14 +104,14 @@ describe('Archiving updates', function() { this.project_id, this.doc_id, this.updates, - error => { + (error) => { if (error != null) { throw error } return TrackChangesClient.flushDoc( this.project_id, this.doc_id, - error => { + (error) => { if (error != null) { throw error } @@ -124,7 +124,7 @@ describe('Archiving updates', function() { return null }) - after(function(done) { + after(function (done) { MockWebApi.getUserInfo.restore() return db.docHistory.remove( { project_id: ObjectId(this.project_id) }, @@ -143,18 +143,22 @@ describe('Archiving updates', function() { ) }) - describe("archiving a doc's updates", function() { - before(function(done) { - TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, error => { - if (error != null) { - throw error + describe("archiving a doc's updates", function () { + before(function (done) { + TrackChangesClient.pushDocHistory( + this.project_id, + this.doc_id, + (error) => { + if (error != null) { + throw error + } + return done() } - return done() - }) + ) return null }) - it('should have one cached pack', function(done) { + it('should have one cached pack', function (done) { return db.docHistory.count( { doc_id: ObjectId(this.doc_id), expiresAt: { $exists: true } }, (error, count) => { @@ -167,7 +171,7 @@ describe('Archiving updates', function() { ) }) - it('should have one remaining pack after cache is expired', function(done) { + it('should have one remaining pack after cache is expired', function (done) { return db.docHistory.remove( { doc_id: ObjectId(this.doc_id), @@ -191,7 +195,7 @@ describe('Archiving updates', function() { ) }) - it('should have a docHistoryIndex entry marked as inS3', function(done) { + it('should have a docHistoryIndex entry marked as inS3', function (done) { return db.docHistoryIndex.findOne( { _id: ObjectId(this.doc_id) }, (error, index) => { @@ -204,7 +208,7 @@ describe('Archiving updates', function() { ) }) - it('should have a docHistoryIndex entry with the last version', function(done) { + it('should have a docHistoryIndex entry with the last version', function (done) { return db.docHistoryIndex.findOne( { _id: ObjectId(this.doc_id) }, (error, index) => { @@ -217,7 +221,7 @@ describe('Archiving updates', function() { ) }) - return it('should store 1024 doc changes in S3 in one pack', function(done) { + return it('should store 1024 doc changes in S3 in one pack', function (done) { return db.docHistoryIndex.findOne( { _id: ObjectId(this.doc_id) }, (error, index) => { @@ -240,18 +244,22 @@ describe('Archiving updates', function() { }) }) - return describe("unarchiving a doc's updates", function() { - before(function(done) { - TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, error => { - if (error != null) { - throw error + return describe("unarchiving a doc's updates", function () { + before(function (done) { + TrackChangesClient.pullDocHistory( + this.project_id, + this.doc_id, + (error) => { + if (error != null) { + throw error + } + return done() } - return done() - }) + ) return null }) - return it('should restore both packs', function(done) { + return it('should restore both packs', function (done) { return db.docHistory.count( { doc_id: ObjectId(this.doc_id) }, (error, count) => { diff --git a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js index 9eaf86e85e..4a06e086e5 100644 --- a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js @@ -24,13 +24,13 @@ const TrackChangesApp = require('./helpers/TrackChangesApp') const TrackChangesClient = require('./helpers/TrackChangesClient') const MockWebApi = require('./helpers/MockWebApi') -describe('Flushing updates', function() { - before(function(done) { +describe('Flushing updates', function () { + before(function (done) { return TrackChangesApp.ensureRunning(done) }) - describe("flushing a doc's updates", function() { - before(function(done) { + describe("flushing a doc's updates", function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -46,14 +46,14 @@ describe('Flushing updates', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } return TrackChangesClient.flushDoc( this.project_id, this.doc_id, - error => { + (error) => { if (error != null) { throw error } @@ -65,7 +65,7 @@ describe('Flushing updates', function() { return null }) - return it('should flush the op into mongo', function(done) { + return it('should flush the op into mongo', function (done) { TrackChangesClient.getCompressedUpdates(this.doc_id, (error, updates) => { expect(updates[0].pack[0].op).to.deep.equal([ { @@ -79,9 +79,9 @@ describe('Flushing updates', function() { }) }) - return describe("flushing a project's updates", function() { - describe('with versioning enabled', function() { - before(function(done) { + return describe("flushing a project's updates", function () { + describe('with versioning enabled', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -109,11 +109,11 @@ describe('Flushing updates', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } - return TrackChangesClient.flushProject(this.project_id, error => { + return TrackChangesClient.flushProject(this.project_id, (error) => { if (error != null) { throw error } @@ -124,7 +124,7 @@ describe('Flushing updates', function() { return null }) - it('should not mark the updates for deletion', function(done) { + it('should not mark the updates for deletion', function (done) { TrackChangesClient.getCompressedUpdates( this.doc_id, (error, updates) => { @@ -135,7 +135,7 @@ describe('Flushing updates', function() { return null }) - return it('should preserve history forever', function(done) { + return it('should preserve history forever', function (done) { TrackChangesClient.getProjectMetaData( this.project_id, (error, project) => { @@ -147,8 +147,8 @@ describe('Flushing updates', function() { }) }) - describe('without versioning enabled', function() { - before(function(done) { + describe('without versioning enabled', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -176,11 +176,11 @@ describe('Flushing updates', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } - return TrackChangesClient.flushProject(this.project_id, error => { + return TrackChangesClient.flushProject(this.project_id, (error) => { if (error != null) { throw error } @@ -191,7 +191,7 @@ describe('Flushing updates', function() { return null }) - return it('should mark the updates for deletion', function(done) { + return it('should mark the updates for deletion', function (done) { TrackChangesClient.getCompressedUpdates( this.doc_id, (error, updates) => { @@ -203,8 +203,8 @@ describe('Flushing updates', function() { }) }) - return describe('without versioning enabled but with preserveHistory set to true', function() { - before(function(done) { + return describe('without versioning enabled but with preserveHistory set to true', function () { + before(function (done) { this.project_id = ObjectId().toString() this.doc_id = ObjectId().toString() this.user_id = ObjectId().toString() @@ -219,7 +219,7 @@ describe('Flushing updates', function() { TrackChangesClient.setPreserveHistoryForProject( this.project_id, - error => { + (error) => { if (error != null) { throw error } @@ -241,13 +241,13 @@ describe('Flushing updates', function() { v: 3 } ], - error => { + (error) => { if (error != null) { throw error } return TrackChangesClient.flushProject( this.project_id, - error => { + (error) => { if (error != null) { throw error } @@ -261,7 +261,7 @@ describe('Flushing updates', function() { return null }) - return it('should not mark the updates for deletion', function(done) { + return it('should not mark the updates for deletion', function (done) { TrackChangesClient.getCompressedUpdates( this.doc_id, (error, updates) => { diff --git a/services/track-changes/test/acceptance/js/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js index 871258b30d..4830f338d4 100644 --- a/services/track-changes/test/acceptance/js/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/js/GettingADiffTests.js @@ -23,8 +23,8 @@ const TrackChangesClient = require('./helpers/TrackChangesClient') const MockDocUpdaterApi = require('./helpers/MockDocUpdaterApi') const MockWebApi = require('./helpers/MockWebApi') -describe('Getting a diff', function() { - beforeEach(function(done) { +describe('Getting a diff', function () { + beforeEach(function (done) { sinon.spy(MockDocUpdaterApi, 'getDoc') this.now = Date.now() @@ -89,7 +89,7 @@ describe('Getting a diff', function() { this.project_id, this.doc_id, this.updates, - error => { + (error) => { if (error != null) { throw error } @@ -112,17 +112,17 @@ describe('Getting a diff', function() { return null }) - afterEach(function() { + afterEach(function () { MockDocUpdaterApi.getDoc.restore() MockWebApi.getUserInfo.restore() return null }) - it('should return the diff', function() { + it('should return the diff', function () { return expect(this.diff).to.deep.equal(this.expected_diff) }) - return it('should get the doc from the doc updater', function() { + return it('should get the doc from the doc updater', function () { MockDocUpdaterApi.getDoc .calledWith(this.project_id, this.doc_id) .should.equal(true) diff --git a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js index d9cd43f1f8..052cb405e7 100644 --- a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js @@ -23,8 +23,8 @@ const TrackChangesApp = require('./helpers/TrackChangesApp') const TrackChangesClient = require('./helpers/TrackChangesClient') const MockWebApi = require('./helpers/MockWebApi') -describe('Getting updates', function() { - before(function(done) { +describe('Getting updates', function () { + before(function (done) { this.now = Date.now() this.to = this.now this.user_id = ObjectId().toString() @@ -72,7 +72,7 @@ describe('Getting updates', function() { this.project_id, this.doc_id, this.updates, - error => { + (error) => { if (error != null) { throw error } @@ -82,7 +82,6 @@ describe('Getting updates', function() { }) return null }) - ;({ after() { MockWebApi.getUserInfo.restore() @@ -90,8 +89,8 @@ describe('Getting updates', function() { } }) - describe('getting updates up to the limit', function() { - before(function(done) { + describe('getting updates up to the limit', function () { + before(function (done) { TrackChangesClient.getUpdates( this.project_id, { before: this.to + 1, min_count: 3 }, @@ -106,11 +105,11 @@ describe('Getting updates', function() { return null }) - it('should fetch the user details from the web api', function() { + it('should fetch the user details from the web api', function () { return MockWebApi.getUserInfo.calledWith(this.user_id).should.equal(true) }) - return it('should return at least the min_count number of summarized updates', function() { + return it('should return at least the min_count number of summarized updates', function () { const docs1 = {} docs1[this.doc_id] = { toV: 20, fromV: 19 } const docs2 = {} @@ -146,8 +145,8 @@ describe('Getting updates', function() { }) }) - return describe('getting updates beyond the end of the database', function() { - before(function(done) { + return describe('getting updates beyond the end of the database', function () { + before(function (done) { TrackChangesClient.getUpdates( this.project_id, { before: this.to - 8 * this.hours + 1, min_count: 30 }, @@ -162,7 +161,7 @@ describe('Getting updates', function() { return null }) - return it('should return as many updates as it can', function() { + return it('should return as many updates as it can', function () { const docs1 = {} docs1[this.doc_id] = { toV: 4, fromV: 3 } const docs2 = {} diff --git a/services/track-changes/test/acceptance/js/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js index ad5352ae44..484d1a9bc6 100644 --- a/services/track-changes/test/acceptance/js/LockManagerTests.js +++ b/services/track-changes/test/acceptance/js/LockManagerTests.js @@ -20,42 +20,42 @@ const LockManager = require('../../../app/js/LockManager') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now const TrackChangesApp = require('./helpers/TrackChangesApp') -describe('Locking document', function() { - before(function(done) { +describe('Locking document', function () { + before(function (done) { TrackChangesApp.ensureRunning(done) return null }) - return describe('when the lock has expired in redis', function() { - before(function(done) { + return describe('when the lock has expired in redis', function () { + before(function (done) { LockManager.LOCK_TTL = 1 // second LockManager.runWithLock( 'doc123', - releaseA => { + (releaseA) => { // we create a lock A and allow it to expire in redis return setTimeout( () => // now we create a new lock B and try to release A LockManager.runWithLock( 'doc123', - releaseB => { + (releaseB) => { return releaseA() }, // try to release lock A to see if it wipes out lock B - error => {} + (error) => {} ), // we never release lock B so nothing should happen here 1500 ) }, // enough time to wait until the lock has expired - error => + (error) => // we get here after trying to release lock A done() ) return null }) - return it('the new lock should not be removed by the expired locker', function(done) { + return it('the new lock should not be removed by the expired locker', function (done) { LockManager.checkLock('doc123', (err, isFree) => { expect(isFree).to.equal(false) return done() diff --git a/services/track-changes/test/acceptance/js/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js index bfa55e8ba2..8afef4b168 100644 --- a/services/track-changes/test/acceptance/js/RestoringVersions.js +++ b/services/track-changes/test/acceptance/js/RestoringVersions.js @@ -23,8 +23,8 @@ const TrackChangesClient = require('./helpers/TrackChangesClient') const MockDocUpdaterApi = require('./helpers/MockDocUpdaterApi') const MockWebApi = require('./helpers/MockWebApi') -describe('Restoring a version', function() { - before(function(done) { +describe('Restoring a version', function () { + before(function (done) { sinon.spy(MockDocUpdaterApi, 'setDoc') this.now = Date.now() @@ -78,7 +78,7 @@ describe('Restoring a version', function() { this.project_id, this.doc_id, this.updates, - error => { + (error) => { if (error != null) { throw error } @@ -87,7 +87,7 @@ describe('Restoring a version', function() { this.doc_id, this.beforeVersion, this.user_id, - error => { + (error) => { if (error != null) { throw error } @@ -100,12 +100,12 @@ describe('Restoring a version', function() { return null }) - after(function() { + after(function () { MockDocUpdaterApi.setDoc.restore() return null }) - return it('should set the doc in the doc updater', function() { + return it('should set the doc in the doc updater', function () { MockDocUpdaterApi.setDoc .calledWith( this.project_id, diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js index ef912948eb..b1ebcf9cef 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js @@ -19,7 +19,7 @@ module.exports = MockDocUpdaterApi = { getAllDoc(project_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return callback(null, this.docs) }, @@ -39,12 +39,12 @@ module.exports = MockDocUpdaterApi = { }) return app - .listen(3016, error => { + .listen(3016, (error) => { if (error != null) { throw error } }) - .on('error', error => { + .on('error', (error) => { console.error('error starting MockDocStoreApi:', error.message) return process.exit(1) }) diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js index 449bf1e217..b3b5e23ba7 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js @@ -22,14 +22,14 @@ module.exports = MockDocUpdaterApi = { getDoc(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return callback(null, this.docs[doc_id]) }, setDoc(project_id, doc_id, lines, user_id, undoing, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } if (!this.docs[doc_id]) { this.docs[doc_id] = {} @@ -56,33 +56,30 @@ module.exports = MockDocUpdaterApi = { ) }) - app.post( - '/project/:project_id/doc/:doc_id', - (req, res, next) => { - return this.setDoc( - req.params.project_id, - req.params.doc_id, - req.body.lines, - req.body.user_id, - req.body.undoing, - (errr, doc) => { - if (typeof error !== 'undefined' && error !== null) { - return res.sendStatus(500) - } else { - return res.sendStatus(204) - } + app.post('/project/:project_id/doc/:doc_id', (req, res, next) => { + return this.setDoc( + req.params.project_id, + req.params.doc_id, + req.body.lines, + req.body.user_id, + req.body.undoing, + (errr, doc) => { + if (typeof error !== 'undefined' && error !== null) { + return res.sendStatus(500) + } else { + return res.sendStatus(204) } - ) - } - ) + } + ) + }) return app - .listen(3003, error => { + .listen(3003, (error) => { if (error != null) { throw error } }) - .on('error', error => { + .on('error', (error) => { console.error('error starting MockDocUpdaterApi:', error.message) return process.exit(1) }) diff --git a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js index 45e94db8f8..b7ebbc876f 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js @@ -21,14 +21,14 @@ module.exports = MockWebApi = { getUserInfo(user_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return callback(null, this.users[user_id] || null) }, getProjectDetails(project_id, callback) { if (callback == null) { - callback = function(error, project) {} + callback = function (error, project) {} } return callback(null, this.projects[project_id]) }, @@ -61,12 +61,12 @@ module.exports = MockWebApi = { }) return app - .listen(3000, error => { + .listen(3000, (error) => { if (error != null) { throw error } }) - .on('error', error => { + .on('error', (error) => { console.error('error starting MockWebApiServer:', error.message) return process.exit(1) }) diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js index 1ce662548e..81efb5c912 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js @@ -23,7 +23,7 @@ module.exports = { callbacks: [], ensureRunning(callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } if (this.running) { return callback() @@ -37,10 +37,10 @@ module.exports = { Settings.internal != null ? Settings.internal.trackchanges : undefined, - x => x.port + (x) => x.port ), 'localhost', - error => { + (error) => { if (error != null) { throw error } diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index d49980976b..cca55a87aa 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -33,9 +33,9 @@ const S3_BUCKET = Settings.trackchanges.stores.doc_history module.exports = TrackChangesClient = { flushAndGetCompressedUpdates(project_id, doc_id, callback) { if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } - return TrackChangesClient.flushDoc(project_id, doc_id, error => { + return TrackChangesClient.flushDoc(project_id, doc_id, (error) => { if (error != null) { return callback(error) } @@ -45,7 +45,7 @@ module.exports = TrackChangesClient = { flushDoc(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return request.post( { @@ -60,7 +60,7 @@ module.exports = TrackChangesClient = { flushProject(project_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return request.post( { @@ -75,7 +75,7 @@ module.exports = TrackChangesClient = { getCompressedUpdates(doc_id, callback) { if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } return db.docHistory .find({ doc_id: ObjectId(doc_id) }) @@ -85,7 +85,7 @@ module.exports = TrackChangesClient = { getProjectMetaData(project_id, callback) { if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } return db.projectHistoryMetaData.find( { @@ -97,7 +97,7 @@ module.exports = TrackChangesClient = { setPreserveHistoryForProject(project_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return db.projectHistoryMetaData.update( { @@ -115,18 +115,18 @@ module.exports = TrackChangesClient = { pushRawUpdates(project_id, doc_id, updates, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return rclient.sadd( Keys.docsWithHistoryOps({ project_id }), doc_id, - error => { + (error) => { if (error != null) { return callback(error) } return rclient.rpush( Keys.uncompressedHistoryOps({ doc_id }), - ...Array.from(Array.from(updates).map(u => JSON.stringify(u))), + ...Array.from(Array.from(updates).map((u) => JSON.stringify(u))), callback ) } @@ -135,7 +135,7 @@ module.exports = TrackChangesClient = { getDiff(project_id, doc_id, from, to, callback) { if (callback == null) { - callback = function(error, diff) {} + callback = function (error, diff) {} } return request.get( { @@ -150,7 +150,7 @@ module.exports = TrackChangesClient = { getUpdates(project_id, options, callback) { if (callback == null) { - callback = function(error, body) {} + callback = function (error, body) {} } return request.get( { @@ -165,7 +165,7 @@ module.exports = TrackChangesClient = { restoreDoc(project_id, doc_id, version, user_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return request.post( { @@ -183,7 +183,7 @@ module.exports = TrackChangesClient = { pushDocHistory(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return request.post( { @@ -198,7 +198,7 @@ module.exports = TrackChangesClient = { pullDocHistory(project_id, doc_id, callback) { if (callback == null) { - callback = function(error) {} + callback = function (error) {} } return request.post( { @@ -237,7 +237,7 @@ module.exports = TrackChangesClient = { getS3Doc(project_id, doc_id, pack_id, callback) { if (callback == null) { - callback = function(error, body) {} + callback = function (error, body) {} } const params = { Bucket: S3_BUCKET, @@ -263,7 +263,7 @@ module.exports = TrackChangesClient = { removeS3Doc(project_id, doc_id, callback) { if (callback == null) { - callback = function(error, res, body) {} + callback = function (error, res, body) {} } let params = { Bucket: S3_BUCKET, @@ -278,7 +278,7 @@ module.exports = TrackChangesClient = { params = { Bucket: S3_BUCKET, Delete: { - Objects: data.Contents.map(s3object => ({ Key: s3object.Key })) + Objects: data.Contents.map((s3object) => ({ Key: s3object.Key })) } } diff --git a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js index b84775ddea..6526c9445d 100644 --- a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js +++ b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js @@ -17,8 +17,8 @@ const { expect } = chai const modulePath = '../../../../app/js/DiffGenerator.js' const SandboxedModule = require('sandboxed-module') -describe('DiffGenerator', function() { - beforeEach(function() { +describe('DiffGenerator', function () { + beforeEach(function () { this.DiffGenerator = SandboxedModule.require(modulePath, { requires: { 'logger-sharelatex': { warn: sinon.stub() } @@ -34,9 +34,9 @@ describe('DiffGenerator', function() { }) }) - describe('rewindOp', function() { - describe('rewinding an insert', function() { - return it('should undo the insert', function() { + describe('rewindOp', function () { + describe('rewinding an insert', function () { + return it('should undo the insert', function () { const content = 'hello world' const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, @@ -46,8 +46,8 @@ describe('DiffGenerator', function() { }) }) - describe('rewinding a delete', function() { - return it('should undo the delete', function() { + describe('rewinding a delete', function () { + return it('should undo the delete', function () { const content = 'hello rld' const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, @@ -57,8 +57,8 @@ describe('DiffGenerator', function() { }) }) - describe('with an inconsistent update', function() { - return it('should throw an error', function() { + describe('with an inconsistent update', function () { + return it('should throw an error', function () { const content = 'hello world' return expect(() => { return this.DiffGenerator.rewindOp(content, { p: 6, i: 'foo' }) @@ -66,8 +66,8 @@ describe('DiffGenerator', function() { }) }) - return describe('with an update which is beyond the length of the content', function() { - return it('should undo the insert as if it were at the end of the content', function() { + return describe('with an update which is beyond the length of the content', function () { + return it('should undo the insert as if it were at the end of the content', function () { const content = 'foobar' const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 4, @@ -78,8 +78,8 @@ describe('DiffGenerator', function() { }) }) - describe('rewindUpdate', function() { - return it('should rewind ops in reverse', function() { + describe('rewindUpdate', function () { + return it('should rewind ops in reverse', function () { const content = 'aaabbbccc' const update = { op: [ @@ -92,8 +92,8 @@ describe('DiffGenerator', function() { }) }) - describe('rewindUpdates', function() { - return it('should rewind updates in reverse', function() { + describe('rewindUpdates', function () { + return it('should rewind updates in reverse', function () { const content = 'aaabbbccc' const updates = [ { op: [{ p: 3, i: 'bbb' }] }, @@ -104,8 +104,8 @@ describe('DiffGenerator', function() { }) }) - describe('buildDiff', function() { - beforeEach(function() { + describe('buildDiff', function () { + beforeEach(function () { this.diff = [{ u: 'mock-diff' }] this.content = 'Hello world' this.updates = [ @@ -121,11 +121,11 @@ describe('DiffGenerator', function() { )) }) - it('should return the diff', function() { + it('should return the diff', function () { return this.result.should.deep.equal(this.diff) }) - it('should build the content into an initial diff', function() { + it('should build the content into an initial diff', function () { return this.DiffGenerator.applyUpdateToDiff .calledWith( [ @@ -138,24 +138,24 @@ describe('DiffGenerator', function() { .should.equal(true) }) - it('should apply each update', function() { - return Array.from(this.updates).map(update => + it('should apply each update', function () { + return Array.from(this.updates).map((update) => this.DiffGenerator.applyUpdateToDiff .calledWith(sinon.match.any, update) .should.equal(true) ) }) - return it('should compress the diff', function() { + return it('should compress the diff', function () { return this.DiffGenerator.compressDiff .calledWith(this.diff) .should.equal(true) }) }) - describe('compressDiff', function() { - describe('with adjacent inserts with the same user_id', function() { - return it('should create one update with combined meta data and min/max timestamps', function() { + describe('compressDiff', function () { + describe('with adjacent inserts with the same user_id', function () { + return it('should create one update with combined meta data and min/max timestamps', function () { const diff = this.DiffGenerator.compressDiff([ { i: 'foo', @@ -175,8 +175,8 @@ describe('DiffGenerator', function() { }) }) - describe('with adjacent inserts with different user_ids', function() { - return it('should leave the inserts unchanged', function() { + describe('with adjacent inserts with different user_ids', function () { + return it('should leave the inserts unchanged', function () { const input = [ { i: 'foo', @@ -192,8 +192,8 @@ describe('DiffGenerator', function() { }) }) - describe('with adjacent deletes with the same user_id', function() { - return it('should create one update with combined meta data and min/max timestamps', function() { + describe('with adjacent deletes with the same user_id', function () { + return it('should create one update with combined meta data and min/max timestamps', function () { const diff = this.DiffGenerator.compressDiff([ { d: 'foo', @@ -213,8 +213,8 @@ describe('DiffGenerator', function() { }) }) - return describe('with adjacent deletes with different user_ids', function() { - return it('should leave the deletes unchanged', function() { + return describe('with adjacent deletes with different user_ids', function () { + return it('should leave the deletes unchanged', function () { const input = [ { d: 'foo', @@ -231,9 +231,9 @@ describe('DiffGenerator', function() { }) }) - return describe('applyUpdateToDiff', function() { - describe('an insert', function() { - it('should insert into the middle of (u)nchanged text', function() { + return describe('applyUpdateToDiff', function () { + describe('an insert', function () { + it('should insert into the middle of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { op: [{ p: 3, i: 'baz' }], meta: this.meta @@ -245,7 +245,7 @@ describe('DiffGenerator', function() { ]) }) - it('should insert into the start of (u)changed text', function() { + it('should insert into the start of (u)changed text', function () { const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { op: [{ p: 0, i: 'baz' }], meta: this.meta @@ -256,7 +256,7 @@ describe('DiffGenerator', function() { ]) }) - it('should insert into the end of (u)changed text', function() { + it('should insert into the end of (u)changed text', function () { const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { op: [{ p: 6, i: 'baz' }], meta: this.meta @@ -267,7 +267,7 @@ describe('DiffGenerator', function() { ]) }) - it('should insert into the middle of (i)inserted text', function() { + it('should insert into the middle of (i)inserted text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ i: 'foobar', meta: this.meta }], { op: [{ p: 3, i: 'baz' }], meta: this.meta } @@ -279,7 +279,7 @@ describe('DiffGenerator', function() { ]) }) - return it('should not count deletes in the running length total', function() { + return it('should not count deletes in the running length total', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ d: 'deleted', meta: this.meta }, { u: 'foobar' }], { op: [{ p: 3, i: 'baz' }], meta: this.meta } @@ -293,9 +293,9 @@ describe('DiffGenerator', function() { }) }) - return describe('a delete', function() { - describe('deleting unchanged text', function() { - it('should delete from the middle of (u)nchanged text', function() { + return describe('a delete', function () { + describe('deleting unchanged text', function () { + it('should delete from the middle of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foobazbar' }], { op: [{ p: 3, d: 'baz' }], meta: this.meta } @@ -307,7 +307,7 @@ describe('DiffGenerator', function() { ]) }) - it('should delete from the start of (u)nchanged text', function() { + it('should delete from the start of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foobazbar' }], { op: [{ p: 0, d: 'foo' }], meta: this.meta } @@ -318,7 +318,7 @@ describe('DiffGenerator', function() { ]) }) - it('should delete from the end of (u)nchanged text', function() { + it('should delete from the end of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foobazbar' }], { op: [{ p: 6, d: 'bar' }], meta: this.meta } @@ -329,7 +329,7 @@ describe('DiffGenerator', function() { ]) }) - return it('should delete across multiple (u)changed text parts', function() { + return it('should delete across multiple (u)changed text parts', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foo' }, { u: 'baz' }, { u: 'bar' }], { op: [{ p: 2, d: 'obazb' }], meta: this.meta } @@ -344,8 +344,8 @@ describe('DiffGenerator', function() { }) }) - describe('deleting inserts', function() { - it('should delete from the middle of (i)nserted text', function() { + describe('deleting inserts', function () { + it('should delete from the middle of (i)nserted text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ i: 'foobazbar', meta: this.meta }], { op: [{ p: 3, d: 'baz' }], meta: this.meta } @@ -356,7 +356,7 @@ describe('DiffGenerator', function() { ]) }) - it('should delete from the start of (u)nchanged text', function() { + it('should delete from the start of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ i: 'foobazbar', meta: this.meta }], { op: [{ p: 0, d: 'foo' }], meta: this.meta } @@ -364,7 +364,7 @@ describe('DiffGenerator', function() { return expect(diff).to.deep.equal([{ i: 'bazbar', meta: this.meta }]) }) - it('should delete from the end of (u)nchanged text', function() { + it('should delete from the end of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ i: 'foobazbar', meta: this.meta }], { op: [{ p: 6, d: 'bar' }], meta: this.meta } @@ -372,7 +372,7 @@ describe('DiffGenerator', function() { return expect(diff).to.deep.equal([{ i: 'foobaz', meta: this.meta }]) }) - return it('should delete across multiple (u)changed and (i)nserted text parts', function() { + return it('should delete across multiple (u)changed and (i)nserted text parts', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foo' }, { i: 'baz', meta: this.meta }, { u: 'bar' }], { op: [{ p: 2, d: 'obazb' }], meta: this.meta } @@ -386,8 +386,8 @@ describe('DiffGenerator', function() { }) }) - describe('deleting over existing deletes', function() { - return it('should delete across multiple (u)changed and (d)deleted text parts', function() { + describe('deleting over existing deletes', function () { + return it('should delete across multiple (u)changed and (d)deleted text parts', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foo' }, { d: 'baz', meta: this.meta }, { u: 'bar' }], { op: [{ p: 2, d: 'ob' }], meta: this.meta } @@ -402,8 +402,8 @@ describe('DiffGenerator', function() { }) }) - describe("deleting when the text doesn't match", function() { - it('should throw an error when deleting from the middle of (u)nchanged text', function() { + describe("deleting when the text doesn't match", function () { + it('should throw an error when deleting from the middle of (u)nchanged text', function () { return expect(() => this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { op: [{ p: 3, d: 'xxx' }], @@ -412,7 +412,7 @@ describe('DiffGenerator', function() { ).to.throw(this.DiffGenerator.ConsistencyError) }) - it('should throw an error when deleting from the start of (u)nchanged text', function() { + it('should throw an error when deleting from the start of (u)nchanged text', function () { return expect(() => this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { op: [{ p: 0, d: 'xxx' }], @@ -421,7 +421,7 @@ describe('DiffGenerator', function() { ).to.throw(this.DiffGenerator.ConsistencyError) }) - return it('should throw an error when deleting from the end of (u)nchanged text', function() { + return it('should throw an error when deleting from the end of (u)nchanged text', function () { return expect(() => this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { op: [{ p: 6, d: 'xxx' }], @@ -431,8 +431,8 @@ describe('DiffGenerator', function() { }) }) - describe('when the last update in the existing diff is a delete', function() { - return it('should insert the new update before the delete', function() { + describe('when the last update in the existing diff is a delete', function () { + return it('should insert the new update before the delete', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ u: 'foo' }, { d: 'bar', meta: this.meta }], { op: [{ p: 3, i: 'baz' }], meta: this.meta } @@ -445,8 +445,8 @@ describe('DiffGenerator', function() { }) }) - return describe('when the only update in the existing diff is a delete', function() { - return it('should insert the new update after the delete', function() { + return describe('when the only update in the existing diff is a delete', function () { + return it('should insert the new update after the delete', function () { const diff = this.DiffGenerator.applyUpdateToDiff( [{ d: 'bar', meta: this.meta }], { op: [{ p: 0, i: 'baz' }], meta: this.meta } diff --git a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js index bee618badf..7b72f3a002 100644 --- a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js @@ -17,8 +17,8 @@ const { expect } = chai const modulePath = '../../../../app/js/DiffManager.js' const SandboxedModule = require('sandboxed-module') -describe('DiffManager', function() { - beforeEach(function() { +describe('DiffManager', function () { + beforeEach(function () { this.DiffManager = SandboxedModule.require(modulePath, { requires: { 'logger-sharelatex': (this.logger = { @@ -38,8 +38,8 @@ describe('DiffManager', function() { return (this.doc_id = 'mock-doc-id') }) - describe('getLatestDocAndUpdates', function() { - beforeEach(function() { + describe('getLatestDocAndUpdates', function () { + beforeEach(function () { this.content = 'hello world' this.version = 42 this.updates = ['mock-update-1', 'mock-update-2'] @@ -52,8 +52,8 @@ describe('DiffManager', function() { .callsArgWith(3, null, this.updates)) }) - describe('with a fromVersion', function() { - beforeEach(function() { + describe('with a fromVersion', function () { + beforeEach(function () { return this.DiffManager.getLatestDocAndUpdates( this.project_id, this.doc_id, @@ -62,27 +62,27 @@ describe('DiffManager', function() { ) }) - it('should get the latest version of the doc', function() { + it('should get the latest version of the doc', function () { return this.DocumentUpdaterManager.getDocument .calledWith(this.project_id, this.doc_id) .should.equal(true) }) - it('should get the latest updates', function() { + it('should get the latest updates', function () { return this.UpdatesManager.getDocUpdatesWithUserInfo .calledWith(this.project_id, this.doc_id, { from: this.from }) .should.equal(true) }) - return it('should call the callback with the content, version and updates', function() { + return it('should call the callback with the content, version and updates', function () { return this.callback .calledWith(null, this.content, this.version, this.updates) .should.equal(true) }) }) - return describe('with no fromVersion', function() { - beforeEach(function() { + return describe('with no fromVersion', function () { + beforeEach(function () { return this.DiffManager.getLatestDocAndUpdates( this.project_id, this.doc_id, @@ -91,19 +91,19 @@ describe('DiffManager', function() { ) }) - it('should get the latest version of the doc', function() { + it('should get the latest version of the doc', function () { return this.DocumentUpdaterManager.getDocument .calledWith(this.project_id, this.doc_id) .should.equal(true) }) - it('should not get the latest updates', function() { + it('should not get the latest updates', function () { return this.UpdatesManager.getDocUpdatesWithUserInfo.called.should.equal( false ) }) - return it('should call the callback with the content, version and blank updates', function() { + return it('should call the callback with the content, version and blank updates', function () { return this.callback .calledWith(null, this.content, this.version, []) .should.equal(true) @@ -111,8 +111,8 @@ describe('DiffManager', function() { }) }) - describe('getDiff', function() { - beforeEach(function() { + describe('getDiff', function () { + beforeEach(function () { this.content = 'hello world' // Op versions are the version they were applied to, so doc is always one version // ahead.s @@ -146,8 +146,8 @@ describe('DiffManager', function() { return (this.diff = [{ u: 'mock-diff' }]) }) - describe('with matching versions', function() { - beforeEach(function() { + describe('with matching versions', function () { + beforeEach(function () { this.DiffManager.getDocumentBeforeVersion = sinon .stub() .callsArgWith(3, null, this.rewound_content, this.updates) @@ -161,13 +161,13 @@ describe('DiffManager', function() { ) }) - it('should get the latest doc and version with all recent updates', function() { + it('should get the latest doc and version with all recent updates', function () { return this.DiffManager.getDocumentBeforeVersion .calledWith(this.project_id, this.doc_id, this.fromVersion) .should.equal(true) }) - it('should generate the diff', function() { + it('should generate the diff', function () { return this.DiffGenerator.buildDiff .calledWith( this.rewound_content, @@ -176,21 +176,20 @@ describe('DiffManager', function() { .should.equal(true) }) - return it('should call the callback with the diff', function() { + return it('should call the callback with the diff', function () { return this.callback.calledWith(null, this.diff).should.equal(true) }) }) - describe('when the updates are inconsistent', function() { - beforeEach(function() { + describe('when the updates are inconsistent', function () { + beforeEach(function () { this.DiffManager.getLatestDocAndUpdates = sinon .stub() .callsArgWith(3, null, this.content, this.version, this.updates) this.DiffGenerator.buildDiff = sinon .stub() .throws((this.error = new Error('inconsistent!'))) - this.DiffGenerator.rewindUpdates = sinon - .stub() + this.DiffGenerator.rewindUpdates = sinon.stub() this.DiffManager.getDiff( this.project_id, this.doc_id, @@ -200,7 +199,7 @@ describe('DiffManager', function() { ) }) - it('should call the callback with an error', function() { + it('should call the callback with an error', function () { this.callback.calledWith(sinon.match(Error)).should.equal(true) const errorObj = this.callback.args[0][0] expect(errorObj.message).to.include('inconsistent!') @@ -208,15 +207,15 @@ describe('DiffManager', function() { }) }) - describe('getDocumentBeforeVersion', function() { - beforeEach(function() { + describe('getDocumentBeforeVersion', function () { + beforeEach(function () { this.DiffManager._tryGetDocumentBeforeVersion = sinon.stub() this.document = 'mock-documents' return (this.rewound_updates = 'mock-rewound-updates') }) - describe('succesfully', function() { - beforeEach(function() { + describe('succesfully', function () { + beforeEach(function () { this.DiffManager._tryGetDocumentBeforeVersion.yields( null, this.document, @@ -230,21 +229,21 @@ describe('DiffManager', function() { ) }) - it('should call _tryGetDocumentBeforeVersion', function() { + it('should call _tryGetDocumentBeforeVersion', function () { return this.DiffManager._tryGetDocumentBeforeVersion .calledWith(this.project_id, this.doc_id, this.version) .should.equal(true) }) - return it('should call the callback with the response', function() { + return it('should call the callback with the response', function () { return this.callback .calledWith(null, this.document, this.rewound_updates) .should.equal(true) }) }) - describe('with a retry needed', function() { - beforeEach(function() { + describe('with a retry needed', function () { + beforeEach(function () { let retried = false this.DiffManager._tryGetDocumentBeforeVersion = ( project_id, @@ -270,21 +269,21 @@ describe('DiffManager', function() { ) }) - it('should call _tryGetDocumentBeforeVersion twice', function() { + it('should call _tryGetDocumentBeforeVersion twice', function () { return this.DiffManager._tryGetDocumentBeforeVersion.calledTwice.should.equal( true ) }) - return it('should call the callback with the response', function() { + return it('should call the callback with the response', function () { return this.callback .calledWith(null, this.document, this.rewound_updates) .should.equal(true) }) }) - describe('with a non-retriable error', function() { - beforeEach(function() { + describe('with a non-retriable error', function () { + beforeEach(function () { this.error = new Error('oops') this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error) return this.DiffManager.getDocumentBeforeVersion( @@ -295,19 +294,19 @@ describe('DiffManager', function() { ) }) - it('should call _tryGetDocumentBeforeVersion once', function() { + it('should call _tryGetDocumentBeforeVersion once', function () { return this.DiffManager._tryGetDocumentBeforeVersion.calledOnce.should.equal( true ) }) - return it('should call the callback with the error', function() { + return it('should call the callback with the error', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) - return describe('when retry limit is matched', function() { - beforeEach(function() { + return describe('when retry limit is matched', function () { + beforeEach(function () { this.error = new Error('oops') this.error.retry = true this.DiffManager._tryGetDocumentBeforeVersion.yields(this.error) @@ -319,20 +318,20 @@ describe('DiffManager', function() { ) }) - it('should call _tryGetDocumentBeforeVersion three times (max retries)', function() { + it('should call _tryGetDocumentBeforeVersion three times (max retries)', function () { return this.DiffManager._tryGetDocumentBeforeVersion.calledThrice.should.equal( true ) }) - return it('should call the callback with the error', function() { + return it('should call the callback with the error', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) }) - return describe('_tryGetDocumentBeforeVersion', function() { - beforeEach(function() { + return describe('_tryGetDocumentBeforeVersion', function () { + beforeEach(function () { this.content = 'hello world' // Op versions are the version they were applied to, so doc is always one version // ahead.s @@ -364,8 +363,8 @@ describe('DiffManager', function() { return (this.diff = [{ u: 'mock-diff' }]) }) - describe('with matching versions', function() { - beforeEach(function() { + describe('with matching versions', function () { + beforeEach(function () { this.DiffManager.getLatestDocAndUpdates = sinon .stub() .callsArgWith(3, null, this.content, this.version, this.updates) @@ -386,25 +385,25 @@ describe('DiffManager', function() { ) }) - it('should get the latest doc and version with all recent updates', function() { + it('should get the latest doc and version with all recent updates', function () { return this.DiffManager.getLatestDocAndUpdates .calledWith(this.project_id, this.doc_id, this.fromVersion) .should.equal(true) }) - it('should rewind the diff', function() { + it('should rewind the diff', function () { return sinon.assert.calledOnce(this.rewindUpdatesWithArgs) }) - return it('should call the callback with the rewound document and updates', function() { + return it('should call the callback with the rewound document and updates', function () { return this.callback .calledWith(null, this.rewound_content, this.updates) .should.equal(true) }) }) - describe('with mismatching versions', function() { - beforeEach(function() { + describe('with mismatching versions', function () { + beforeEach(function () { this.version = 50 this.updates = [ { op: 'mock-1', v: 40 }, @@ -421,15 +420,15 @@ describe('DiffManager', function() { ) }) - return it('should call the callback with an error with retry = true set', function() { + return it('should call the callback with an error with retry = true set', function () { this.callback.calledOnce.should.equal(true) const error = this.callback.args[0][0] return expect(error.retry).to.equal(true) }) }) - return describe('when the updates are inconsistent', function() { - beforeEach(function() { + return describe('when the updates are inconsistent', function () { + beforeEach(function () { this.DiffManager.getLatestDocAndUpdates = sinon .stub() .callsArgWith(3, null, this.content, this.version, this.updates) @@ -444,7 +443,7 @@ describe('DiffManager', function() { ) }) - return it('should call the callback with an error', function() { + return it('should call the callback with an error', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index ad0e33ca54..3eec0c1bbe 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -18,8 +18,8 @@ const { ObjectId } = require('mongojs') const MemoryStream = require('memorystream') const zlib = require('zlib') -describe('MongoAWS', function() { - beforeEach(function() { +describe('MongoAWS', function () { + beforeEach(function () { this.MongoAWS = SandboxedModule.require(modulePath, { singleOnly: true, requires: { @@ -58,8 +58,8 @@ describe('MongoAWS', function() { return (this.callback = sinon.stub()) }) - describe('archivePack', function() { - beforeEach(function(done) { + describe('archivePack', function () { + beforeEach(function (done) { this.awssdk.config = { update: sinon.stub() } this.awssdk.S3 = sinon.stub() this.S3S.WriteStream = () => MemoryStream.createWriteStream() @@ -79,13 +79,13 @@ describe('MongoAWS', function() { ) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - return describe('unArchivePack', function() { - beforeEach(function(done) { + return describe('unArchivePack', function () { + beforeEach(function (done) { return zlib.gzip('{"pack":"123"}', (err, zbuf) => { this.awssdk.config = { update: sinon.stub() } this.awssdk.S3 = sinon.stub() @@ -106,7 +106,7 @@ describe('MongoAWS', function() { }) }) - return it('should call db.docHistory.insert', function() { + return it('should call db.docHistory.insert', function () { return this.db.docHistory.insert.called.should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index a1dcaa0ee5..83f4bf04ad 100644 --- a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -16,8 +16,8 @@ const { expect } = chai const modulePath = '../../../../app/js/DocumentUpdaterManager.js' const SandboxedModule = require('sandboxed-module') -describe('DocumentUpdaterManager', function() { - beforeEach(function() { +describe('DocumentUpdaterManager', function () { + beforeEach(function () { this.DocumentUpdaterManager = SandboxedModule.require(modulePath, { requires: { request: (this.request = {}), @@ -35,9 +35,9 @@ describe('DocumentUpdaterManager', function() { return (this.version = 42) }) - describe('getDocument', function() { - describe('successfully', function() { - beforeEach(function() { + describe('getDocument', function () { + describe('successfully', function () { + beforeEach(function () { this.body = JSON.stringify({ lines: this.lines, version: this.version, @@ -53,20 +53,20 @@ describe('DocumentUpdaterManager', function() { ) }) - it('should get the document from the document updater', function() { + it('should get the document from the document updater', function () { const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}` return this.request.get.calledWith(url).should.equal(true) }) - return it('should call the callback with the content and version', function() { + return it('should call the callback with the content and version', function () { return this.callback .calledWith(null, this.lines.join('\n'), this.version) .should.equal(true) }) }) - describe('when the document updater API returns an error', function() { - beforeEach(function() { + describe('when the document updater API returns an error', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith( @@ -82,13 +82,13 @@ describe('DocumentUpdaterManager', function() { ) }) - return it('should return an error to the callback', function() { + return it('should return an error to the callback', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) - return describe('when the document updater returns a failure error code', function() { - beforeEach(function() { + return describe('when the document updater returns a failure error code', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') @@ -99,7 +99,7 @@ describe('DocumentUpdaterManager', function() { ) }) - return it('should return the callback with an error', function() { + return it('should return the callback with an error', function () { return this.callback .calledWith( sinon.match.has( @@ -112,14 +112,14 @@ describe('DocumentUpdaterManager', function() { }) }) - return describe('setDocument', function() { - beforeEach(function() { + return describe('setDocument', function () { + beforeEach(function () { this.content = 'mock content' return (this.user_id = 'user-id-123') }) - describe('successfully', function() { - beforeEach(function() { + describe('successfully', function () { + beforeEach(function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 200 }) @@ -132,7 +132,7 @@ describe('DocumentUpdaterManager', function() { ) }) - it('should set the document in the document updater', function() { + it('should set the document in the document updater', function () { const url = `${this.settings.apis.documentupdater.url}/project/${this.project_id}/doc/${this.doc_id}` return this.request.post .calledWith({ @@ -147,13 +147,13 @@ describe('DocumentUpdaterManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.calledWith(null).should.equal(true) }) }) - describe('when the document updater API returns an error', function() { - beforeEach(function() { + describe('when the document updater API returns an error', function () { + beforeEach(function () { this.request.post = sinon .stub() .callsArgWith( @@ -171,13 +171,13 @@ describe('DocumentUpdaterManager', function() { ) }) - return it('should return an error to the callback', function() { + return it('should return an error to the callback', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) - return describe('when the document updater returns a failure error code', function() { - beforeEach(function() { + return describe('when the document updater returns a failure error code', function () { + beforeEach(function () { this.request.post = sinon .stub() .callsArgWith(1, null, { statusCode: 500 }, '') @@ -190,7 +190,7 @@ describe('DocumentUpdaterManager', function() { ) }) - return it('should return the callback with an error', function() { + return it('should return the callback with an error', function () { return this.callback .calledWith( sinon.match.has( diff --git a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js index 52beb5a198..08509f6b28 100644 --- a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js @@ -16,8 +16,8 @@ const { expect } = chai const modulePath = '../../../../app/js/HttpController.js' const SandboxedModule = require('sandboxed-module') -describe('HttpController', function() { - beforeEach(function() { +describe('HttpController', function () { + beforeEach(function () { this.HttpController = SandboxedModule.require(modulePath, { singleOnly: true, requires: { @@ -37,8 +37,8 @@ describe('HttpController', function() { return (this.now = Date.now()) }) - describe('flushDoc', function() { - beforeEach(function() { + describe('flushDoc', function () { + beforeEach(function () { this.req = { params: { doc_id: this.doc_id, @@ -52,19 +52,19 @@ describe('HttpController', function() { return this.HttpController.flushDoc(this.req, this.res, this.next) }) - it('should process the updates', function() { + it('should process the updates', function () { return this.UpdatesManager.processUncompressedUpdatesWithLock .calledWith(this.project_id, this.doc_id) .should.equal(true) }) - return it('should return a success code', function() { + return it('should return a success code', function () { return this.res.sendStatus.calledWith(204).should.equal(true) }) }) - describe('flushProject', function() { - beforeEach(function() { + describe('flushProject', function () { + beforeEach(function () { this.req = { params: { project_id: this.project_id @@ -77,19 +77,19 @@ describe('HttpController', function() { return this.HttpController.flushProject(this.req, this.res, this.next) }) - it('should process the updates', function() { + it('should process the updates', function () { return this.UpdatesManager.processUncompressedUpdatesForProject .calledWith(this.project_id) .should.equal(true) }) - return it('should return a success code', function() { + return it('should return a success code', function () { return this.res.sendStatus.calledWith(204).should.equal(true) }) }) - describe('getDiff', function() { - beforeEach(function() { + describe('getDiff', function () { + beforeEach(function () { this.from = 42 this.to = 45 this.req = { @@ -108,7 +108,7 @@ describe('HttpController', function() { return this.HttpController.getDiff(this.req, this.res, this.next) }) - it('should get the diff', function() { + it('should get the diff', function () { return this.DiffManager.getDiff .calledWith( this.project_id, @@ -119,13 +119,13 @@ describe('HttpController', function() { .should.equal(true) }) - return it('should return the diff', function() { + return it('should return the diff', function () { return this.res.json.calledWith({ diff: this.diff }).should.equal(true) }) }) - describe('getUpdates', function() { - beforeEach(function() { + describe('getUpdates', function () { + beforeEach(function () { this.before = Date.now() this.nextBeforeTimestamp = this.before - 100 this.min_count = 10 @@ -146,7 +146,7 @@ describe('HttpController', function() { return this.HttpController.getUpdates(this.req, this.res, this.next) }) - it('should get the updates', function() { + it('should get the updates', function () { return this.UpdatesManager.getSummarizedProjectUpdates .calledWith(this.project_id, { before: this.before, @@ -155,7 +155,7 @@ describe('HttpController', function() { .should.equal(true) }) - return it('should return the formatted updates', function() { + return it('should return the formatted updates', function () { return this.res.json .calledWith({ updates: this.updates, @@ -165,8 +165,8 @@ describe('HttpController', function() { }) }) - return describe('RestoreManager', function() { - beforeEach(function() { + return describe('RestoreManager', function () { + beforeEach(function () { this.version = '42' this.req = { params: { @@ -184,7 +184,7 @@ describe('HttpController', function() { return this.HttpController.restore(this.req, this.res, this.next) }) - it('should restore the document', function() { + it('should restore the document', function () { return this.RestoreManager.restoreToBeforeVersion .calledWith( this.project_id, @@ -195,7 +195,7 @@ describe('HttpController', function() { .should.equal(true) }) - return it('should return a success code', function() { + return it('should return a success code', function () { return this.res.sendStatus.calledWith(204).should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js index 9c5a8fdebc..991e5ee2c6 100644 --- a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js @@ -22,8 +22,8 @@ const { expect } = chai const modulePath = '../../../../app/js/LockManager.js' const SandboxedModule = require('sandboxed-module') -describe('LockManager', function() { - beforeEach(function() { +describe('LockManager', function () { + beforeEach(function () { this.Settings = { redis: { lock: {} @@ -45,37 +45,37 @@ describe('LockManager', function() { return (this.callback = sinon.stub()) }) - describe('checkLock', function() { - describe('when the lock is taken', function() { - beforeEach(function() { + describe('checkLock', function () { + describe('when the lock is taken', function () { + beforeEach(function () { this.rclient.exists = sinon.stub().callsArgWith(1, null, '1') return this.LockManager.checkLock(this.key, this.callback) }) - it('should check the lock in redis', function() { + it('should check the lock in redis', function () { return this.rclient.exists.calledWith(this.key).should.equal(true) }) - return it('should return the callback with false', function() { + return it('should return the callback with false', function () { return this.callback.calledWith(null, false).should.equal(true) }) }) - return describe('when the lock is free', function() { - beforeEach(function() { + return describe('when the lock is free', function () { + beforeEach(function () { this.rclient.exists = sinon.stub().callsArgWith(1, null, '0') return this.LockManager.checkLock(this.key, this.callback) }) - return it('should return the callback with true', function() { + return it('should return the callback with true', function () { return this.callback.calledWith(null, true).should.equal(true) }) }) }) - describe('tryLock', function() { - describe('when the lock is taken', function() { - beforeEach(function() { + describe('tryLock', function () { + describe('when the lock is taken', function () { + beforeEach(function () { this.rclient.set = sinon.stub().callsArgWith(5, null, null) this.LockManager.randomLock = sinon .stub() @@ -83,7 +83,7 @@ describe('LockManager', function() { return this.LockManager.tryLock(this.key, this.callback) }) - it('should check the lock in redis', function() { + it('should check the lock in redis', function () { return this.rclient.set .calledWith( this.key, @@ -95,43 +95,43 @@ describe('LockManager', function() { .should.equal(true) }) - return it('should return the callback with false', function() { + return it('should return the callback with false', function () { return this.callback.calledWith(null, false).should.equal(true) }) }) - return describe('when the lock is free', function() { - beforeEach(function() { + return describe('when the lock is free', function () { + beforeEach(function () { this.rclient.set = sinon.stub().callsArgWith(5, null, 'OK') return this.LockManager.tryLock(this.key, this.callback) }) - return it('should return the callback with true', function() { + return it('should return the callback with true', function () { return this.callback.calledWith(null, true).should.equal(true) }) }) }) - describe('deleteLock', function() { - return beforeEach(function() { - beforeEach(function() { + describe('deleteLock', function () { + return beforeEach(function () { + beforeEach(function () { this.rclient.del = sinon.stub().callsArg(1) return this.LockManager.deleteLock(this.key, this.callback) }) - it('should delete the lock in redis', function() { + it('should delete the lock in redis', function () { return this.rclient.del.calledWith(key).should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) - describe('getLock', function() { - describe('when the lock is not taken', function() { - beforeEach(function(done) { + describe('getLock', function () { + describe('when the lock is not taken', function () { + beforeEach(function (done) { this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, true) return this.LockManager.getLock(this.key, (...args) => { this.callback(...Array.from(args || [])) @@ -139,26 +139,26 @@ describe('LockManager', function() { }) }) - it('should try to get the lock', function() { + it('should try to get the lock', function () { return this.LockManager.tryLock.calledWith(this.key).should.equal(true) }) - it('should only need to try once', function() { + it('should only need to try once', function () { return this.LockManager.tryLock.callCount.should.equal(1) }) - return it('should return the callback', function() { + return it('should return the callback', function () { return this.callback.calledWith(null).should.equal(true) }) }) - describe('when the lock is initially set', function() { - beforeEach(function(done) { + describe('when the lock is initially set', function () { + beforeEach(function (done) { const startTime = Date.now() this.LockManager.LOCK_TEST_INTERVAL = 5 - this.LockManager.tryLock = function(doc_id, callback) { + this.LockManager.tryLock = function (doc_id, callback) { if (callback == null) { - callback = function(error, isFree) {} + callback = function (error, isFree) {} } if (Date.now() - startTime < 100) { return callback(null, false) @@ -174,17 +174,17 @@ describe('LockManager', function() { }) }) - it('should call tryLock multiple times until free', function() { + it('should call tryLock multiple times until free', function () { return (this.LockManager.tryLock.callCount > 1).should.equal(true) }) - return it('should return the callback', function() { + return it('should return the callback', function () { return this.callback.calledWith(null).should.equal(true) }) }) - return describe('when the lock times out', function() { - beforeEach(function(done) { + return describe('when the lock times out', function () { + beforeEach(function (done) { const time = Date.now() this.LockManager.MAX_LOCK_WAIT_TIME = 5 this.LockManager.tryLock = sinon.stub().callsArgWith(1, null, false) @@ -194,7 +194,7 @@ describe('LockManager', function() { }) }) - return it('should return the callback with an error', function() { + return it('should return the callback with an error', function () { return this.callback .calledWith(sinon.match.instanceOf(Error)) .should.equal(true) @@ -202,12 +202,12 @@ describe('LockManager', function() { }) }) - return describe('runWithLock', function() { - describe('with successful run', function() { - beforeEach(function() { - this.runner = function(releaseLock) { + return describe('runWithLock', function () { + describe('with successful run', function () { + beforeEach(function () { + this.runner = function (releaseLock) { if (releaseLock == null) { - releaseLock = function(error) {} + releaseLock = function (error) {} } return releaseLock() } @@ -221,31 +221,31 @@ describe('LockManager', function() { ) }) - it('should get the lock', function() { + it('should get the lock', function () { return this.LockManager.getLock.calledWith(this.key).should.equal(true) }) - it('should run the passed function', function() { + it('should run the passed function', function () { return this.runner.called.should.equal(true) }) - it('should release the lock', function() { + it('should release the lock', function () { return this.LockManager.releaseLock .calledWith(this.key) .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('when the runner function returns an error', function() { - beforeEach(function() { + describe('when the runner function returns an error', function () { + beforeEach(function () { this.error = new Error('oops') - this.runner = releaseLock => { + this.runner = (releaseLock) => { if (releaseLock == null) { - releaseLock = function(error) {} + releaseLock = function (error) {} } return releaseLock(this.error) } @@ -259,20 +259,20 @@ describe('LockManager', function() { ) }) - it('should release the lock', function() { + it('should release the lock', function () { return this.LockManager.releaseLock .calledWith(this.key) .should.equal(true) }) - return it('should call the callback with the error', function() { + return it('should call the callback with the error', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) - return describe('releaseLock', function() { - describe('when the lock is current', function() { - beforeEach(function() { + return describe('releaseLock', function () { + describe('when the lock is current', function () { + beforeEach(function () { this.rclient.eval = sinon.stub().yields(null, 1) return this.LockManager.releaseLock( this.key, @@ -281,7 +281,7 @@ describe('LockManager', function() { ) }) - it('should clear the data from redis', function() { + it('should clear the data from redis', function () { return this.rclient.eval .calledWith( this.LockManager.unlockScript, @@ -292,13 +292,13 @@ describe('LockManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - return describe('when the lock has expired', function() { - beforeEach(function() { + return describe('when the lock has expired', function () { + beforeEach(function () { this.rclient.eval = sinon.stub().yields(null, 0) return this.LockManager.releaseLock( this.key, @@ -307,7 +307,7 @@ describe('LockManager', function() { ) }) - return it('should return an error if the lock has expired', function() { + return it('should return an error if the lock has expired', function () { return this.callback .calledWith( sinon.match.has('message', 'tried to release timed out lock') diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index 8adc8dd202..dc044cf288 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -19,8 +19,8 @@ const SandboxedModule = require('sandboxed-module') const { ObjectId } = require('mongojs') const tk = require('timekeeper') -describe('MongoManager', function() { - beforeEach(function() { +describe('MongoManager', function () { + beforeEach(function () { tk.freeze(new Date()) this.MongoManager = SandboxedModule.require(modulePath, { requires: { @@ -35,12 +35,12 @@ describe('MongoManager', function() { return (this.project_id = ObjectId().toString()) }) - afterEach(function() { + afterEach(function () { return tk.reset() }) - describe('getLastCompressedUpdate', function() { - beforeEach(function() { + describe('getLastCompressedUpdate', function () { + beforeEach(function () { this.update = 'mock-update' this.db.docHistory = {} this.db.docHistory.find = sinon.stub().returns(this.db.docHistory) @@ -57,28 +57,28 @@ describe('MongoManager', function() { ) }) - it('should find the updates for the doc', function() { + it('should find the updates for the doc', function () { return this.db.docHistory.find .calledWith({ doc_id: ObjectId(this.doc_id) }) .should.equal(true) }) - it('should limit to one result', function() { + it('should limit to one result', function () { return this.db.docHistory.limit.calledWith(1).should.equal(true) }) - it('should sort in descending version order', function() { + it('should sort in descending version order', function () { return this.db.docHistory.sort.calledWith({ v: -1 }).should.equal(true) }) - return it('should call the call back with the update', function() { + return it('should call the call back with the update', function () { return this.callback.calledWith(null, this.update).should.equal(true) }) }) - describe('peekLastCompressedUpdate', function() { - describe('when there is no last update', function() { - beforeEach(function() { + describe('peekLastCompressedUpdate', function () { + describe('when there is no last update', function () { + beforeEach(function () { this.PackManager.getLastPackFromIndex = sinon .stub() .callsArgWith(1, null, null) @@ -91,19 +91,19 @@ describe('MongoManager', function() { ) }) - it('should get the last update', function() { + it('should get the last update', function () { return this.MongoManager.getLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - return it('should call the callback with no update', function() { + return it('should call the callback with no update', function () { return this.callback.calledWith(null, null).should.equal(true) }) }) - describe('when there is an update', function() { - beforeEach(function() { + describe('when there is an update', function () { + beforeEach(function () { this.update = { _id: Object() } this.MongoManager.getLastCompressedUpdate = sinon .stub() @@ -114,19 +114,19 @@ describe('MongoManager', function() { ) }) - it('should get the last update', function() { + it('should get the last update', function () { return this.MongoManager.getLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - return it('should call the callback with the update', function() { + return it('should call the callback with the update', function () { return this.callback.calledWith(null, this.update).should.equal(true) }) }) - return describe('when there is a last update in S3', function() { - beforeEach(function() { + return describe('when there is a last update in S3', function () { + beforeEach(function () { this.update = { _id: Object(), v: 12345, v_end: 12345, inS3: true } this.PackManager.getLastPackFromIndex = sinon .stub() @@ -140,13 +140,13 @@ describe('MongoManager', function() { ) }) - it('should get the last update', function() { + it('should get the last update', function () { return this.MongoManager.getLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - return it('should call the callback with a null update and the correct version', function() { + return it('should call the callback with a null update and the correct version', function () { return this.callback .calledWith(null, null, this.update.v_end) .should.equal(true) @@ -154,8 +154,8 @@ describe('MongoManager', function() { }) }) - describe('backportProjectId', function() { - beforeEach(function() { + describe('backportProjectId', function () { + beforeEach(function () { this.db.docHistory = { update: sinon.stub().callsArg(3) } return this.MongoManager.backportProjectId( this.project_id, @@ -164,7 +164,7 @@ describe('MongoManager', function() { ) }) - it("should insert the project_id into all entries for the doc_id which don't have it set", function() { + it("should insert the project_id into all entries for the doc_id which don't have it set", function () { return this.db.docHistory.update .calledWith( { @@ -181,13 +181,13 @@ describe('MongoManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('getProjectMetaData', function() { - beforeEach(function() { + describe('getProjectMetaData', function () { + beforeEach(function () { this.metadata = { mock: 'metadata' } this.db.projectHistoryMetaData = { find: sinon.stub().callsArgWith(1, null, [this.metadata]) @@ -198,19 +198,19 @@ describe('MongoManager', function() { ) }) - it('should look up the meta data in the db', function() { + it('should look up the meta data in the db', function () { return this.db.projectHistoryMetaData.find .calledWith({ project_id: ObjectId(this.project_id) }) .should.equal(true) }) - return it('should return the metadata', function() { + return it('should return the metadata', function () { return this.callback.calledWith(null, this.metadata).should.equal(true) }) }) - return describe('setProjectMetaData', function() { - beforeEach(function() { + return describe('setProjectMetaData', function () { + beforeEach(function () { this.metadata = { mock: 'metadata' } this.db.projectHistoryMetaData = { update: sinon.stub().callsArgWith(3, null, [this.metadata]) @@ -222,7 +222,7 @@ describe('MongoManager', function() { ) }) - it('should upsert the metadata into the DB', function() { + it('should upsert the metadata into the DB', function () { return this.db.projectHistoryMetaData.update .calledWith( { @@ -238,7 +238,7 @@ describe('MongoManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index 843a010f8f..6a5708fd9f 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -24,8 +24,8 @@ const _ = require('underscore') const tk = require('timekeeper') -describe('PackManager', function() { - beforeEach(function() { +describe('PackManager', function () { + beforeEach(function () { tk.freeze(new Date()) this.PackManager = SandboxedModule.require(modulePath, { requires: { @@ -46,12 +46,12 @@ describe('PackManager', function() { return (this.PackManager.MAX_COUNT = 512) }) - afterEach(function() { + afterEach(function () { return tk.reset() }) - describe('insertCompressedUpdates', function() { - beforeEach(function() { + describe('insertCompressedUpdates', function () { + beforeEach(function () { this.lastUpdate = { _id: '12345', pack: [ @@ -72,8 +72,8 @@ describe('PackManager', function() { }) }) - describe('with no last update', function() { - beforeEach(function() { + describe('with no last update', function () { + beforeEach(function () { this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) return this.PackManager.insertCompressedUpdates( this.project_id, @@ -85,21 +85,21 @@ describe('PackManager', function() { ) }) - describe('for a small update', function() { - it('should insert the update into a new pack', function() { + describe('for a small update', function () { + it('should insert the update into a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith(this.project_id, this.doc_id, this.newUpdates, true) .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - return describe('for many small updates', function() { - beforeEach(function() { - this.newUpdates = __range__(0, 2048, true).map(i => ({ + return describe('for many small updates', function () { + beforeEach(function () { + this.newUpdates = __range__(0, 2048, true).map((i) => ({ op: `op-${i}`, meta: `meta-${i}`, v: i @@ -114,7 +114,7 @@ describe('PackManager', function() { ) }) - it('should append the initial updates to the existing pack', function() { + it('should append the initial updates to the existing pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -125,7 +125,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the first set remaining updates as a new pack', function() { + it('should insert the first set remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -136,7 +136,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the second set of remaining updates as a new pack', function() { + it('should insert the second set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -147,7 +147,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the third set of remaining updates as a new pack', function() { + it('should insert the third set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -158,7 +158,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the final set of remaining updates as a new pack', function() { + it('should insert the final set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -169,14 +169,14 @@ describe('PackManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) - describe('with an existing pack as the last update', function() { - beforeEach(function() { + describe('with an existing pack as the last update', function () { + beforeEach(function () { this.PackManager.appendUpdatesToExistingPack = sinon.stub().callsArg(5) this.PackManager.insertUpdatesIntoNewPack = sinon.stub().callsArg(4) return this.PackManager.insertCompressedUpdates( @@ -189,8 +189,8 @@ describe('PackManager', function() { ) }) - describe('for a small update', function() { - it('should append the update to the existing pack', function() { + describe('for a small update', function () { + it('should append the update to the existing pack', function () { return this.PackManager.appendUpdatesToExistingPack .calledWith( this.project_id, @@ -201,19 +201,19 @@ describe('PackManager', function() { ) .should.equal(true) }) - it('should not insert any new packs', function() { + it('should not insert any new packs', function () { return this.PackManager.insertUpdatesIntoNewPack.called.should.equal( false ) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('for many small updates', function() { - beforeEach(function() { - this.newUpdates = __range__(0, 2048, true).map(i => ({ + describe('for many small updates', function () { + beforeEach(function () { + this.newUpdates = __range__(0, 2048, true).map((i) => ({ op: `op-${i}`, meta: `meta-${i}`, v: i @@ -228,7 +228,7 @@ describe('PackManager', function() { ) }) - it('should append the initial updates to the existing pack', function() { + it('should append the initial updates to the existing pack', function () { return this.PackManager.appendUpdatesToExistingPack .calledWith( this.project_id, @@ -240,7 +240,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the first set remaining updates as a new pack', function() { + it('should insert the first set remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -251,7 +251,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the second set of remaining updates as a new pack', function() { + it('should insert the second set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -262,7 +262,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the third set of remaining updates as a new pack', function() { + it('should insert the third set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -273,7 +273,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the final set of remaining updates as a new pack', function() { + it('should insert the final set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -284,21 +284,21 @@ describe('PackManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - return describe('for many big updates', function() { - beforeEach(function() { + return describe('for many big updates', function () { + beforeEach(function () { const longString = __range__( 0, 0.75 * this.PackManager.MAX_SIZE, true ) - .map(j => 'a') + .map((j) => 'a') .join('') - this.newUpdates = [0, 1, 2, 3, 4].map(i => ({ + this.newUpdates = [0, 1, 2, 3, 4].map((i) => ({ op: `op-${i}-${longString}`, meta: `meta-${i}`, v: i @@ -313,7 +313,7 @@ describe('PackManager', function() { ) }) - it('should append the initial updates to the existing pack', function() { + it('should append the initial updates to the existing pack', function () { return this.PackManager.appendUpdatesToExistingPack .calledWith( this.project_id, @@ -325,7 +325,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the first set remaining updates as a new pack', function() { + it('should insert the first set remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -336,7 +336,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the second set of remaining updates as a new pack', function() { + it('should insert the second set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -347,7 +347,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the third set of remaining updates as a new pack', function() { + it('should insert the third set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -358,7 +358,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should insert the final set of remaining updates as a new pack', function() { + it('should insert the final set of remaining updates as a new pack', function () { return this.PackManager.insertUpdatesIntoNewPack .calledWith( this.project_id, @@ -369,15 +369,15 @@ describe('PackManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) - describe('flushCompressedUpdates', function() { - return describe('when there is no previous update', function() { - beforeEach(function() { + describe('flushCompressedUpdates', function () { + return describe('when there is no previous update', function () { + beforeEach(function () { return this.PackManager.flushCompressedUpdates( this.project_id, this.doc_id, @@ -388,8 +388,8 @@ describe('PackManager', function() { ) }) - return describe('for a small update that will expire', function() { - it('should insert the update into mongo', function() { + return describe('for a small update that will expire', function () { + it('should insert the update into mongo', function () { return this.db.docHistory.save .calledWithMatch({ pack: this.newUpdates, @@ -402,7 +402,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should set an expiry time in the future', function() { + it('should set an expiry time in the future', function () { return this.db.docHistory.save .calledWithMatch({ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) @@ -410,15 +410,15 @@ describe('PackManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) }) - describe('when there is a recent previous update in mongo that expires', function() { - beforeEach(function() { + describe('when there is a recent previous update in mongo that expires', function () { + beforeEach(function () { this.lastUpdate = { _id: '12345', pack: [ @@ -441,8 +441,8 @@ describe('PackManager', function() { ) }) - return describe('for a small update that will expire', function() { - it('should append the update in mongo', function() { + return describe('for a small update that will expire', function () { + it('should append the update in mongo', function () { return this.db.docHistory.findAndModify .calledWithMatch({ query: { _id: this.lastUpdate._id }, @@ -454,7 +454,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should set an expiry time in the future', function() { + it('should set an expiry time in the future', function () { return this.db.docHistory.findAndModify .calledWithMatch({ update: { @@ -464,14 +464,14 @@ describe('PackManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) - describe('when there is a recent previous update in mongo that expires', function() { - beforeEach(function() { + describe('when there is a recent previous update in mongo that expires', function () { + beforeEach(function () { this.PackManager.updateIndex = sinon.stub().callsArg(2) this.lastUpdate = { @@ -496,8 +496,8 @@ describe('PackManager', function() { ) }) - return describe('for a small update that will not expire', function() { - it('should insert the update into mongo', function() { + return describe('for a small update that will not expire', function () { + it('should insert the update into mongo', function () { return this.db.docHistory.save .calledWithMatch({ pack: this.newUpdates, @@ -510,20 +510,20 @@ describe('PackManager', function() { .should.equal(true) }) - it('should not set any expiry time', function() { + it('should not set any expiry time', function () { return this.db.docHistory.save .neverCalledWithMatch(sinon.match.has('expiresAt')) .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) - return describe('when there is an old previous update in mongo', function() { - beforeEach(function() { + return describe('when there is an old previous update in mongo', function () { + beforeEach(function () { this.lastUpdate = { _id: '12345', pack: [ @@ -546,8 +546,8 @@ describe('PackManager', function() { ) }) - return describe('for a small update that will expire', function() { - it('should insert the update into mongo', function() { + return describe('for a small update that will expire', function () { + it('should insert the update into mongo', function () { return this.db.docHistory.save .calledWithMatch({ pack: this.newUpdates, @@ -560,7 +560,7 @@ describe('PackManager', function() { .should.equal(true) }) - it('should set an expiry time in the future', function() { + it('should set an expiry time in the future', function () { return this.db.docHistory.save .calledWithMatch({ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) @@ -568,28 +568,28 @@ describe('PackManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) }) - describe('getOpsByVersionRange', function() {}) + describe('getOpsByVersionRange', function () {}) - describe('loadPacksByVersionRange', function() {}) + describe('loadPacksByVersionRange', function () {}) - describe('fetchPacksIfNeeded', function() {}) + describe('fetchPacksIfNeeded', function () {}) - describe('makeProjectIterator', function() {}) + describe('makeProjectIterator', function () {}) - describe('getPackById', function() {}) + describe('getPackById', function () {}) - describe('increaseTTL', function() {}) + describe('increaseTTL', function () {}) - describe('getIndex', function() {}) + describe('getIndex', function () {}) - describe('getPackFromIndex', function() {}) + describe('getPackFromIndex', function () {}) // getLastPackFromIndex: // getIndexWithKeys // initialiseIndex @@ -604,9 +604,9 @@ describe('PackManager', function() { // updateIndexIfNeeded // findUnarchivedPacks - return describe('checkArchiveNotInProgress', function() { - describe('when an archive is in progress', function() { - beforeEach(function() { + return describe('checkArchiveNotInProgress', function () { + describe('when an archive is in progress', function () { + beforeEach(function () { this.db.docHistoryIndex = { findOne: sinon.stub().callsArgWith(2, null, { inS3: false }) } @@ -617,18 +617,18 @@ describe('PackManager', function() { this.callback ) }) - it('should call the callback', function() { + it('should call the callback', function () { return this.callback.called.should.equal(true) }) - return it('should return an error', function() { + return it('should return an error', function () { return this.callback .calledWith(sinon.match.has('message')) .should.equal(true) }) }) - describe('when an archive is completed', function() { - beforeEach(function() { + describe('when an archive is completed', function () { + beforeEach(function () { this.db.docHistoryIndex = { findOne: sinon.stub().callsArgWith(2, null, { inS3: true }) } @@ -639,18 +639,18 @@ describe('PackManager', function() { this.callback ) }) - it('should call the callback', function() { + it('should call the callback', function () { return this.callback.called.should.equal(true) }) - return it('should return an error', function() { + return it('should return an error', function () { return this.callback .calledWith(sinon.match.has('message')) .should.equal(true) }) }) - return describe('when the archive has not started or completed', function() { - beforeEach(function() { + return describe('when the archive has not started or completed', function () { + beforeEach(function () { this.db.docHistoryIndex = { findOne: sinon.stub().callsArgWith(2, null, {}) } @@ -661,10 +661,10 @@ describe('PackManager', function() { this.callback ) }) - it('should call the callback with no error', function() { + it('should call the callback with no error', function () { return this.callback.called.should.equal(true) }) - return it('should return with no error', function() { + return it('should return with no error', function () { return (typeof this.callback.lastCall.args[0]).should.equal('undefined') }) }) diff --git a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js index d05bf50814..fcfc17b1bb 100644 --- a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js @@ -18,8 +18,8 @@ const { expect } = chai const modulePath = '../../../../app/js/RedisManager.js' const SandboxedModule = require('sandboxed-module') -describe('RedisManager', function() { - beforeEach(function() { +describe('RedisManager', function () { + beforeEach(function () { this.RedisManager = SandboxedModule.require(modulePath, { requires: { 'redis-sharelatex': { @@ -52,13 +52,13 @@ describe('RedisManager', function() { return (this.callback = sinon.stub()) }) - describe('getOldestDocUpdates', function() { - beforeEach(function() { + describe('getOldestDocUpdates', function () { + beforeEach(function () { this.rawUpdates = [ { v: 42, op: 'mock-op-42' }, { v: 45, op: 'mock-op-45' } ] - this.jsonUpdates = Array.from(this.rawUpdates).map(update => + this.jsonUpdates = Array.from(this.rawUpdates).map((update) => JSON.stringify(update) ) this.rclient.lrange = sinon.stub().callsArgWith(3, null, this.jsonUpdates) @@ -69,7 +69,7 @@ describe('RedisManager', function() { ) }) - it('should read the updates from redis', function() { + it('should read the updates from redis', function () { return this.rclient.lrange .calledWith( `UncompressedHistoryOps:${this.doc_id}`, @@ -79,27 +79,27 @@ describe('RedisManager', function() { .should.equal(true) }) - it('should call the callback with the unparsed ops', function() { + it('should call the callback with the unparsed ops', function () { return this.callback.calledWith(null, this.jsonUpdates).should.equal(true) }) - describe('expandDocUpdates', function() { - beforeEach(function() { + describe('expandDocUpdates', function () { + beforeEach(function () { return this.RedisManager.expandDocUpdates( this.jsonUpdates, this.callback ) }) - return it('should call the callback with the parsed ops', function() { + return it('should call the callback with the parsed ops', function () { return this.callback .calledWith(null, this.rawUpdates) .should.equal(true) }) }) - return describe('deleteAppliedDocUpdates', function() { - beforeEach(function() { + return describe('deleteAppliedDocUpdates', function () { + beforeEach(function () { this.rclient.lrem = sinon.stub() this.rclient.srem = sinon.stub() this.rclient.exec = sinon.stub().callsArgWith(0) @@ -111,7 +111,7 @@ describe('RedisManager', function() { ) }) - it('should delete the first update from redis', function() { + it('should delete the first update from redis', function () { return this.rclient.lrem .calledWith( `UncompressedHistoryOps:${this.doc_id}`, @@ -121,7 +121,7 @@ describe('RedisManager', function() { .should.equal(true) }) - it('should delete the second update from redis', function() { + it('should delete the second update from redis', function () { return this.rclient.lrem .calledWith( `UncompressedHistoryOps:${this.doc_id}`, @@ -131,20 +131,20 @@ describe('RedisManager', function() { .should.equal(true) }) - it('should delete the doc from the set of docs with history ops', function() { + it('should delete the doc from the set of docs with history ops', function () { return this.rclient.srem .calledWith(`DocsWithHistoryOps:${this.project_id}`, this.doc_id) .should.equal(true) }) - return it('should call the callback ', function() { + return it('should call the callback ', function () { return this.callback.called.should.equal(true) }) }) }) - return describe('getDocIdsWithHistoryOps', function() { - beforeEach(function() { + return describe('getDocIdsWithHistoryOps', function () { + beforeEach(function () { this.doc_ids = ['mock-id-1', 'mock-id-2'] this.rclient.smembers = sinon.stub().callsArgWith(1, null, this.doc_ids) return this.RedisManager.getDocIdsWithHistoryOps( @@ -153,13 +153,13 @@ describe('RedisManager', function() { ) }) - it('should read the doc_ids from redis', function() { + it('should read the doc_ids from redis', function () { return this.rclient.smembers .calledWith(`DocsWithHistoryOps:${this.project_id}`) .should.equal(true) }) - return it('should call the callback with the doc_ids', function() { + return it('should call the callback with the doc_ids', function () { return this.callback.calledWith(null, this.doc_ids).should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js index 86d2f92f0c..7e9c6896b0 100644 --- a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js +++ b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js @@ -16,8 +16,8 @@ const { expect } = chai const modulePath = '../../../../app/js/RestoreManager.js' const SandboxedModule = require('sandboxed-module') -describe('RestoreManager', function() { - beforeEach(function() { +describe('RestoreManager', function () { + beforeEach(function () { this.RestoreManager = SandboxedModule.require(modulePath, { requires: { 'logger-sharelatex': (this.logger = { @@ -35,8 +35,8 @@ describe('RestoreManager', function() { return (this.version = 42) }) - return describe('restoreToBeforeVersion', function() { - beforeEach(function() { + return describe('restoreToBeforeVersion', function () { + beforeEach(function () { this.content = 'mock content' this.DocumentUpdaterManager.setDocument = sinon.stub().callsArg(4) this.DiffManager.getDocumentBeforeVersion = sinon @@ -51,19 +51,19 @@ describe('RestoreManager', function() { ) }) - it('should get the content before the requested version', function() { + it('should get the content before the requested version', function () { return this.DiffManager.getDocumentBeforeVersion .calledWith(this.project_id, this.doc_id, this.version) .should.equal(true) }) - it('should set the document in the document updater', function() { + it('should set the document in the document updater', function () { return this.DocumentUpdaterManager.setDocument .calledWith(this.project_id, this.doc_id, this.content, this.user_id) .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js index 9a88db6357..110b8ed1c8 100644 --- a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js +++ b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js @@ -17,14 +17,14 @@ const modulePath = '../../../../app/js/UpdateCompressor.js' const SandboxedModule = require('sandboxed-module') const bigstring = __range__(0, 2 * 1024 * 1024, true) - .map(i => 'a') + .map((i) => 'a') .join('') const mediumstring = __range__(0, 1024 * 1024, true) - .map(j => 'a') + .map((j) => 'a') .join('') -describe('UpdateCompressor', function() { - beforeEach(function() { +describe('UpdateCompressor', function () { + beforeEach(function () { this.UpdateCompressor = SandboxedModule.require(modulePath, { requires: { '../lib/diff_match_patch': require('../../../../app/lib/diff_match_patch') @@ -36,8 +36,8 @@ describe('UpdateCompressor', function() { return (this.ts2 = Date.now() + 1000) }) - describe('convertToSingleOpUpdates', function() { - it('should split grouped updates into individual updates', function() { + describe('convertToSingleOpUpdates', function () { + it('should split grouped updates into individual updates', function () { return expect( this.UpdateCompressor.convertToSingleOpUpdates([ { @@ -77,7 +77,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should return no-op updates when the op list is empty', function() { + it('should return no-op updates when the op list is empty', function () { return expect( this.UpdateCompressor.convertToSingleOpUpdates([ { @@ -95,7 +95,7 @@ describe('UpdateCompressor', function() { ]) }) - return it('should ignore comment ops', function() { + return it('should ignore comment ops', function () { return expect( this.UpdateCompressor.convertToSingleOpUpdates([ { @@ -123,8 +123,8 @@ describe('UpdateCompressor', function() { }) }) - describe('concatUpdatesWithSameVersion', function() { - it('should concat updates with the same version', function() { + describe('concatUpdatesWithSameVersion', function () { + it('should concat updates with the same version', function () { return expect( this.UpdateCompressor.concatUpdatesWithSameVersion([ { @@ -173,7 +173,7 @@ describe('UpdateCompressor', function() { ]) }) - return it('should turn a noop into an empty op', function() { + return it('should turn a noop into an empty op', function () { return expect( this.UpdateCompressor.concatUpdatesWithSameVersion([ { @@ -196,9 +196,9 @@ describe('UpdateCompressor', function() { }) }) - describe('compress', function() { - describe('insert - insert', function() { - it('should append one insert to the other', function() { + describe('compress', function () { + describe('insert - insert', function () { + it('should append one insert to the other', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -225,7 +225,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should insert one insert inside the other', function() { + it('should insert one insert inside the other', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -252,7 +252,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should not append separated inserts', function() { + it('should not append separated inserts', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -288,7 +288,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should not append inserts that are too big (second op)', function() { + it('should not append inserts that are too big (second op)', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -324,7 +324,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should not append inserts that are too big (first op)', function() { + it('should not append inserts that are too big (first op)', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -360,7 +360,7 @@ describe('UpdateCompressor', function() { ]) }) - return it('should not append inserts that are too big (first and second op)', function() { + return it('should not append inserts that are too big (first and second op)', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -397,8 +397,8 @@ describe('UpdateCompressor', function() { }) }) - describe('delete - delete', function() { - it('should append one delete to the other', function() { + describe('delete - delete', function () { + it('should append one delete to the other', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -425,7 +425,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should insert one delete inside the other', function() { + it('should insert one delete inside the other', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -452,7 +452,7 @@ describe('UpdateCompressor', function() { ]) }) - return it('should not append separated deletes', function() { + return it('should not append separated deletes', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -489,8 +489,8 @@ describe('UpdateCompressor', function() { }) }) - describe('insert - delete', function() { - it('should undo a previous insert', function() { + describe('insert - delete', function () { + it('should undo a previous insert', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -517,7 +517,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should remove part of an insert from the middle', function() { + it('should remove part of an insert from the middle', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -544,7 +544,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should cancel out two opposite updates', function() { + it('should cancel out two opposite updates', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -571,7 +571,7 @@ describe('UpdateCompressor', function() { ]) }) - it('should not combine separated updates', function() { + it('should not combine separated updates', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -607,7 +607,7 @@ describe('UpdateCompressor', function() { ]) }) - return it('should not combine updates with overlap beyond the end', function() { + return it('should not combine updates with overlap beyond the end', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -644,8 +644,8 @@ describe('UpdateCompressor', function() { }) }) - describe('delete - insert', function() { - it('should do a diff of the content', function() { + describe('delete - insert', function () { + it('should do a diff of the content', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -681,7 +681,7 @@ describe('UpdateCompressor', function() { ]) }) - return it('should return a no-op if the delete and insert are the same', function() { + return it('should return a no-op if the delete and insert are the same', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -709,8 +709,8 @@ describe('UpdateCompressor', function() { }) }) - describe('noop - insert', function() { - return it('should leave them untouched', function() { + describe('noop - insert', function () { + return it('should leave them untouched', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -747,8 +747,8 @@ describe('UpdateCompressor', function() { }) }) - return describe('noop - delete', function() { - return it('should leave them untouched', function() { + return describe('noop - delete', function () { + return it('should leave them untouched', function () { return expect( this.UpdateCompressor.compressUpdates([ { @@ -786,9 +786,9 @@ describe('UpdateCompressor', function() { }) }) - return describe('compressRawUpdates', function() { - return describe('merging in-place with an array op', function() { - return it('should not change the existing last updates', function() { + return describe('compressRawUpdates', function () { + return describe('merging in-place with an array op', function () { + return it('should not change the existing last updates', function () { return expect( this.UpdateCompressor.compressRawUpdates( { diff --git a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js index 1a4e0f7861..5623954230 100644 --- a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js +++ b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js @@ -17,8 +17,8 @@ const modulePath = '../../../../app/js/UpdateTrimmer.js' const SandboxedModule = require('sandboxed-module') const tk = require('timekeeper') -describe('UpdateTrimmer', function() { - beforeEach(function() { +describe('UpdateTrimmer', function () { + beforeEach(function () { this.now = new Date() tk.freeze(this.now) @@ -37,12 +37,12 @@ describe('UpdateTrimmer', function() { return (this.project_id = 'mock-project-id') }) - afterEach(function() { + afterEach(function () { return tk.reset() }) - return describe('shouldTrimUpdates', function() { - beforeEach(function() { + return describe('shouldTrimUpdates', function () { + beforeEach(function () { this.metadata = {} this.details = { features: {} } this.MongoManager.getProjectMetaData = sinon @@ -55,8 +55,8 @@ describe('UpdateTrimmer', function() { .callsArgWith(1, null, this.details)) }) - describe('with preserveHistory set in the project meta data', function() { - beforeEach(function() { + describe('with preserveHistory set in the project meta data', function () { + beforeEach(function () { this.metadata.preserveHistory = true return this.UpdateTrimmer.shouldTrimUpdates( this.project_id, @@ -64,28 +64,28 @@ describe('UpdateTrimmer', function() { ) }) - it('should look up the meta data', function() { + it('should look up the meta data', function () { return this.MongoManager.getProjectMetaData .calledWith(this.project_id) .should.equal(true) }) - it('should not look up the project details', function() { + it('should not look up the project details', function () { return this.WebApiManager.getProjectDetails.called.should.equal(false) }) - return it('should return false', function() { + return it('should return false', function () { return this.callback.calledWith(null, false).should.equal(true) }) }) - describe('without preserveHistory set in the project meta data', function() { - beforeEach(function() { + describe('without preserveHistory set in the project meta data', function () { + beforeEach(function () { return (this.metadata.preserveHistory = false) }) - describe('when the project has the versioning feature', function() { - beforeEach(function() { + describe('when the project has the versioning feature', function () { + beforeEach(function () { this.details.features.versioning = true return this.UpdateTrimmer.shouldTrimUpdates( this.project_id, @@ -93,37 +93,37 @@ describe('UpdateTrimmer', function() { ) }) - it('should look up the meta data', function() { + it('should look up the meta data', function () { return this.MongoManager.getProjectMetaData .calledWith(this.project_id) .should.equal(true) }) - it('should look up the project details', function() { + it('should look up the project details', function () { return this.WebApiManager.getProjectDetails .calledWith(this.project_id) .should.equal(true) }) - it('should insert preserveHistory into the metadata', function() { + it('should insert preserveHistory into the metadata', function () { return this.MongoManager.setProjectMetaData .calledWith(this.project_id, { preserveHistory: true }) .should.equal(true) }) - it('should upgrade any existing history', function() { + it('should upgrade any existing history', function () { return this.MongoManager.upgradeHistory .calledWith(this.project_id) .should.equal(true) }) - return it('should return false', function() { + return it('should return false', function () { return this.callback.calledWith(null, false).should.equal(true) }) }) - return describe('when the project does not have the versioning feature', function() { - beforeEach(function() { + return describe('when the project does not have the versioning feature', function () { + beforeEach(function () { this.details.features.versioning = false return this.UpdateTrimmer.shouldTrimUpdates( this.project_id, @@ -131,21 +131,21 @@ describe('UpdateTrimmer', function() { ) }) - return it('should return true', function() { + return it('should return true', function () { return this.callback.calledWith(null, true).should.equal(true) }) }) }) - return describe('without any meta data', function() { - beforeEach(function() { + return describe('without any meta data', function () { + beforeEach(function () { return (this.MongoManager.getProjectMetaData = sinon .stub() .callsArgWith(1, null, null)) }) - describe('when the project has the versioning feature', function() { - beforeEach(function() { + describe('when the project has the versioning feature', function () { + beforeEach(function () { this.details.features.versioning = true return this.UpdateTrimmer.shouldTrimUpdates( this.project_id, @@ -153,25 +153,25 @@ describe('UpdateTrimmer', function() { ) }) - it('should insert preserveHistory into the metadata', function() { + it('should insert preserveHistory into the metadata', function () { return this.MongoManager.setProjectMetaData .calledWith(this.project_id, { preserveHistory: true }) .should.equal(true) }) - it('should upgrade any existing history', function() { + it('should upgrade any existing history', function () { return this.MongoManager.upgradeHistory .calledWith(this.project_id) .should.equal(true) }) - return it('should return false', function() { + return it('should return false', function () { return this.callback.calledWith(null, false).should.equal(true) }) }) - return describe('when the project does not have the versioning feature', function() { - beforeEach(function() { + return describe('when the project does not have the versioning feature', function () { + beforeEach(function () { this.details.features.versioning = false return this.UpdateTrimmer.shouldTrimUpdates( this.project_id, @@ -179,7 +179,7 @@ describe('UpdateTrimmer', function() { ) }) - return it('should return true', function() { + return it('should return true', function () { return this.callback.calledWith(null, true).should.equal(true) }) }) diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index 788821aea1..cb2af00c16 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -20,8 +20,8 @@ const { expect } = chai const modulePath = '../../../../app/js/UpdatesManager.js' const SandboxedModule = require('sandboxed-module') -describe('UpdatesManager', function() { - beforeEach(function() { +describe('UpdatesManager', function () { + beforeEach(function () { this.UpdatesManager = SandboxedModule.require(modulePath, { singleOnly: true, requires: { @@ -53,9 +53,9 @@ describe('UpdatesManager', function() { return (this.temporary = 'temp-mock') }) - describe('compressAndSaveRawUpdates', function() { - describe('when there are no raw ops', function() { - beforeEach(function() { + describe('compressAndSaveRawUpdates', function () { + describe('when there are no raw ops', function () { + beforeEach(function () { this.MongoManager.peekLastCompressedUpdate = sinon.stub() return this.UpdatesManager.compressAndSaveRawUpdates( this.project_id, @@ -66,19 +66,19 @@ describe('UpdatesManager', function() { ) }) - it('should not need to access the database', function() { + it('should not need to access the database', function () { return this.MongoManager.peekLastCompressedUpdate.called.should.equal( false ) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('when there is no compressed history to begin with', function() { - beforeEach(function() { + describe('when there is no compressed history to begin with', function () { + beforeEach(function () { this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, { v: 13, op: 'mock-op-13' } @@ -101,13 +101,13 @@ describe('UpdatesManager', function() { ) }) - it('should look at the last compressed op', function() { + it('should look at the last compressed op', function () { return this.MongoManager.peekLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - it('should save the compressed ops as a pack', function() { + it('should save the compressed ops as a pack', function () { return this.PackManager.insertCompressedUpdates .calledWith( this.project_id, @@ -119,13 +119,13 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('when the raw ops need appending to existing history', function() { - beforeEach(function() { + describe('when the raw ops need appending to existing history', function () { + beforeEach(function () { this.lastCompressedUpdate = { v: 11, op: 'compressed-op-11' } this.compressedUpdates = [ { v: 12, op: 'compressed-op-11+12' }, @@ -146,8 +146,8 @@ describe('UpdatesManager', function() { .returns(this.compressedUpdates)) }) - describe('when the raw ops start where the existing history ends', function() { - beforeEach(function() { + describe('when the raw ops start where the existing history ends', function () { + beforeEach(function () { this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, { v: 13, op: 'mock-op-13' } @@ -161,19 +161,19 @@ describe('UpdatesManager', function() { ) }) - it('should look at the last compressed op', function() { + it('should look at the last compressed op', function () { return this.MongoManager.peekLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - it('should compress the raw ops', function() { + it('should compress the raw ops', function () { return this.UpdateCompressor.compressRawUpdates .calledWith(null, this.rawUpdates) .should.equal(true) }) - it('should save the new compressed ops into a pack', function() { + it('should save the new compressed ops into a pack', function () { return this.PackManager.insertCompressedUpdates .calledWith( this.project_id, @@ -185,13 +185,13 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('when the raw ops start where the existing history ends and the history is in a pack', function() { - beforeEach(function() { + describe('when the raw ops start where the existing history ends and the history is in a pack', function () { + beforeEach(function () { this.lastCompressedUpdate = { pack: [{ v: 11, op: 'compressed-op-11' }], v: 11 @@ -217,19 +217,19 @@ describe('UpdatesManager', function() { ) }) - it('should look at the last compressed op', function() { + it('should look at the last compressed op', function () { return this.MongoManager.peekLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - it('should compress the raw ops', function() { + it('should compress the raw ops', function () { return this.UpdateCompressor.compressRawUpdates .calledWith(null, this.rawUpdates) .should.equal(true) }) - it('should save the new compressed ops into a pack', function() { + it('should save the new compressed ops into a pack', function () { return this.PackManager.insertCompressedUpdates .calledWith( this.project_id, @@ -241,13 +241,13 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('when some raw ops are passed that have already been compressed', function() { - beforeEach(function() { + describe('when some raw ops are passed that have already been compressed', function () { + beforeEach(function () { this.rawUpdates = [ { v: 10, op: 'mock-op-10' }, { v: 11, op: 'mock-op-11' }, @@ -264,15 +264,15 @@ describe('UpdatesManager', function() { ) }) - return it('should only compress the more recent raw ops', function() { + return it('should only compress the more recent raw ops', function () { return this.UpdateCompressor.compressRawUpdates .calledWith(null, this.rawUpdates.slice(-2)) .should.equal(true) }) }) - describe('when the raw ops do not follow from the last compressed op version', function() { - beforeEach(function() { + describe('when the raw ops do not follow from the last compressed op version', function () { + beforeEach(function () { this.rawUpdates = [{ v: 13, op: 'mock-op-13' }] return this.UpdatesManager.compressAndSaveRawUpdates( this.project_id, @@ -283,7 +283,7 @@ describe('UpdatesManager', function() { ) }) - it('should call the callback with an error', function() { + it('should call the callback with an error', function () { return this.callback .calledWith( sinon.match.has( @@ -294,15 +294,15 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should not insert any update into mongo', function() { + return it('should not insert any update into mongo', function () { return this.PackManager.insertCompressedUpdates.called.should.equal( false ) }) }) - return describe('when the raw ops are out of order', function() { - beforeEach(function() { + return describe('when the raw ops are out of order', function () { + beforeEach(function () { this.rawUpdates = [ { v: 13, op: 'mock-op-13' }, { v: 12, op: 'mock-op-12' } @@ -316,13 +316,13 @@ describe('UpdatesManager', function() { ) }) - it('should call the callback with an error', function() { + it('should call the callback with an error', function () { return this.callback .calledWith(sinon.match.has('message')) .should.equal(true) }) - return it('should not insert any update into mongo', function() { + return it('should not insert any update into mongo', function () { return this.PackManager.insertCompressedUpdates.called.should.equal( false ) @@ -330,8 +330,8 @@ describe('UpdatesManager', function() { }) }) - return describe('when the raw ops need appending to existing history which is in S3', function() { - beforeEach(function() { + return describe('when the raw ops need appending to existing history which is in S3', function () { + beforeEach(function () { this.lastCompressedUpdate = null this.lastVersion = 11 this.compressedUpdates = [{ v: 13, op: 'compressed-op-12' }] @@ -345,8 +345,8 @@ describe('UpdatesManager', function() { .returns(this.compressedUpdates)) }) - return describe('when the raw ops start where the existing history ends', function() { - beforeEach(function() { + return describe('when the raw ops start where the existing history ends', function () { + beforeEach(function () { this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, { v: 13, op: 'mock-op-13' } @@ -360,19 +360,19 @@ describe('UpdatesManager', function() { ) }) - it('should try to look at the last compressed op', function() { + it('should try to look at the last compressed op', function () { return this.MongoManager.peekLastCompressedUpdate .calledWith(this.doc_id) .should.equal(true) }) - it('should compress the last compressed op and the raw ops', function() { + it('should compress the last compressed op and the raw ops', function () { return this.UpdateCompressor.compressRawUpdates .calledWith(this.lastCompressedUpdate, this.rawUpdates) .should.equal(true) }) - it('should save the compressed ops', function() { + it('should save the compressed ops', function () { return this.PackManager.insertCompressedUpdates .calledWith( this.project_id, @@ -384,15 +384,15 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) }) - describe('processUncompressedUpdates', function() { - beforeEach(function() { + describe('processUncompressedUpdates', function () { + beforeEach(function () { this.UpdatesManager.compressAndSaveRawUpdates = sinon .stub() .callsArgWith(4) @@ -403,8 +403,8 @@ describe('UpdatesManager', function() { .callsArgWith(1, null, (this.temporary = 'temp mock'))) }) - describe('when there is fewer than one batch to send', function() { - beforeEach(function() { + describe('when there is fewer than one batch to send', function () { + beforeEach(function () { this.updates = ['mock-update'] this.RedisManager.getOldestDocUpdates = sinon .stub() @@ -420,13 +420,13 @@ describe('UpdatesManager', function() { ) }) - it('should get the oldest updates', function() { + it('should get the oldest updates', function () { return this.RedisManager.getOldestDocUpdates .calledWith(this.doc_id, this.UpdatesManager.REDIS_READ_BATCH_SIZE) .should.equal(true) }) - it('should compress and save the updates', function() { + it('should compress and save the updates', function () { return this.UpdatesManager.compressAndSaveRawUpdates .calledWith( this.project_id, @@ -437,19 +437,19 @@ describe('UpdatesManager', function() { .should.equal(true) }) - it('should delete the batch of uncompressed updates that was just processed', function() { + it('should delete the batch of uncompressed updates that was just processed', function () { return this.RedisManager.deleteAppliedDocUpdates .calledWith(this.project_id, this.doc_id, this.updates) .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - return describe('when there are multiple batches to send', function() { - beforeEach(function(done) { + return describe('when there are multiple batches to send', function () { + beforeEach(function (done) { this.UpdatesManager.REDIS_READ_BATCH_SIZE = 2 this.updates = [ 'mock-update-0', @@ -465,7 +465,7 @@ describe('UpdatesManager', function() { callback ) => { if (callback == null) { - callback = function(error, updates) {} + callback = function (error, updates) {} } const updates = this.redisArray.slice(0, batchSize) this.redisArray = this.redisArray.slice(batchSize) @@ -487,11 +487,11 @@ describe('UpdatesManager', function() { ) }) - it('should get the oldest updates in three batches ', function() { + it('should get the oldest updates in three batches ', function () { return this.RedisManager.getOldestDocUpdates.callCount.should.equal(3) }) - it('should compress and save the updates in batches', function() { + it('should compress and save the updates in batches', function () { this.UpdatesManager.compressAndSaveRawUpdates .calledWith( this.project_id, @@ -518,20 +518,20 @@ describe('UpdatesManager', function() { .should.equal(true) }) - it('should delete the batches of uncompressed updates', function() { + it('should delete the batches of uncompressed updates', function () { return this.RedisManager.deleteAppliedDocUpdates.callCount.should.equal( 3 ) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) }) - describe('processCompressedUpdatesWithLock', function() { - beforeEach(function() { + describe('processCompressedUpdatesWithLock', function () { + beforeEach(function () { this.UpdateTrimmer.shouldTrimUpdates = sinon .stub() .callsArgWith(1, null, (this.temporary = 'temp mock')) @@ -545,31 +545,31 @@ describe('UpdatesManager', function() { ) }) - it('should check if the updates are temporary', function() { + it('should check if the updates are temporary', function () { return this.UpdateTrimmer.shouldTrimUpdates .calledWith(this.project_id) .should.equal(true) }) - it('should backport the project id', function() { + it('should backport the project id', function () { return this.MongoManager.backportProjectId .calledWith(this.project_id, this.doc_id) .should.equal(true) }) - it('should run processUncompressedUpdates with the lock', function() { + it('should run processUncompressedUpdates with the lock', function () { return this.LockManager.runWithLock .calledWith(`HistoryLock:${this.doc_id}`) .should.equal(true) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('getDocUpdates', function() { - beforeEach(function() { + describe('getDocUpdates', function () { + beforeEach(function () { this.updates = ['mock-updates'] this.options = { to: 'mock-to', limit: 'mock-limit' } this.PackManager.getOpsByVersionRange = sinon @@ -586,13 +586,13 @@ describe('UpdatesManager', function() { ) }) - it('should process outstanding updates', function() { + it('should process outstanding updates', function () { return this.UpdatesManager.processUncompressedUpdatesWithLock .calledWith(this.project_id, this.doc_id) .should.equal(true) }) - it('should get the updates from the database', function() { + it('should get the updates from the database', function () { return this.PackManager.getOpsByVersionRange .calledWith( this.project_id, @@ -603,13 +603,13 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should return the updates', function() { + return it('should return the updates', function () { return this.callback.calledWith(null, this.updates).should.equal(true) }) }) - describe('getDocUpdatesWithUserInfo', function() { - beforeEach(function() { + describe('getDocUpdatesWithUserInfo', function () { + beforeEach(function () { this.updates = ['mock-updates'] this.options = { to: 'mock-to', limit: 'mock-limit' } this.updatesWithUserInfo = ['updates-with-user-info'] @@ -627,27 +627,27 @@ describe('UpdatesManager', function() { ) }) - it('should get the updates', function() { + it('should get the updates', function () { return this.UpdatesManager.getDocUpdates .calledWith(this.project_id, this.doc_id, this.options) .should.equal(true) }) - it('should file the updates with the user info', function() { + it('should file the updates with the user info', function () { return this.UpdatesManager.fillUserInfo .calledWith(this.updates) .should.equal(true) }) - return it('should return the updates with the filled details', function() { + return it('should return the updates with the filled details', function () { return this.callback .calledWith(null, this.updatesWithUserInfo) .should.equal(true) }) }) - describe('processUncompressedUpdatesForProject', function() { - beforeEach(function(done) { + describe('processUncompressedUpdatesForProject', function () { + beforeEach(function (done) { this.doc_ids = ['mock-id-1', 'mock-id-2'] this.UpdateTrimmer.shouldTrimUpdates = sinon .stub() @@ -668,27 +668,27 @@ describe('UpdatesManager', function() { ) }) - it('should get all the docs with history ops', function() { + it('should get all the docs with history ops', function () { return this.RedisManager.getDocIdsWithHistoryOps .calledWith(this.project_id) .should.equal(true) }) - it('should process the doc ops for the each doc_id', function() { - return Array.from(this.doc_ids).map(doc_id => + it('should process the doc ops for the each doc_id', function () { + return Array.from(this.doc_ids).map((doc_id) => this.UpdatesManager._processUncompressedUpdatesForDocWithLock .calledWith(this.project_id, doc_id, this.temporary) .should.equal(true) ) }) - return it('should call the callback', function() { + return it('should call the callback', function () { return this.callback.called.should.equal(true) }) }) - describe('getSummarizedProjectUpdates', function() { - beforeEach(function() { + describe('getSummarizedProjectUpdates', function () { + beforeEach(function () { this.updates = [ { doc_id: 123, @@ -707,7 +707,7 @@ describe('UpdatesManager', function() { this.updatesWithUserInfo = ['updates-with-user-info'] this.done_state = false this.iterator = { - next: cb => { + next: (cb) => { this.done_state = true return cb(null, this.updates) }, @@ -731,25 +731,25 @@ describe('UpdatesManager', function() { ) }) - it('should process any outstanding updates', function() { + it('should process any outstanding updates', function () { return this.UpdatesManager.processUncompressedUpdatesForProject .calledWith(this.project_id) .should.equal(true) }) - it('should get the updates', function() { + it('should get the updates', function () { return this.PackManager.makeProjectIterator .calledWith(this.project_id, this.options.before) .should.equal(true) }) - it('should fill the updates with the user info', function() { + it('should fill the updates with the user info', function () { return this.UpdatesManager.fillSummarizedUserInfo .calledWith(this.summarizedUpdates) .should.equal(true) }) - return it('should return the updates with the filled details', function() { + return it('should return the updates with the filled details', function () { return this.callback .calledWith(null, this.updatesWithUserInfo) .should.equal(true) @@ -861,9 +861,9 @@ describe('UpdatesManager', function() { // it "should call the callback with the updates", -> // @callback.calledWith(null, @updates, null).should.equal true - describe('fillUserInfo', function() { - describe('with valid users', function() { - beforeEach(function(done) { + describe('fillUserInfo', function () { + describe('with valid users', function () { + beforeEach(function (done) { const { ObjectId } = require('mongojs') this.user_id_1 = ObjectId().toString() this.user_id_2 = ObjectId().toString() @@ -893,7 +893,7 @@ describe('UpdatesManager', function() { this.WebApiManager.getUserInfo = (user_id, callback) => { if (callback == null) { - callback = function(error, userInfo) {} + callback = function (error, userInfo) {} } return callback(null, this.user_info[user_id]) } @@ -908,7 +908,7 @@ describe('UpdatesManager', function() { ) }) - it('should only call getUserInfo once for each user_id', function() { + it('should only call getUserInfo once for each user_id', function () { this.WebApiManager.getUserInfo.calledTwice.should.equal(true) this.WebApiManager.getUserInfo .calledWith(this.user_id_1) @@ -918,7 +918,7 @@ describe('UpdatesManager', function() { .should.equal(true) }) - return it('should return the updates with the user info filled', function() { + return it('should return the updates with the user info filled', function () { return expect(this.results).to.deep.equal([ { meta: { @@ -948,8 +948,8 @@ describe('UpdatesManager', function() { }) }) - return describe('with invalid user ids', function() { - beforeEach(function(done) { + return describe('with invalid user ids', function () { + beforeEach(function (done) { this.updates = [ { meta: { @@ -966,7 +966,7 @@ describe('UpdatesManager', function() { ] this.WebApiManager.getUserInfo = (user_id, callback) => { if (callback == null) { - callback = function(error, userInfo) {} + callback = function (error, userInfo) {} } return callback(null, this.user_info[user_id]) } @@ -981,11 +981,11 @@ describe('UpdatesManager', function() { ) }) - it('should not call getUserInfo', function() { + it('should not call getUserInfo', function () { return this.WebApiManager.getUserInfo.called.should.equal(false) }) - return it('should return the updates without the user info filled', function() { + return it('should return the updates without the user info filled', function () { return expect(this.results).to.deep.equal([ { meta: {}, @@ -1000,14 +1000,14 @@ describe('UpdatesManager', function() { }) }) - return describe('_summarizeUpdates', function() { - beforeEach(function() { + return describe('_summarizeUpdates', function () { + beforeEach(function () { this.now = Date.now() this.user_1 = { id: 'mock-user-1' } return (this.user_2 = { id: 'mock-user-2' }) }) - it('should concat updates that are close in time', function() { + it('should concat updates that are close in time', function () { const result = this.UpdatesManager._summarizeUpdates([ { doc_id: 'doc-id-1', @@ -1046,7 +1046,7 @@ describe('UpdatesManager', function() { ]) }) - it('should leave updates that are far apart in time', function() { + it('should leave updates that are far apart in time', function () { const oneDay = 1000 * 60 * 60 * 24 const result = this.UpdatesManager._summarizeUpdates([ { @@ -1098,7 +1098,7 @@ describe('UpdatesManager', function() { ]) }) - it('should concat onto existing summarized updates', function() { + it('should concat onto existing summarized updates', function () { const result = this.UpdatesManager._summarizeUpdates( [ { @@ -1157,7 +1157,7 @@ describe('UpdatesManager', function() { ]) }) - it('should include null user values', function() { + it('should include null user values', function () { const result = this.UpdatesManager._summarizeUpdates([ { doc_id: 'doc-id-1', @@ -1195,7 +1195,7 @@ describe('UpdatesManager', function() { ]) }) - it('should include null user values, when the null is earlier in the updates list', function() { + it('should include null user values, when the null is earlier in the updates list', function () { const result = this.UpdatesManager._summarizeUpdates([ { doc_id: 'doc-id-1', @@ -1233,7 +1233,7 @@ describe('UpdatesManager', function() { ]) }) - it('should roll several null user values into one', function() { + it('should roll several null user values into one', function () { const result = this.UpdatesManager._summarizeUpdates([ { doc_id: 'doc-id-1', @@ -1280,7 +1280,7 @@ describe('UpdatesManager', function() { ]) }) - return it('should split updates before a big delete', function() { + return it('should split updates before a big delete', function () { const result = this.UpdatesManager._summarizeUpdates([ { doc_id: 'doc-id-1', diff --git a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js index d62466b0b9..5aca308be2 100644 --- a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js @@ -16,8 +16,8 @@ const { expect } = chai const modulePath = '../../../../app/js/WebApiManager.js' const SandboxedModule = require('sandboxed-module') -describe('WebApiManager', function() { - beforeEach(function() { +describe('WebApiManager', function () { + beforeEach(function () { this.WebApiManager = SandboxedModule.require(modulePath, { requires: { requestretry: (this.request = {}), @@ -49,9 +49,9 @@ describe('WebApiManager', function() { return (this.project = { features: 'mock-features' }) }) - describe('getUserInfo', function() { - describe('successfully', function() { - beforeEach(function() { + describe('getUserInfo', function () { + describe('successfully', function () { + beforeEach(function () { this.body = JSON.stringify(this.user_info) this.request.get = sinon .stub() @@ -59,7 +59,7 @@ describe('WebApiManager', function() { return this.WebApiManager.getUserInfo(this.user_id, this.callback) }) - it('should get the user from the web api', function() { + it('should get the user from the web api', function () { return this.request.get .calledWithMatch({ url: `${this.settings.apis.web.url}/user/${this.user_id}/personal_info`, @@ -72,7 +72,7 @@ describe('WebApiManager', function() { .should.equal(true) }) - return it('should call the callback with only the email, id and names', function() { + return it('should call the callback with only the email, id and names', function () { return this.callback .calledWith(null, { id: this.user_id, @@ -84,8 +84,8 @@ describe('WebApiManager', function() { }) }) - describe('when the web API returns an error', function() { - beforeEach(function() { + describe('when the web API returns an error', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith( @@ -97,20 +97,20 @@ describe('WebApiManager', function() { return this.WebApiManager.getUserInfo(this.user_id, this.callback) }) - return it('should return an error to the callback', function() { + return it('should return an error to the callback', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) - describe('when the web returns a failure error code', function() { - beforeEach(function() { + describe('when the web returns a failure error code', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500, attempts: 42 }, '') return this.WebApiManager.getUserInfo(this.user_id, this.callback) }) - return it('should return the callback with an error', function() { + return it('should return the callback with an error', function () { return this.callback .calledWith( sinon.match.has( @@ -122,23 +122,23 @@ describe('WebApiManager', function() { }) }) - return describe('when the user cannot be found', function() { - beforeEach(function() { + return describe('when the user cannot be found', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 404 }, 'nothing') return this.WebApiManager.getUserInfo(this.user_id, this.callback) }) - return it('should return a null value', function() { + return it('should return a null value', function () { return this.callback.calledWith(null, null).should.equal(true) }) }) }) - return describe('getProjectDetails', function() { - describe('successfully', function() { - beforeEach(function() { + return describe('getProjectDetails', function () { + describe('successfully', function () { + beforeEach(function () { this.body = JSON.stringify(this.project) this.request.get = sinon .stub() @@ -149,7 +149,7 @@ describe('WebApiManager', function() { ) }) - it('should get the project from the web api', function() { + it('should get the project from the web api', function () { return this.request.get .calledWithMatch({ url: `${this.settings.apis.web.url}/project/${this.project_id}/details`, @@ -162,13 +162,13 @@ describe('WebApiManager', function() { .should.equal(true) }) - return it('should call the callback with the project', function() { + return it('should call the callback with the project', function () { return this.callback.calledWith(null, this.project).should.equal(true) }) }) - describe('when the web API returns an error', function() { - beforeEach(function() { + describe('when the web API returns an error', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith( @@ -183,13 +183,13 @@ describe('WebApiManager', function() { ) }) - return it('should return an error to the callback', function() { + return it('should return an error to the callback', function () { return this.callback.calledWith(this.error).should.equal(true) }) }) - return describe('when the web returns a failure error code', function() { - beforeEach(function() { + return describe('when the web returns a failure error code', function () { + beforeEach(function () { this.request.get = sinon .stub() .callsArgWith(1, null, { statusCode: 500, attempts: 42 }, '') @@ -199,7 +199,7 @@ describe('WebApiManager', function() { ) }) - return it('should return the callback with an error', function() { + return it('should return the callback with an error', function () { return this.callback .calledWith( sinon.match.has( From 8335f31d6ce758209268e2aa7f0073b65c8079e7 Mon Sep 17 00:00:00 2001 From: Tim Alby Date: Thu, 4 Jun 2020 10:37:43 +0200 Subject: [PATCH 504/549] fix linting --- services/track-changes/.eslintignore | 1 + services/track-changes/app.js | 15 +++++++++------ .../track-changes/config/settings.defaults.js | 16 ++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) create mode 100644 services/track-changes/.eslintignore diff --git a/services/track-changes/.eslintignore b/services/track-changes/.eslintignore new file mode 100644 index 0000000000..c59a33a4df --- /dev/null +++ b/services/track-changes/.eslintignore @@ -0,0 +1 @@ +app/lib/diff_match_patch.js diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 8d0890e988..df2db8a79a 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -42,7 +42,7 @@ const Path = require('path') Metrics.memory.monitor(logger) -const child_process = require('child_process') +const childProcess = require('child_process') const HttpController = require('./app/js/HttpController') const express = require('express') @@ -84,11 +84,14 @@ app.post('/pack', function (req, res, next) { return res.send('pack already running') } else { logger.log('running pack') - packWorker = child_process.fork(__dirname + '/app/js/PackWorker.js', [ - req.query.limit || 1000, - req.query.delay || 1000, - req.query.timeout || 30 * 60 * 1000 - ]) + packWorker = childProcess.fork( + Path.join(__dirname, '/app/js/PackWorker.js'), + [ + req.query.limit || 1000, + req.query.delay || 1000, + req.query.timeout || 30 * 60 * 1000 + ] + ) packWorker.on('exit', function (code, signal) { logger.log({ code, signal }, 'history auto pack exited') return (packWorker = null) diff --git a/services/track-changes/config/settings.defaults.js b/services/track-changes/config/settings.defaults.js index 2cee6df6af..426592ed26 100755 --- a/services/track-changes/config/settings.defaults.js +++ b/services/track-changes/config/settings.defaults.js @@ -40,11 +40,11 @@ module.exports = { port: process.env.REDIS_PORT || 6379, password: process.env.REDIS_PASSWORD || '', key_schema: { - historyLock({ doc_id }) { - return `HistoryLock:{${doc_id}}` + historyLock({ doc_id: docId }) { + return `HistoryLock:{${docId}}` }, - historyIndexLock({ project_id }) { - return `HistoryIndexLock:{${project_id}}` + historyIndexLock({ project_id: projectId }) { + return `HistoryIndexLock:{${projectId}}` } } }, @@ -53,11 +53,11 @@ module.exports = { port: process.env.REDIS_PORT || 6379, password: process.env.REDIS_PASSWORD || '', key_schema: { - uncompressedHistoryOps({ doc_id }) { - return `UncompressedHistoryOps:{${doc_id}}` + uncompressedHistoryOps({ doc_id: docId }) { + return `UncompressedHistoryOps:{${docId}}` }, - docsWithHistoryOps({ project_id }) { - return `DocsWithHistoryOps:{${project_id}}` + docsWithHistoryOps({ project_id: projectId }) { + return `DocsWithHistoryOps:{${projectId}}` } } } From 472d65d15c3c08c6ff83f37a90ede89944365fdb Mon Sep 17 00:00:00 2001 From: Tim Alby Date: Thu, 4 Jun 2020 10:39:35 +0200 Subject: [PATCH 505/549] Revert "[misc] make: ignore a lint/format task failure" This reverts commit 85f3dc661b17f8b99aa073c376aa2aa5af8998af. --- services/track-changes/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index b01ebbcbe0..e1b4843fea 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -17,13 +17,13 @@ clean: docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) format: - $(DOCKER_COMPOSE) run --rm test_unit npm run format || true + $(DOCKER_COMPOSE) run --rm test_unit npm run format format_fix: $(DOCKER_COMPOSE) run --rm test_unit npm run format:fix lint: - $(DOCKER_COMPOSE) run --rm test_unit npm run lint || true + $(DOCKER_COMPOSE) run --rm test_unit npm run lint test: format lint test_unit test_acceptance From 16922bf17764d4cf94b5e0e24cfd9931718d6a1e Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Wed, 17 Jun 2020 16:36:57 +0200 Subject: [PATCH 506/549] Pinned adobe/s3mock docker image --- services/track-changes/docker-compose.ci.yml | 2 +- services/track-changes/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 4e41056770..99232575ca 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -52,7 +52,7 @@ services: mongo: image: mongo:3.6 s3: - image: adobe/s3mock + image: adobe/s3mock:2.1.20 environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket healthcheck: diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 27652dedab..85d9c9e915 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -51,7 +51,7 @@ services: image: mongo:3.6 s3: - image: adobe/s3mock + image: adobe/s3mock:2.1.20 environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket healthcheck: From 105335c62ae7910bf2e9cb1b53d4c0a9b6b76673 Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Fri, 19 Jun 2020 19:19:15 +0200 Subject: [PATCH 507/549] Remove source maps --- services/track-changes/app.js.map | 10 ---------- services/track-changes/app/js/DiffGenerator.js.map | 10 ---------- services/track-changes/app/js/DiffManager.js.map | 10 ---------- .../track-changes/app/js/DocumentUpdaterManager.js.map | 10 ---------- services/track-changes/app/js/HealthChecker.js.map | 10 ---------- services/track-changes/app/js/HttpController.js.map | 10 ---------- services/track-changes/app/js/LockManager.js.map | 10 ---------- services/track-changes/app/js/MongoAWS.js.map | 10 ---------- services/track-changes/app/js/MongoManager.js.map | 10 ---------- services/track-changes/app/js/PackManager.js.map | 10 ---------- services/track-changes/app/js/PackWorker.js.map | 10 ---------- services/track-changes/app/js/ProjectIterator.js.map | 10 ---------- services/track-changes/app/js/RedisManager.js.map | 10 ---------- services/track-changes/app/js/RestoreManager.js.map | 10 ---------- services/track-changes/app/js/UpdateCompressor.js.map | 10 ---------- services/track-changes/app/js/UpdateTrimmer.js.map | 10 ---------- services/track-changes/app/js/UpdatesManager.js.map | 10 ---------- services/track-changes/app/js/WebApiManager.js.map | 10 ---------- services/track-changes/app/js/mongojs.js.map | 10 ---------- 19 files changed, 190 deletions(-) delete mode 100644 services/track-changes/app.js.map delete mode 100644 services/track-changes/app/js/DiffGenerator.js.map delete mode 100644 services/track-changes/app/js/DiffManager.js.map delete mode 100644 services/track-changes/app/js/DocumentUpdaterManager.js.map delete mode 100644 services/track-changes/app/js/HealthChecker.js.map delete mode 100644 services/track-changes/app/js/HttpController.js.map delete mode 100644 services/track-changes/app/js/LockManager.js.map delete mode 100644 services/track-changes/app/js/MongoAWS.js.map delete mode 100644 services/track-changes/app/js/MongoManager.js.map delete mode 100644 services/track-changes/app/js/PackManager.js.map delete mode 100644 services/track-changes/app/js/PackWorker.js.map delete mode 100644 services/track-changes/app/js/ProjectIterator.js.map delete mode 100644 services/track-changes/app/js/RedisManager.js.map delete mode 100644 services/track-changes/app/js/RestoreManager.js.map delete mode 100644 services/track-changes/app/js/UpdateCompressor.js.map delete mode 100644 services/track-changes/app/js/UpdateTrimmer.js.map delete mode 100644 services/track-changes/app/js/UpdatesManager.js.map delete mode 100644 services/track-changes/app/js/WebApiManager.js.map delete mode 100644 services/track-changes/app/js/mongojs.js.map diff --git a/services/track-changes/app.js.map b/services/track-changes/app.js.map deleted file mode 100644 index c408f74302..0000000000 --- a/services/track-changes/app.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "app.js", - "sourceRoot": "", - "sources": [ - "app.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,OAAO,CAAC,UAAR,CAAmB,eAAnB;;EACA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,kBAAA,GAAqB,MAAM,CAAC,UAAP,CAAkB,eAAlB,CAAkC,CAAC;;EAExD,IAAG,4DAAH;IACC,MAAM,CAAC,wBAAP,CAAgC,QAAQ,CAAC,MAAM,CAAC,GAAhD,EADD;;;EAIA,UAAA,GAAa,SAAC,OAAD;WACX,IAAI,CAAC,KAAL,CACC,IAAI,CAAC,SAAL,CAAe,OAAf,EAAwB,SAAC,GAAD,EAAM,KAAN;AACvB,UAAA;MAAA,IAAG,OAAO,KAAP,KAAgB,QAAhB,IAA4B,CAAC,GAAA,GAAM,KAAK,CAAC,MAAb,CAAA,GAAuB,EAAtD;AACC,eAAO,KAAK,CAAC,MAAN,CAAa,CAAb,EAAe,EAAf,CAAA,GAAqB,CAAA,wBAAA,GAAyB,GAAzB,GAA6B,gBAA7B,CAArB,GAAoE,KAAK,CAAC,MAAN,CAAa,CAAC,EAAd,EAD5E;OAAA,MAAA;AAGC,eAAO,MAHR;;IADuB,CAAxB,CADD;EADW;;EASb,kBAAkB,CAAC,cAAnB,CAAkC;IACjC,SAAA,EAAW,UADsB;IAEjC,UAAA,EAAY,UAFqB;IAGjC,UAAA,EAAY,UAHqB;IAIjC,UAAA,EAAY,UAJqB;GAAlC;;EAOA,IAAA,GAAO,OAAA,CAAQ,MAAR;;EAEP,OAAO,CAAC,MAAM,CAAC,OAAf,CAAuB,MAAvB;;EAEA,aAAA,GAAgB,OAAA,CAAQ,eAAR;;EAEhB,cAAA,GAAiB,OAAA,CAAQ,yBAAR;;EACjB,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,GAAA,GAAM,OAAA,CAAA;;EAEN,GAAG,CAAC,GAAJ,CAAQ,OAAO,CAAC,IAAI,CAAC,OAAb,CAAqB,MAArB,CAAR;;EAEA,OAAO,CAAC,kBAAR,CAA2B,GAA3B;;EAEA,GAAG,CAAC,IAAJ,CAAS,wCAAT,EAAmD,cAAc,CAAC,QAAlE;;EAEA,GAAG,CAAC,GAAJ,CAAQ,uCAAR,EAAiD,cAAc,CAAC,OAAhE;;EAEA,GAAG,CAAC,GAAJ,CAAQ,wCAAR,EAAkD,cAAc,CAAC,QAAjE;;EAEA,GAAG,CAAC,GAAJ,CAAQ,8BAAR,EAAwC,cAAc,CAAC,UAAvD;;EAEA,GAAG,CAAC,IAAJ,CAAS,4BAAT,EAAuC,cAAc,CAAC,YAAtD;;EAEA,GAAG,CAAC,IAAJ,CAAS,2DAAT,EAAsE,cAAc,CAAC,OAArF;;EAEA,GAAG,CAAC,IAAJ,CAAU,uCAAV,EAAmD,cAAc,CAAC,cAAlE;;EACA,GAAG,CAAC,IAAJ,CAAU,uCAAV,EAAmD,cAAc,CAAC,cAAlE;;EAEA,GAAG,CAAC,IAAJ,CAAS,YAAT,EAAuB,cAAc,CAAC,QAAtC;;EACA,GAAG,CAAC,IAAJ,CAAS,iBAAT,EAA4B,cAAc,CAAC,oBAA3C;;EAEA,UAAA,GAAa;;EAEb,GAAG,CAAC,IAAJ,CAAS,OAAT,EAAkB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;IACjB,IAAG,kBAAH;aACC,GAAG,CAAC,IAAJ,CAAS,sBAAT,EADD;KAAA,MAAA;MAGC,MAAM,CAAC,GAAP,CAAW,cAAX;MACA,UAAA,GAAa,aAAa,CAAC,IAAd,CAAmB,SAAA,GAAY,uBAA/B,EACZ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAV,IAAmB,IAApB,EAA0B,GAAG,CAAC,KAAK,CAAC,KAAV,IAAmB,IAA7C,EAAmD,GAAG,CAAC,KAAK,CAAC,OAAV,IAAqB,EAAA,GAAG,EAAH,GAAM,IAA9E,CADY;MAEb,UAAU,CAAC,EAAX,CAAc,MAAd,EAAsB,SAAC,IAAD,EAAO,MAAP;QACrB,MAAM,CAAC,GAAP,CAAW;UAAC,MAAA,IAAD;UAAO,QAAA,MAAP;SAAX,EAA2B,0BAA3B;eACA,UAAA,GAAa;MAFQ,CAAtB;aAGA,GAAG,CAAC,IAAJ,CAAS,cAAT,EATD;;EADiB,CAAlB;;EAYA,GAAG,CAAC,GAAJ,CAAQ,SAAR,EAAmB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;WAClB,GAAG,CAAC,IAAJ,CAAS,wBAAT;EADkB,CAAnB;;EAGA,GAAG,CAAC,GAAJ,CAAQ,OAAR,EAAiB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AAChB,UAAM,IAAI,KAAJ,CAAU,kBAAV;EADU,CAAjB;;EAGA,GAAG,CAAC,GAAJ,CAAQ,aAAR,EAAuB,cAAc,CAAC,SAAtC;;EAEA,GAAG,CAAC,GAAJ,CAAQ,eAAR,EAA0B,cAAc,CAAC,WAAzC;;EAEA,QAAA,GAAW,OAAA,CAAQ,aAAR;;EACX,GAAG,CAAC,GAAJ,CAAQ,UAAR,EAAoB,SAAC,GAAD,EAAM,GAAN;AACnB,QAAA;IAAA,IAAA,GAAO,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,IAAV,IAAkB,MAA3B;IACP,QAAQ,CAAC,cAAT,CAAwB,MAAxB;WACA,UAAA,CAAW,SAAA;AACV,UAAA;MAAA,OAAA,GAAU,QAAQ,CAAC,aAAT,CAAuB,MAAvB;aACV,GAAG,CAAC,IAAJ,CAAS,OAAT;IAFU,CAAX,EAGE,IAHF;EAHmB,CAApB;;EAQA,GAAG,CAAC,GAAJ,CAAQ,SAAC,KAAD,EAAQ,GAAR,EAAa,GAAb,EAAkB,IAAlB;IACP,MAAM,CAAC,KAAP,CAAa;MAAA,GAAA,EAAK,KAAL;MAAY,GAAA,EAAK,GAAjB;KAAb,EAAmC,2BAAnC;WACA,GAAG,CAAC,IAAJ,CAAS,GAAT;EAFO,CAAR;;EAIA,IAAA,kFAAsC,CAAE,uBAAjC,IAAyC;;EAChD,IAAA,kFAAsC,CAAE,uBAAjC,IAAyC;;EAEhD,IAAG,CAAC,MAAM,CAAC,MAAX;IACC,GAAG,CAAC,MAAJ,CAAW,IAAX,EAAiB,IAAjB,EAAuB,SAAC,KAAD;MACtB,IAAG,aAAH;eACC,MAAM,CAAC,KAAP,CAAa;UAAA,GAAA,EAAK,KAAL;SAAb,EAAyB,sCAAzB,EADD;OAAA,MAAA;eAGC,MAAM,CAAC,IAAP,CAAY,yCAAA,GAA0C,IAA1C,GAA+C,GAA/C,GAAkD,IAA9D,EAHD;;IADsB,CAAvB,EADD;;;EAOA,MAAM,CAAC,OAAP,GAAiB;AAzGjB" -} \ No newline at end of file diff --git a/services/track-changes/app/js/DiffGenerator.js.map b/services/track-changes/app/js/DiffGenerator.js.map deleted file mode 100644 index 2394cfe7df..0000000000 --- a/services/track-changes/app/js/DiffGenerator.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "DiffGenerator.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/DiffGenerator.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,gBAAA,GAAmB,SAAC,OAAD;AAClB,QAAA;IAAA,KAAA,GAAQ,IAAI,KAAJ,CAAU,OAAV;IACR,KAAK,CAAC,IAAN,GAAa;IACb,KAAK,CAAC,SAAN,GAAkB,gBAAgB,CAAC;AACnC,WAAO;EAJW;;EAKnB,gBAAgB,CAAC,SAAS,CAAC,SAA3B,GAAuC,KAAK,CAAC;;EAE7C,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,aAAA,GAChB;IAAA,gBAAA,EAAkB,gBAAlB;IAEA,YAAA,EAAc,SAAC,OAAD,EAAU,MAAV;AACb,UAAA;AAAA;AAAA,WAAA,2CAAA;;YAAkC,EAAE,CAAC,MAAH,KAAe;AAChD;YACC,OAAA,GAAU,aAAa,CAAC,QAAd,CAAuB,OAAvB,EAAgC,EAAhC,EADX;WAAA,cAAA;YAEM;YACL,IAAG,CAAA,YAAa,gBAAb,IAAkC,CAAA,CAAA,GAAI,MAAM,CAAC,EAAE,CAAC,MAAV,GAAmB,CAAvB,CAArC;cAGC,MAAM,CAAC,KAAP,CAAa;gBAAC,GAAA,EAAK,CAAN;gBAAS,QAAA,MAAT;gBAAiB,EAAA,EAAI,IAAI,CAAC,SAAL,CAAe,EAAf,CAArB;eAAb,EAAuD,sBAAvD;cACA,EAAE,CAAC,MAAH,GAAY,KAJb;aAAA,MAAA;AAMC,oBAAM,EANP;aAHD;;;AADD;AAWA,aAAO;IAZM,CAFd;IAgBA,QAAA,EAAU,SAAC,OAAD,EAAU,EAAV;AACT,UAAA;MAAA,IAAG,YAAH;QAMC,CAAA,GAAI,EAAE,CAAC;QACP,KAAA,GAAQ,OAAO,CAAC,MAAR,GAAiB,EAAE,CAAC,CAAC,CAAC;QAC9B,IAAG,CAAA,GAAI,KAAP;UACC,MAAM,CAAC,IAAP,CAAY;YAAC,OAAA,KAAD;YAAQ,GAAA,CAAR;WAAZ,EAAwB,uCAAxB;UACA,CAAA,GAAI,MAFL;;QAIA,eAAA,GAAkB,OAAO,CAAC,KAAR,CAAc,CAAd,EAAiB,CAAA,GAAI,EAAE,CAAC,CAAC,CAAC,MAA1B;QAClB,IAAG,EAAE,CAAC,CAAH,KAAQ,eAAX;AACC,gBAAM,IAAI,gBAAJ,CACL,qBAAA,GAAsB,EAAE,CAAC,CAAzB,GAA2B,yCAA3B,GAAoE,eAApE,GAAoF,GAD/E,EADP;;AAKA,eAAO,OAAO,CAAC,KAAR,CAAc,CAAd,EAAiB,CAAjB,CAAA,GAAsB,OAAO,CAAC,KAAR,CAAc,CAAA,GAAI,EAAE,CAAC,CAAC,CAAC,MAAvB,EAlB9B;OAAA,MAoBK,IAAG,YAAH;AACJ,eAAO,OAAO,CAAC,KAAR,CAAc,CAAd,EAAiB,EAAE,CAAC,CAApB,CAAA,GAAyB,EAAE,CAAC,CAA5B,GAAgC,OAAO,CAAC,KAAR,CAAc,EAAE,CAAC,CAAjB,EADnC;OAAA,MAAA;AAIJ,eAAO,QAJH;;IArBI,CAhBV;IA2CA,aAAA,EAAe,SAAC,OAAD,EAAU,OAAV;AACd,UAAA;AAAA;AAAA,WAAA,qCAAA;;AACC;UACC,OAAA,GAAU,aAAa,CAAC,YAAd,CAA2B,OAA3B,EAAoC,MAApC,EADX;SAAA,cAAA;UAEM;UACL,CAAC,CAAC,gBAAF,GAAqB;AACrB,gBAAM,EAJP;;AADD;AAMA,aAAO;IAPO,CA3Cf;IAoDA,SAAA,EAAW,SAAC,cAAD,EAAiB,OAAjB;AACV,UAAA;MAAA,IAAA,GAAO;QAAE;UAAA,CAAA,EAAG,cAAH;SAAF;;AACP,WAAA,yCAAA;;QACC,IAAA,GAAO,aAAa,CAAC,iBAAd,CAAgC,IAAhC,EAAsC,MAAtC;AADR;MAEA,IAAA,GAAO,aAAa,CAAC,YAAd,CAA2B,IAA3B;AACP,aAAO;IALG,CApDX;IA2DA,YAAA,EAAc,SAAC,IAAD;AACb,UAAA;MAAA,OAAA,GAAU;AACV,WAAA,sCAAA;;QACC,QAAA,GAAW,OAAQ,CAAA,OAAO,CAAC,MAAR,GAAiB,CAAjB;QACnB,IAAG,kBAAA,IAAc,6DAAd,IAAuC,2DAA1C;UACC,IAAG,oBAAA,IAAgB,gBAAhB,IAA4B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAnB,KAAyB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAvE;YACC,QAAQ,CAAC,CAAT,IAAc,IAAI,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,QAAd,GAAyB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,QAAvB,EAAiC,IAAI,CAAC,IAAI,CAAC,QAA3C;YACzB,QAAQ,CAAC,IAAI,CAAC,MAAd,GAAuB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,MAAvB,EAA+B,IAAI,CAAC,IAAI,CAAC,MAAzC,EAHxB;WAAA,MAIK,IAAG,oBAAA,IAAgB,gBAAhB,IAA4B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAnB,KAAyB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAvE;YACJ,QAAQ,CAAC,CAAT,IAAc,IAAI,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,QAAd,GAAyB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,QAAvB,EAAiC,IAAI,CAAC,IAAI,CAAC,QAA3C;YACzB,QAAQ,CAAC,IAAI,CAAC,MAAd,GAAuB,IAAI,CAAC,GAAL,CAAS,QAAQ,CAAC,IAAI,CAAC,MAAvB,EAA+B,IAAI,CAAC,IAAI,CAAC,MAAzC,EAHnB;WAAA,MAAA;YAKJ,OAAO,CAAC,IAAR,CAAa,IAAb,EALI;WALN;SAAA,MAAA;UAYC,OAAO,CAAC,IAAR,CAAa,IAAb,EAZD;;AAFD;AAeA,aAAO;IAjBM,CA3Dd;IA8EA,aAAA,EAAe,SAAC,IAAD,EAAO,EAAP,EAAW,IAAX;AACd,UAAA;MAAA,QAAA,GAAW;MAEX,aAAA,GAAgB,IAAI,CAAC,KAAL,CAAA;MAChB,MAAgC,aAAa,CAAC,gBAAd,CAA+B,aAA/B,EAA8C,EAAE,CAAC,CAAjD,CAAhC,EAAC,+BAAD,EAAe;MACf,OAAA,GAAU;MAEV,IAAG,YAAH;QACC,OAAO,CAAC,IAAR,CACC;UAAA,CAAA,EAAG,EAAE,CAAC,CAAN;UACA,IAAA,EAAM,IADN;SADD,EADD;OAAA,MAIK,IAAG,YAAH;QACJ,OAAgC,aAAa,CAAC,8BAAd,CAA6C,aAA7C,EAA4D,EAA5D,EAAgE,IAAhE,CAAhC,EAAC,gCAAD,EAAe;QACf,OAAO,CAAC,IAAR,gBAAa,YAAb,EAFI;;MAIL,OAAO,CAAC,IAAR,gBAAa,aAAb;AAEA,aAAO;IAjBO,CA9Ef;IAiGA,iBAAA,EAAmB,SAAC,IAAD,EAAO,MAAP;AAClB,UAAA;AAAA;AAAA,WAAA,qCAAA;;YAAyB,EAAE,CAAC,MAAH,KAAe;UACvC,IAAA,GAAO,aAAa,CAAC,aAAd,CAA4B,IAA5B,EAAkC,EAAlC,EAAsC,MAAM,CAAC,IAA7C;;AADR;AAEA,aAAO;IAHW,CAjGnB;IAsGA,gBAAA,EAAkB,SAAC,aAAD,EAAgB,WAAhB;AACjB,UAAA;MAAA,YAAA,GAAe;MACf,QAAA,GAAW;AACX,aAAM,IAAA,GAAO,aAAa,CAAC,KAAd,CAAA,CAAb;QACC,MAAA,GAAS,aAAa,CAAC,oBAAd,CAAmC,IAAnC;QACT,IAAG,cAAH;UACC,YAAY,CAAC,IAAb,CAAkB,IAAlB,EADD;SAAA,MAEK,IAAG,QAAA,GAAW,MAAX,IAAqB,WAAxB;UACJ,UAAA,GAAa,WAAA,GAAc;UAC3B,IAAG,UAAA,GAAa,CAAhB;YACC,YAAY,CAAC,IAAb,CAAkB,aAAa,CAAC,UAAd,CAAyB,IAAzB,EAA+B,CAA/B,EAAkC,UAAlC,CAAlB,EADD;;UAEA,IAAG,UAAA,GAAa,MAAhB;YACC,aAAa,CAAC,OAAd,CAAsB,aAAa,CAAC,UAAd,CAAyB,IAAzB,EAA+B,UAA/B,CAAtB,EADD;;AAEA,gBANI;SAAA,MAAA;UAQJ,QAAA,IAAY;UACZ,YAAY,CAAC,IAAb,CAAkB,IAAlB,EATI;;MAJN;AAeA,aAAO;QACN,YAAA,EAAc,YADR;QAEN,aAAA,EAAe,aAFT;;IAlBU,CAtGlB;IA6HA,8BAAA,EAAgC,SAAC,aAAD,EAAgB,QAAhB,EAA0B,IAA1B;AAC/B,UAAA;MAAA,YAAA,GAAe;MACf,WAAA,GAAc;AACd,aAAM,WAAA,IAAgB,aAAa,CAAC,MAAd,GAAuB,CAA7C;QACC,MAAwC,aAAa,CAAC,mBAAd,CAAkC,aAAlC,EAAiD,WAAjD,EAA8D,IAA9D,CAAxC,EAAC,qBAAD,EAAU,iCAAV,EAAyB;QACzB,IAA6B,eAA7B;UAAA,YAAY,CAAC,IAAb,CAAkB,OAAlB,EAAA;;MAFD;AAGA,aAAO;QACN,YAAA,EAAc,YADR;QAEN,aAAA,EAAe,aAFT;;IANwB,CA7HhC;IAwIA,mBAAA,EAAqB,SAAC,aAAD,EAAgB,EAAhB,EAAoB,IAApB;AACpB,UAAA;MAAA,IAAA,GAAO,aAAa,CAAC,KAAd,CAAA;MACP,UAAA,GAAa,aAAa,CAAC,oBAAd,CAAmC,IAAnC;MAEb,IAAG,cAAH;QAEC,WAAA,GAAc;QACd,OAAA,GAAU,KAHX;OAAA,MAKK,IAAG,UAAA,GAAa,EAAE,CAAC,CAAC,CAAC,MAArB;QAEJ,aAAA,GAAgB,aAAa,CAAC,UAAd,CAAyB,IAAzB,EAA+B,EAAE,CAAC,CAAC,CAAC,MAApC;QAChB,aAAa,CAAC,OAAd,CAAsB,aAAtB;QAEA,cAAA,GAAiB,aAAa,CAAC,iBAAd,CAAgC,IAAhC,CAAqC,CAAC,KAAtC,CAA4C,CAA5C,EAA+C,EAAE,CAAC,CAAC,CAAC,MAApD;QACjB,IAAG,cAAA,KAAkB,EAAE,CAAC,CAAxB;AACC,gBAAM,IAAI,gBAAJ,CAAqB,oBAAA,GAAqB,cAArB,GAAoC,gCAApC,GAAoE,EAAE,CAAC,CAAvE,GAAyE,GAA9F,EADP;;QAGA,IAAG,cAAH;UACC,OAAA,GACC;YAAA,CAAA,EAAG,EAAE,CAAC,CAAN;YACA,IAAA,EAAM,IADN;YAFF;SAAA,MAIK,IAAG,cAAH;UACJ,OAAA,GAAU,KADN;;QAGL,WAAA,GAAc,KAhBV;OAAA,MAkBA,IAAG,UAAA,KAAc,EAAE,CAAC,CAAC,CAAC,MAAtB;QAGJ,cAAA,GAAiB,aAAa,CAAC,iBAAd,CAAgC,IAAhC;QACjB,IAAG,cAAA,KAAkB,EAAE,CAAC,CAAxB;AACC,gBAAM,IAAI,gBAAJ,CAAqB,oBAAA,GAAqB,cAArB,GAAoC,gCAApC,GAAoE,EAAE,CAAC,CAAvE,GAAyE,GAA9F,EADP;;QAGA,IAAG,cAAH;UACC,OAAA,GACC;YAAA,CAAA,EAAG,EAAE,CAAC,CAAN;YACA,IAAA,EAAM,IADN;YAFF;SAAA,MAIK,IAAG,cAAH;UACJ,OAAA,GAAU,KADN;;QAGL,WAAA,GAAc,KAdV;OAAA,MAgBA,IAAG,UAAA,GAAa,EAAE,CAAC,CAAC,CAAC,MAArB;QAGJ,cAAA,GAAiB,aAAa,CAAC,iBAAd,CAAgC,IAAhC;QACjB,SAAA,GAAY,EAAE,CAAC,CAAC,CAAC,KAAL,CAAW,CAAX,EAAc,cAAc,CAAC,MAA7B;QACZ,IAAG,cAAA,KAAkB,SAArB;AACC,gBAAM,IAAI,gBAAJ,CAAqB,oBAAA,GAAqB,cAArB,GAAoC,gCAApC,GAAoE,SAApE,GAA8E,GAAnG,EADP;;QAGA,IAAG,IAAI,CAAC,CAAR;UACC,OAAA,GACC;YAAA,CAAA,EAAG,IAAI,CAAC,CAAR;YACA,IAAA,EAAM,IADN;YAFF;SAAA,MAIK,IAAG,cAAH;UACJ,OAAA,GAAU,KADN;;QAGL,WAAA,GACC;UAAA,CAAA,EAAG,EAAE,CAAC,CAAN;UAAS,CAAA,EAAG,EAAE,CAAC,CAAC,CAAC,KAAL,CAAW,aAAa,CAAC,oBAAd,CAAmC,IAAnC,CAAX,CAAZ;UAhBG;;AAkBL,aAAO;QACN,OAAA,EAAS,OADH;QAEN,aAAA,EAAe,aAFT;QAGN,WAAA,EAAa,WAHP;;IA7Da,CAxIrB;IA2MA,UAAA,EAAY,SAAC,QAAD,EAAW,IAAX,EAAiB,EAAjB;AACX,UAAA;MAAA,IAAG,kBAAH;QACC,IAAA,GAAO;UAAE,CAAA,EAAG,QAAQ,CAAC,CAAC,CAAC,KAAX,CAAiB,IAAjB,EAAuB,EAAvB,CAAL;UADR;OAAA,MAEK,IAAG,kBAAH;QACJ,IAAA,GAAO;UAAE,CAAA,EAAG,QAAQ,CAAC,CAAC,CAAC,KAAX,CAAiB,IAAjB,EAAuB,EAAvB,CAAL;UADH;;MAEL,IAAG,qBAAH;QACC,IAAI,CAAC,IAAL,GAAY,QAAQ,CAAC,KADtB;;AAEA,aAAO;IAPI,CA3MZ;IAoNA,oBAAA,EAAsB,SAAC,IAAD;aACrB,CAAC,IAAI,CAAC,CAAL,IAAU,IAAI,CAAC,CAAf,IAAoB,IAAI,CAAC,CAAzB,IAA8B,EAA/B,CAAkC,CAAC;IADd,CApNtB;IAuNA,iBAAA,EAAmB,SAAC,IAAD;aAClB,IAAI,CAAC,CAAL,IAAU,IAAI,CAAC,CAAf,IAAoB,IAAI,CAAC,CAAzB,IAA8B;IADZ,CAvNnB;;AAVD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/DiffManager.js.map b/services/track-changes/app/js/DiffManager.js.map deleted file mode 100644 index 9cd8d0895a..0000000000 --- a/services/track-changes/app/js/DiffManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "DiffManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/DiffManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA,0EAAA;IAAA;;EAAA,cAAA,GAAiB,OAAA,CAAQ,kBAAR;;EACjB,sBAAA,GAAyB,OAAA,CAAQ,0BAAR;;EACzB,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,WAAA,GAChB;IAAA,sBAAA,EAAwB,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,QAAlC;;QAAkC,WAAW,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB,EAA0B,OAA1B,GAAA;;aAGpE,sBAAsB,CAAC,WAAvB,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB;QACtD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,IAAI,mBAAJ;AACC,iBAAO,QAAA,CAAS,IAAT,EAAe,OAAf,EAAwB,OAAxB,EAAiC,EAAjC,EADR;;eAEA,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,MAArD,EAA6D;UAAA,IAAA,EAAM,WAAN;SAA7D,EAAgF,SAAC,KAAD,EAAQ,OAAR;UAC/E,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT,EAAe,OAAf,EAAwB,OAAxB,EAAiC,OAAjC;QAF+E,CAAhF;MAJsD,CAAvD;IAHuB,CAAxB;IAWA,OAAA,EAAS,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,SAAlC,EAA6C,QAA7C;;QAA6C,WAAW,SAAC,KAAD,EAAQ,IAAR,GAAA;;aAChE,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,WAAzD,EAAsE,SAAC,KAAD,EAAQ,eAAR,EAAyB,OAAzB;AACrE,YAAA;QAAA,IAAG,aAAH;UACC,IAAG,KAAK,CAAC,OAAN,KAAiB,gBAApB;AACC,mBAAO,QAAA,CAAS,IAAT,EAAe,qBAAf,EADR;WAAA,MAAA;AAGC,mBAAO,QAAA,CAAS,KAAT,EAHR;WADD;;QAMA,cAAA,GAAiB;AACjB;AAAA,aAAA,qCAAA;;UACC,IAAG,MAAM,CAAC,CAAP,IAAY,SAAf;YACC,cAAc,CAAC,IAAf,CAAoB,MAApB,EADD;;AADD;AAIA;UACC,IAAA,GAAO,aAAa,CAAC,SAAd,CAAwB,eAAxB,EAAyC,cAAzC,EADR;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,CAAT,EAHR;;eAKA,QAAA,CAAS,IAAT,EAAe,IAAf;MAjBqE,CAAtE;IADQ,CAXT;IA+BA,wBAAA,EAA0B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,SAA9B;AAMzB,UAAA;;QANuD,YAAY,SAAC,KAAD,EAAQ,QAAR,EAAkB,cAAlB,GAAA;;MAMnE,OAAA,GAAU;MACV,QAAA,GAAW,SAAA;AACV,YAAA;QADW,sBAAO;QAClB,IAAG,aAAH;UACC,IAAG,KAAK,CAAC,KAAN,IAAgB,OAAA,GAAU,CAA7B;YACC,MAAM,CAAC,IAAP,CAAY;cAAC,OAAA,KAAD;cAAQ,YAAA,UAAR;cAAoB,QAAA,MAApB;cAA4B,SAAA,OAA5B;cAAqC,SAAA,OAArC;aAAZ,EAA2D,mCAA3D;mBACA,KAAA,CAAA,EAFD;WAAA,MAAA;mBAIC,SAAA,CAAU,KAAV,EAJD;WADD;SAAA,MAAA;iBAOC,SAAA,aAAU,CAAA,IAAM,SAAA,WAAA,IAAA,CAAA,CAAhB,EAPD;;MADU;aAUR,CAAA,KAAA,GAAQ,SAAA;QACV,OAAA;eACA,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,OAA7D,EAAsE,QAAtE;MAFU,CAAR,CAAH,CAAA;IAjByB,CA/B1B;IAoDA,4BAAA,EAA8B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;;QAA8B,WAAW,SAAC,KAAD,EAAQ,QAAR,EAAkB,cAAlB,GAAA;;MACtE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;QAAwC,OAAA,EAAS,OAAjD;OAAX,EAAqE,iCAArE;aACA,WAAW,CAAC,sBAAZ,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,OAAvD,EAAgE,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB,EAA0B,OAA1B;AAC/D,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;AAGA,aAAA,yCAAA;;cAAsB,CAAC,CAAC;AACvB,mBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT;;AADR;AAIA,gDAAgB,CAAE,WAAZ,IAAiB,OAAvB;UACC,OAAO,CAAC,KAAR,CAAA;QADD;QAGA,UAAA,GAAa,OAAQ,CAAA,CAAA;QACrB,IAAG,oBAAA,IAAgB,UAAU,CAAC,CAAX,KAAgB,OAAA,GAAU,CAA7C;UACC,KAAA,GAAQ,IAAI,KAAJ,CAAU,yBAAA,GAA0B,UAAU,CAAC,CAArC,GAAuC,gCAAvC,GAAuE,OAAjF;UACR,KAAK,CAAC,KAAN,GAAc;AACd,iBAAO,QAAA,CAAS,KAAT,EAHR;;QAKA,MAAM,CAAC,GAAP,CAAW;UAAC,UAAA,EAAY,OAAb;UAAsB,iBAAA,uBAAmB,UAAU,CAAE,UAArD;UAAwD,WAAA,EAAa,OAAO,CAAC,MAA7E;SAAX,EAAiG,mBAAjG;QAEA,UAAA,GAAa,OAAO,CAAC,KAAR,CAAA,CAAe,CAAC,OAAhB,CAAA;AAEb;UACC,eAAA,GAAkB,aAAa,CAAC,aAAd,CAA4B,OAA5B,EAAqC,UAArC,EADnB;SAAA,cAAA;UAGM;AACL,iBAAO,QAAA,CAAS,CAAT,EAJR;;eAMA,QAAA,CAAS,IAAT,EAAe,eAAf,EAAgC,UAAhC;MA3B+D,CAAhE;IAF6B,CApD9B;;AAND" -} \ No newline at end of file diff --git a/services/track-changes/app/js/DocumentUpdaterManager.js.map b/services/track-changes/app/js/DocumentUpdaterManager.js.map deleted file mode 100644 index 50a1d7f569..0000000000 --- a/services/track-changes/app/js/DocumentUpdaterManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "DocumentUpdaterManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/DocumentUpdaterManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EAEX,MAAM,CAAC,OAAP,GAAiB,sBAAA,GAChB;IAAA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;AACZ,UAAA;;QADiC,WAAW,SAAC,KAAD,EAAQ,OAAR,EAAiB,OAAjB,GAAA;;MAC5C,GAAA,GAAS,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,GAA/B,GAAmC,WAAnC,GAA8C,UAA9C,GAAyD,OAAzD,GAAgE;MACxE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAW,UAAX;QAAuB,MAAA,EAAQ,MAA/B;OAAX,EAAkD,mCAAlD;aACA,OAAO,CAAC,GAAR,CAAY,GAAZ,EAAiB,SAAC,KAAD,EAAQ,GAAR,EAAa,IAAb;QAChB,IAAG,aAAH;AACC,iBAAO,QAAA,CAAS,KAAT,EADR;;QAEA,IAAG,GAAG,CAAC,UAAJ,IAAkB,GAAlB,IAA0B,GAAG,CAAC,UAAJ,GAAiB,GAA9C;AACC;YACC,IAAA,GAAO,IAAI,CAAC,KAAL,CAAW,IAAX,EADR;WAAA,cAAA;YAEM;AACL,mBAAO,QAAA,CAAS,KAAT,EAHR;;UAIA,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,OAAA,EAAS,IAAI,CAAC,OAAnC;WAAX,EAAwD,+BAAxD;iBACA,QAAA,CAAS,IAAT,EAAe,IAAI,CAAC,KAAK,CAAC,IAAX,CAAgB,IAAhB,CAAf,EAAsC,IAAI,CAAC,OAA3C,EAND;SAAA,MAAA;UAQC,KAAA,GAAQ,IAAI,KAAJ,CAAU,kDAAA,GAAmD,GAAG,CAAC,UAAjE;UACR,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAW,UAAvB;YAAmC,MAAA,EAAO,MAA1C;YAAkD,GAAA,EAAK,GAAvD;WAAb,EAAyE,6BAAzE;iBACA,QAAA,CAAS,KAAT,EAVD;;MAHgB,CAAjB;IAHY,CAAb;IAkBA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,OAA9B,EAAuC,QAAvC;AACZ,UAAA;;QADmD,WAAW,SAAC,KAAD,GAAA;;MAC9D,GAAA,GAAS,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,GAA/B,GAAmC,WAAnC,GAA8C,UAA9C,GAAyD,OAAzD,GAAgE;MACxE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAW,UAAX;QAAuB,MAAA,EAAQ,MAA/B;OAAX,EAAkD,iCAAlD;aACA,OAAO,CAAC,IAAR,CAAa;QACZ,GAAA,EAAK,GADO;QAEZ,IAAA,EACC;UAAA,KAAA,EAAO,OAAO,CAAC,KAAR,CAAc,IAAd,CAAP;UACA,MAAA,EAAQ,SADR;UAEA,OAAA,EAAS,OAFT;UAGA,OAAA,EAAS,IAHT;SAHW;OAAb,EAOG,SAAC,KAAD,EAAQ,GAAR,EAAa,IAAb;QACF,IAAG,aAAH;AACC,iBAAO,QAAA,CAAS,KAAT,EADR;;QAEA,IAAG,GAAG,CAAC,UAAJ,IAAkB,GAAlB,IAA0B,GAAG,CAAC,UAAJ,GAAiB,GAA9C;iBACC,QAAA,CAAS,IAAT,EADD;SAAA,MAAA;UAGC,KAAA,GAAQ,IAAI,KAAJ,CAAU,kDAAA,GAAmD,GAAG,CAAC,UAAjE;UACR,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAW,UAAvB;YAAmC,MAAA,EAAO,MAA1C;YAAkD,GAAA,EAAK,GAAvD;WAAb,EAAyE,6BAAzE;iBACA,QAAA,CAAS,KAAT,EALD;;MAHE,CAPH;IAHY,CAlBb;;AALD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/HealthChecker.js.map b/services/track-changes/app/js/HealthChecker.js.map deleted file mode 100644 index aeb4de5ac2..0000000000 --- a/services/track-changes/app/js/HealthChecker.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "HealthChecker.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/HealthChecker.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,SAAR,CAAkB,CAAC;;EAC9B,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,IAAA,GAAO,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;;EACtC,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,WAAA,GAAc,OAAA,CAAQ,eAAR;;EAEd,MAAM,CAAC,OAAP,GACC;IAAA,KAAA,EAAQ,SAAC,QAAD;AACP,UAAA;MAAA,UAAA,GAAa,QAAA,CAAS,QAAQ,CAAC,YAAY,CAAC,WAAW,CAAC,UAA3C;MACb,GAAA,GAAM,mBAAA,GAAoB,IAApB,GAAyB,WAAzB,GAAoC;MAC1C,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAW,UAAX;OAAX,EAAkC,sBAAlC;MACA,IAAA,GAAO;QACN,SAAC,EAAD;iBACC,OAAO,CAAC,GAAR,CAAY;YAAC,GAAA,EAAI,mBAAA,GAAoB,IAApB,GAAyB,aAA9B;YAA4C,OAAA,EAAQ,IAApD;WAAZ,EAAuE,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;YACtE,IAAG,WAAH;cACC,MAAM,CAAC,GAAP,CAAW;gBAAA,GAAA,EAAI,GAAJ;gBAAS,UAAA,EAAW,UAApB;eAAX,EAA2C,sCAA3C;qBACA,EAAA,CAAG,GAAH,EAFD;aAAA,MAGK,mBAAG,GAAG,CAAE,oBAAL,KAAmB,GAAtB;qBACJ,EAAA,CAAG,4BAAA,GAA6B,GAAG,CAAC,UAApC,EADI;aAAA,MAAA;qBAGJ,EAAA,CAAA,EAHI;;UAJiE,CAAvE;QADD,CADM,EAUN,SAAC,EAAD;iBACC,OAAO,CAAC,IAAR,CAAa;YAAC,GAAA,EAAO,GAAD,GAAK,QAAZ;YAAqB,OAAA,EAAQ,KAA7B;WAAb,EAAkD,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;YACjD,IAAG,WAAH;cACC,MAAM,CAAC,GAAP,CAAW;gBAAA,GAAA,EAAI,GAAJ;gBAAS,UAAA,EAAW,UAApB;eAAX,EAA2C,iCAA3C;qBACA,EAAA,CAAG,GAAH,EAFD;aAAA,MAGK,mBAAG,GAAG,CAAE,oBAAL,KAAmB,GAAtB;qBACJ,EAAA,CAAG,4BAAA,GAA6B,GAAG,CAAC,UAApC,EADI;aAAA,MAAA;qBAGJ,EAAA,CAAA,EAHI;;UAJ4C,CAAlD;QADD,CAVM,EAmBN,SAAC,EAAD;iBACC,OAAO,CAAC,GAAR,CAAY;YAAC,GAAA,EAAO,GAAD,GAAK,UAAZ;YAAuB,OAAA,EAAQ,KAA/B;WAAZ,EAAmD,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;YAClD,IAAG,WAAH;cACC,MAAM,CAAC,GAAP,CAAW;gBAAA,GAAA,EAAI,GAAJ;gBAAS,UAAA,EAAW,UAApB;eAAX,EAA2C,wCAA3C;qBACA,EAAA,CAAG,GAAH,EAFD;aAAA,MAGK,mBAAG,GAAG,CAAE,oBAAL,KAAmB,GAAtB;qBACJ,EAAA,CAAG,4BAAA,GAA6B,GAAG,CAAC,UAApC,EADI;aAAA,MAAA;qBAGJ,EAAA,CAAA,EAHI;;UAJ6C,CAAnD;QADD,CAnBM;;aA6BP,KAAK,CAAC,MAAN,CAAa,IAAb,EAAmB,QAAnB;IAjCO,CAAR;IAmCA,SAAA,EAAW,SAAC,QAAD;aACV,WAAW,CAAC,WAAZ,CAAwB,QAAxB;IADU,CAnCX;;AATD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/HttpController.js.map b/services/track-changes/app/js/HttpController.js.map deleted file mode 100644 index b2d95f4b7b..0000000000 --- a/services/track-changes/app/js/HttpController.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "HttpController.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/HttpController.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,cAAA,GAAiB,OAAA,CAAQ,kBAAR;;EACjB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,cAAA,GAAiB,OAAA,CAAQ,kBAAR;;EACjB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,CAAA,GAAI,OAAA,CAAQ,YAAR;;EAEJ,MAAM,CAAC,OAAP,GAAiB,cAAA,GAChB;IAAA,QAAA,EAAU,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACT,UAAA;;QADoB,OAAO,SAAC,KAAD,GAAA;;MAC3B,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;OAAX,EAAmD,yBAAnD;aACA,cAAc,CAAC,kCAAf,CAAkD,UAAlD,EAA8D,MAA9D,EAAsE,SAAC,KAAD;QACrE,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAFqE,CAAtE;IAJS,CAAV;IAQA,YAAA,EAAc,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACb,UAAA;;QADwB,OAAO,SAAC,KAAD,GAAA;;MAC/B,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;OAAX,EAAmC,6BAAnC;aACA,cAAc,CAAC,oCAAf,CAAoD,UAApD,EAAgE,SAAC,KAAD;QAC/D,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF+D,CAAhE;IAHa,CARd;IAeA,QAAA,EAAU,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AAET,UAAA;;QAFoB,OAAO,SAAC,KAAD,GAAA;;MAE3B,KAAA,GAAW,uBAAH,GAAyB,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,KAAnB,EAA0B,EAA1B,CAAzB,GAA4D,CAAC;MACrE,MAAM,CAAC,GAAP,CAAW;QAAC,KAAA,EAAO,KAAR;OAAX,EAA2B,uBAA3B;aACA,cAAc,CAAC,QAAf,CAAwB,KAAxB,EAA+B,SAAC,KAAD,EAAQ,MAAR;AAC9B,YAAA;QAAA,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;QACC,sBAAD,EAAS,4BAAT,EAAoB;QACpB,MAAA,GAAY,SAAS,CAAC,MAAX,GAAkB,cAAlB,GAAgC,MAAM,CAAC,MAAvC,GAA8C;QACzD,IAAG,KAAA,KAAS,CAAZ;iBACC,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAwB,MAAD,GAAQ,kBAAR,GAAyB,CAAC,GAAG,CAAC,IAAJ,CAAS,IAAT,CAAD,CAAzB,GAAyC,IAAhE,EADD;SAAA,MAEK,IAAG,MAAM,CAAC,MAAP,GAAgB,CAAnB;UACJ,MAAM,CAAC,GAAP,CAAW;YAAC,MAAA,EAAQ,MAAT;YAAiB,SAAA,EAAW,SAA5B;WAAX,EAAmD,yBAAnD;iBACA,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAwB,MAAD,GAAQ,sBAAR,GAA6B,CAAC,MAAM,CAAC,IAAP,CAAY,IAAZ,CAAD,CAA7B,GAAgD,IAAvE,EAFI;SAAA,MAAA;iBAIJ,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAwB,MAAD,GAAQ,YAAR,GAAoB,SAAS,CAAC,MAA9B,GAAqC,eAArC,GAAoD,GAAG,CAAC,MAAxD,GAA+D,IAAtF,EAJI;;MANyB,CAA/B;IAJS,CAfV;IA+BA,oBAAA,EAAsB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;;QAAW,OAAO,SAAC,KAAD,GAAA;;MACvC,MAAM,CAAC,GAAP,CAAW,2BAAX;aACA,cAAc,CAAC,kBAAf,CAAkC,SAAC,KAAD,EAAQ,MAAR;QACjC,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;QACA,IAAG,MAAM,CAAC,MAAP,GAAgB,CAAnB;UACC,MAAM,CAAC,GAAP,CAAW;YAAC,QAAA,EAAU,MAAX;WAAX,EAA+B,wBAA/B;iBACA,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAqB,qBAAA,GAAqB,CAAC,MAAM,CAAC,IAAP,CAAY,IAAZ,CAAD,CAArB,GAAwC,IAA7D,EAFD;SAAA,MAAA;iBAIC,GAAG,CAAC,MAAJ,CAAW,GAAX,CAAe,CAAC,IAAhB,CAAqB,6BAArB,EAJD;;MAFiC,CAAlC;IAFqB,CA/BtB;IAyCA,QAAA,EAAU,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACT,UAAA;;QADoB,OAAO,SAAC,KAAD,GAAA;;MAC3B,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;OAAX,EAAmD,sBAAnD;aACA,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,CAAzD,EAA4D,SAAC,KAAD,EAAQ,QAAR,EAAkB,cAAlB;AAC3D,YAAA;QAAA,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;QACA,MAAA,GAAS;AACT,aAAA,gDAAA;;AACC;AAAA,eAAA,uCAAA;;gBAAyB,EAAE,CAAC,MAAH,KAAa;cACrC,MAAM,CAAC,IAAP,CAAY,EAAZ;;AADD;AADD;QAGA,IAAG,MAAM,CAAC,MAAP,GAAgB,CAAnB;iBACC,GAAG,CAAC,IAAJ,CAAS,MAAT,EADD;SAAA,MAAA;iBAGC,GAAG,CAAC,IAAJ,CAAS,GAAT,EAHD;;MAN2D,CAA5D;IAJS,CAzCV;IAwDA,OAAA,EAAS,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACR,UAAA;;QADmB,OAAO,SAAC,KAAD,GAAA;;MAC1B,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MAExB,IAAG,sBAAH;QACC,IAAA,GAAO,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,IAAnB,EAAyB,EAAzB,EADR;OAAA,MAAA;QAGC,IAAA,GAAO,KAHR;;MAIA,IAAG,oBAAH;QACC,EAAA,GAAK,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,EAAnB,EAAuB,EAAvB,EADN;OAAA,MAAA;QAGC,EAAA,GAAK,KAHN;;MAKA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,MAAA,IAArB;QAA2B,IAAA,EAA3B;OAAX,EAA2C,cAA3C;aACA,WAAW,CAAC,OAAZ,CAAoB,UAApB,EAAgC,MAAhC,EAAwC,IAAxC,EAA8C,EAA9C,EAAkD,SAAC,KAAD,EAAQ,IAAR;QACjD,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS;UAAC,IAAA,EAAM,IAAP;SAAT;MAFiD,CAAlD;IAdQ,CAxDT;IA0EA,UAAA,EAAY,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACX,UAAA;;QADsB,OAAO,SAAC,KAAD,GAAA;;MAC7B,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MAExB,IAAG,wBAAH;QACC,MAAA,GAAS,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,MAAnB,EAA2B,EAA3B,EADV;;MAEA,IAAG,2BAAH;QACC,SAAA,GAAY,QAAA,CAAS,GAAG,CAAC,KAAK,CAAC,SAAnB,EAA8B,EAA9B,EADb;;aAGA,cAAc,CAAC,2BAAf,CAA2C,UAA3C,EAAuD;QAAA,MAAA,EAAQ,MAAR;QAAgB,SAAA,EAAW,SAA3B;OAAvD,EAA6F,SAAC,KAAD,EAAQ,OAAR,EAAiB,mBAAjB;QAC5F,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS;UACR,OAAA,EAAS,OADD;UAER,mBAAA,EAAqB,mBAFb;SAAT;MAF4F,CAA7F;IARW,CA1EZ;IAyFA,OAAA,EAAS,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACR,UAAA;;QADmB,OAAO,SAAC,KAAD,GAAA;;MAC1B,MAAgC,GAAG,CAAC,MAApC,EAAC,mBAAD,EAAS,2BAAT,EAAqB;MACrB,OAAA,GAAU,GAAG,CAAC,OAAQ,CAAA,WAAA;MACtB,OAAA,GAAU,QAAA,CAAS,OAAT,EAAkB,EAAlB;aACV,cAAc,CAAC,sBAAf,CAAsC,UAAtC,EAAkD,MAAlD,EAA0D,OAA1D,EAAmE,OAAnE,EAA4E,SAAC,KAAD;QAC3E,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF2E,CAA5E;IAJQ,CAzFT;IAiGA,cAAA,EAAgB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACf,UAAA;;QAD0B,OAAO,SAAC,KAAD,GAAA;;MACjC,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,qCAAjC;aACA,WAAW,CAAC,YAAZ,CAAyB,UAAzB,EAAqC,MAArC,EAA6C,SAAC,KAAD;QAC5C,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF4C,CAA7C;IAJe,CAjGhB;IAyGA,cAAA,EAAgB,SAAC,GAAD,EAAM,GAAN,EAAW,IAAX;AACf,UAAA;;QAD0B,OAAO,SAAC,KAAD,GAAA;;MACjC,UAAA,GAAa,GAAG,CAAC,MAAM,CAAC;MACxB,MAAA,GAAS,GAAG,CAAC,MAAM,CAAC;MACpB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,2BAAjC;aACA,WAAW,CAAC,YAAZ,CAAyB,UAAzB,EAAqC,MAArC,EAA6C,SAAC,KAAD;QAC5C,IAAsB,aAAtB;AAAA,iBAAO,IAAA,CAAK,KAAL,EAAP;;eACA,GAAG,CAAC,IAAJ,CAAS,GAAT;MAF4C,CAA7C;IAJe,CAzGhB;IAiHA,WAAA,EAAa,SAAC,GAAD,EAAM,GAAN;aACZ,aAAa,CAAC,KAAd,CAAoB,SAAC,GAAD;QACnB,IAAG,WAAH;UACC,MAAM,CAAC,GAAP,CAAW;YAAA,GAAA,EAAI,GAAJ;WAAX,EAAoB,+BAApB;iBACA,GAAG,CAAC,IAAJ,CAAS,GAAT,EAFD;SAAA,MAAA;iBAIC,GAAG,CAAC,IAAJ,CAAS,GAAT,EAJD;;MADmB,CAApB;IADY,CAjHb;IAyHA,SAAA,EAAW,SAAC,GAAD,EAAM,GAAN;aACV,aAAa,CAAC,SAAd,CAAwB,SAAC,GAAD;QACvB,IAAG,WAAH;UACC,MAAM,CAAC,GAAP,CAAW;YAAA,GAAA,EAAI,GAAJ;WAAX,EAAoB,6BAApB;iBACA,GAAG,CAAC,IAAJ,CAAS,GAAT,EAFD;SAAA,MAAA;iBAIC,GAAG,CAAC,IAAJ,CAAS,GAAT,EAJD;;MADuB,CAAxB;IADU,CAzHX;;AATD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/LockManager.js.map b/services/track-changes/app/js/LockManager.js.map deleted file mode 100644 index 60b765a17b..0000000000 --- a/services/track-changes/app/js/LockManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "LockManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/LockManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,KAAA,GAAQ,OAAA,CAAQ,kBAAR;;EACR,OAAA,GAAU,KAAK,CAAC,YAAN,CAAmB,QAAQ,CAAC,KAAK,CAAC,IAAlC;;EACV,EAAA,GAAK,OAAA,CAAQ,IAAR;;EACL,MAAA,GAAS,OAAA,CAAQ,QAAR;;EACT,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,IAAA,GAAO,EAAE,CAAC,QAAH,CAAA;;EACP,GAAA,GAAM,OAAO,CAAC;;EACd,GAAA,GAAM,MAAM,CAAC,WAAP,CAAmB,CAAnB,CAAqB,CAAC,QAAtB,CAA+B,KAA/B;;EACN,KAAA,GAAQ;;EAER,MAAM,CAAC,OAAP,GAAiB,WAAA,GAChB;IAAA,kBAAA,EAAoB,EAApB;IACA,kBAAA,EAAoB,KADpB;IAEA,QAAA,EAAU,GAFV;IAOA,UAAA,EAAa,SAAA;AACZ,UAAA;MAAA,IAAA,GAAO,IAAI,CAAC,GAAL,CAAA;AACP,aAAO,cAAA,GAAe,IAAf,GAAoB,OAApB,GAA2B,GAA3B,GAA+B,UAA/B,GAAyC,GAAzC,GAA6C,QAA7C,GAAqD,IAArD,GAA0D,SAA1D,GAAkE,CAAC,KAAA,EAAD;IAF7D,CAPb;IAWA,YAAA,EAAc,mGAXd;IAaA,OAAA,EAAU,SAAC,GAAD,EAAM,QAAN;AACT,UAAA;;QADe,WAAW,SAAC,GAAD,EAAM,OAAN,GAAA;;MAC1B,SAAA,GAAY,WAAW,CAAC,UAAZ,CAAA;aACZ,OAAO,CAAC,GAAR,CAAY,GAAZ,EAAiB,SAAjB,EAA4B,IAA5B,EAAkC,IAAC,CAAA,QAAnC,EAA6C,IAA7C,EAAmD,SAAC,GAAD,EAAM,OAAN;QAClD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAG,OAAA,KAAW,IAAd;iBACC,QAAA,CAAS,GAAT,EAAc,IAAd,EAAoB,SAApB,EADD;SAAA,MAAA;iBAGC,QAAA,CAAS,GAAT,EAAc,KAAd,EAHD;;MAFkD,CAAnD;IAFS,CAbV;IAsBA,OAAA,EAAS,SAAC,GAAD,EAAM,QAAN;AACR,UAAA;;QADc,WAAW,SAAC,KAAD,GAAA;;MACzB,SAAA,GAAY,IAAI,CAAC,GAAL,CAAA;aACT,CAAA,OAAA,GAAU,SAAA;AACZ,YAAA;QAAA,IAAG,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,SAAb,GAAyB,WAAW,CAAC,kBAAxC;UACC,CAAA,GAAI,IAAI,KAAJ,CAAU,SAAV;UACJ,CAAC,CAAC,GAAF,GAAQ;AACR,iBAAO,QAAA,CAAS,CAAT,EAHR;;eAKA,WAAW,CAAC,OAAZ,CAAoB,GAApB,EAAyB,SAAC,KAAD,EAAQ,OAAR,EAAiB,SAAjB;UACxB,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACA,IAAG,OAAH;mBACC,QAAA,CAAS,IAAT,EAAe,SAAf,EADD;WAAA,MAAA;mBAGC,UAAA,CAAW,OAAX,EAAoB,WAAW,CAAC,kBAAhC,EAHD;;QAFwB,CAAzB;MANY,CAAV,CAAH,CAAA;IAFQ,CAtBT;IAqCA,SAAA,EAAW,SAAC,GAAD,EAAM,QAAN;;QAAM,WAAW,SAAC,GAAD,EAAM,MAAN,GAAA;;aAC3B,OAAO,CAAC,MAAR,CAAe,GAAf,EAAoB,SAAC,GAAD,EAAM,MAAN;QACnB,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,MAAA,GAAS,QAAA,CAAS,MAAT;QACT,IAAG,MAAA,KAAU,CAAb;iBACC,QAAA,CAAS,GAAT,EAAc,KAAd,EADD;SAAA,MAAA;iBAGC,QAAA,CAAS,GAAT,EAAc,IAAd,EAHD;;MAHmB,CAApB;IADU,CArCX;IA8CA,WAAA,EAAa,SAAC,GAAD,EAAM,SAAN,EAAiB,QAAjB;aACZ,OAAO,EAAC,IAAD,EAAP,CAAa,WAAW,CAAC,YAAzB,EAAuC,CAAvC,EAA0C,GAA1C,EAA+C,SAA/C,EAA0D,SAAC,GAAD,EAAM,MAAN;QACzD,IAAG,WAAH;AACC,iBAAO,QAAA,CAAS,GAAT,EADR;;QAEA,IAAG,gBAAA,IAAY,MAAA,KAAY,CAA3B;UACC,MAAM,CAAC,KAAP,CAAa;YAAC,GAAA,EAAI,GAAL;YAAU,SAAA,EAAU,SAApB;YAA+B,SAAA,EAAU,GAAzC;YAA8C,YAAA,EAAa,MAA3D;WAAb,EAAiF,iBAAjF;AACA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,iCAAV,CAAT,EAFR;;eAGA,QAAA,CAAS,GAAT,EAAa,MAAb;MANyD,CAA1D;IADY,CA9Cb;IAuDA,WAAA,EAAa,SAAC,GAAD,EAAM,MAAN,EAAkD,QAAlD;;QAAM,SAAS,CAAE,SAAC,WAAD;;YAAC,cAAc,SAAC,KAAD,GAAA;;QAAf,CAAF;;;QAAmC,WAAW,CAAE,SAAC,KAAD,GAAA,CAAF;;aACzE,WAAW,CAAC,OAAZ,CAAoB,GAApB,EAAyB,SAAC,KAAD,EAAQ,SAAR;QACxB,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,MAAA,CAAO,SAAC,MAAD;iBACN,WAAW,CAAC,WAAZ,CAAwB,GAAxB,EAA6B,SAA7B,EAAwC,SAAC,MAAD;YACvC,KAAA,GAAQ,MAAA,IAAU;YAClB,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;mBACA,QAAA,CAAA;UAHuC,CAAxC;QADM,CAAP;MAFwB,CAAzB;IADY,CAvDb;IAgEA,WAAA,EAAa,SAAC,QAAD;AACZ,UAAA;MAAA,MAAA,GAAS,SAAC,WAAD;eACR,WAAA,CAAA;MADQ;aAET,WAAW,CAAC,WAAZ,CAAwB,+BAAA,GAAgC,IAAhC,GAAqC,OAArC,GAA4C,GAA5C,GAAgD,UAAhD,GAA0D,GAAlF,EAAyF,MAAzF,EAAiG,QAAjG;IAHY,CAhEb;IAqEA,KAAA,EAAO,SAAC,QAAD;MACN,OAAO,CAAC,IAAR,CAAA;aACA,OAAO,CAAC,IAAR,CAAa,KAAb,EAAoB,QAApB;IAFM,CArEP;;AAbD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/MongoAWS.js.map b/services/track-changes/app/js/MongoAWS.js.map deleted file mode 100644 index 8594f9a104..0000000000 --- a/services/track-changes/app/js/MongoAWS.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "MongoAWS.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/MongoAWS.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA,sHAAA;IAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,GAAA,GAAM,OAAA,CAAQ,SAAR;;EACN,GAAA,GAAM,OAAA,CAAQ,YAAR;;EACN,MAAiB,OAAA,CAAQ,WAAR,CAAjB,EAAC,WAAD,EAAK;;EACL,UAAA,GAAa,OAAA,CAAQ,YAAR;;EACb,cAAA,GAAiB,OAAA,CAAQ,QAAR;;EACjB,IAAA,GAAO,OAAA,CAAQ,MAAR;;EACP,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EAEV,IAAA,GAAO,EAAA,GAAK,IAAL,GAAY;;EAEnB,YAAA,GAAe,SAAC,iBAAD,EAAoB,UAApB,EAAgC,MAAhC,EAAwC,OAAxC;AACd,QAAA;IAAA,UAAA,GACC;MAAA,WAAA,EAAa,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,GAAtC;MACA,eAAA,EAAiB,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,MAD1C;;AAGD,WAAO,iBAAA,CAAkB,IAAI,GAAG,CAAC,EAAR,CAAW,UAAX,CAAlB,EAA0C;MAChD,QAAA,EAAU,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,WADS;MAEhD,KAAA,EAAO,UAAA,GAAW,WAAX,GAAuB,MAAvB,GAA8B,QAA9B,GAAuC,OAFE;KAA1C;EALO;;EAUf,MAAM,CAAC,OAAP,GAAiB,QAAA,GAEhB;IAAA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,SAA9B;AAEZ,UAAA;;QAF0C,YAAY,SAAC,KAAD,GAAA;;MAEtD,QAAA,GAAW,SAAA;AACV,YAAA;QADW;QACX,SAAA,aAAU,IAAV;eACA,SAAA,GAAY,SAAA,GAAA;MAFF;MAIX,KAAA,GAAQ;QACP,GAAA,EAAK,QAAA,CAAS,OAAT,CADE;QAEP,MAAA,EAAQ,QAAA,CAAS,MAAT,CAFD;;MAKR,IAAuD,kBAAvD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,oBAAV,CAAT,EAAP;;MACA,IAAmD,cAAnD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT,EAAP;;MACA,IAAoD,eAApD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,iBAAV,CAAT,EAAP;;MAEA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,sBAA1C;MAEA,MAAA,GAAS,YAAA,CAAa,GAAG,CAAC,WAAjB,EAA8B,UAA9B,EAA0C,MAA1C,EAAkD,OAAlD;aAET,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB,KAAtB,EAA6B,SAAC,GAAD,EAAM,MAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAmE,cAAnE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gCAAV,CAAT,EAAP;;QACA,IAAqE,wBAArE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,sCAAV,CAAT,EAAP;;QACA,gBAAA,GAAmB,IAAI,CAAC,SAAL,CAAe,MAAf;QACnB,IAAG,gBAAgB,CAAC,OAAjB,CAAyB,QAAzB,CAAA,KAAsC,CAAC,CAA1C;UACC,KAAA,GAAQ,IAAI,KAAJ,CAAU,4BAAV;UACR,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAY,UAAxB;YAAoC,MAAA,EAAQ,MAA5C;YAAoD,OAAA,EAAS,OAA7D;WAAb,EAAmF,KAAK,CAAC,OAAzF;AACA,iBAAO,QAAA,CAAS,KAAT,EAHR;;eAIA,IAAI,CAAC,IAAL,CAAU,gBAAV,EAA4B,SAAC,GAAD,EAAM,GAAN;UAC3B,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,SAAA,OAArB;YAA8B,QAAA,EAAU,gBAAgB,CAAC,MAAzD;YAAiE,OAAA,EAAS,GAAG,CAAC,MAA9E;WAAX,EAAkG,iBAAlG;UACA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,MAAM,CAAC,EAAP,CAAU,OAAV,EAAmB,SAAC,GAAD;mBAClB,QAAA,CAAS,GAAT;UADkB,CAAnB;UAEA,MAAM,CAAC,EAAP,CAAU,QAAV,EAAoB,SAAA;YACnB,OAAO,CAAC,GAAR,CAAY,cAAZ;YACA,MAAM,CAAC,GAAP,CAAW;cAAC,YAAA,UAAD;cAAa,QAAA,MAAb;cAAqB,SAAA,OAArB;aAAX,EAA0C,wBAA1C;mBACA,QAAA,CAAS,IAAT;UAHmB,CAApB;UAIA,MAAM,CAAC,KAAP,CAAa,GAAb;iBACA,MAAM,CAAC,GAAP,CAAA;QAV2B,CAA5B;MAT4B,CAA7B;IAnBY,CAAb;IAwCA,gBAAA,EAAkB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,SAA9B;AACjB,UAAA;;QAD+C,YAAY,SAAC,KAAD,EAAQ,MAAR,GAAA;;MAC3D,QAAA,GAAW,SAAA;AACV,YAAA;QADW;QACX,SAAA,aAAU,IAAV;eACA,SAAA,GAAY,SAAA,GAAA;MAFF;MAIX,IAAuD,kBAAvD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,oBAAV,CAAT,EAAP;;MACA,IAAmD,cAAnD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT,EAAP;;MACA,IAAoD,eAApD;AAAA,eAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,iBAAV,CAAT,EAAP;;MAEA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,0BAA1C;MAEA,QAAA,GAAW,YAAA,CAAa,GAAG,CAAC,UAAjB,EAA6B,UAA7B,EAAyC,MAAzC,EAAiD,OAAjD;MAEX,WAAA,GAAc,QACb,CAAC,EADY,CACT,MADS,EACD,SAAC,GAAD;AACX,eAAO;MADI,CADC,CAGb,CAAC,EAHY,CAGT,OAHS,EAGA,SAAC,GAAD;eACZ,QAAA,CAAS,GAAT;MADY,CAHA;MAMd,MAAA,GAAS,IAAI,CAAC,YAAL,CAAA;MACT,MAAM,CAAC,WAAP,CAAmB,MAAnB;MACA,MAAM,CAAC,EAAP,CAAU,OAAV,EAAmB,SAAC,GAAD;QAClB,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;SAAX,EAA+C,iCAA/C;eACA,QAAA,CAAS,GAAT;MAFkB,CAAnB;MAIA,YAAA,GAAe,WAAW,CAAC,IAAZ,CAAiB,MAAjB;MACf,KAAA,GAAQ;MACR,YAAY,CAAC,EAAb,CAAgB,OAAhB,EAAyB,SAAC,GAAD;AACxB,eAAO,QAAA,CAAS,GAAT;MADiB,CAAzB;MAEA,YAAY,CAAC,EAAb,CAAgB,KAAhB,EAAuB,SAAA;AACtB,YAAA;QAAA,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,4BAA1C;AACA;UACC,MAAA,GAAS,IAAI,CAAC,KAAL,CAAW,KAAK,CAAC,IAAN,CAAW,EAAX,CAAX,EADV;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,CAAT,EAHR;;QAIA,MAAM,CAAC,GAAP,GAAa,QAAA,CAAS,MAAM,CAAC,GAAhB;QACb,MAAM,CAAC,MAAP,GAAgB,QAAA,CAAS,MAAM,CAAC,MAAhB;QAChB,MAAM,CAAC,UAAP,GAAoB,QAAA,CAAS,MAAM,CAAC,UAAhB;AACpB;AAAA,aAAA,sCAAA;;UACC,IAA6B,cAA7B;YAAA,EAAE,CAAC,GAAH,GAAS,QAAA,CAAS,EAAE,CAAC,GAAZ,EAAT;;AADD;eAEA,QAAA,CAAS,IAAT,EAAe,MAAf;MAXsB,CAAvB;aAYA,YAAY,CAAC,EAAb,CAAgB,MAAhB,EAAwB,SAAC,IAAD;eACvB,KAAK,CAAC,IAAN,CAAW,IAAX;MADuB,CAAxB;IAzCiB,CAxClB;IAoFA,aAAA,EAAe,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;;QAA8B,WAAW,SAAC,KAAD,GAAA;;aACvD,QAAQ,CAAC,gBAAT,CAA0B,UAA1B,EAAsC,MAAtC,EAA8C,OAA9C,EAAuD,SAAC,GAAD,EAAM,MAAN;QACtD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,OAAO,CAAC,GAAR,CAAY,gBAAZ;QAEA,MAAM,CAAC,SAAP,GAAmB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B;QACnB,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,0BAA1C;eACA,EAAE,CAAC,UAAU,CAAC,MAAd,CAAqB,MAArB,EAA6B,QAA7B;MANsD,CAAvD;IADc,CApFf;;AAxBD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/MongoManager.js.map b/services/track-changes/app/js/MongoManager.js.map deleted file mode 100644 index af11849e38..0000000000 --- a/services/track-changes/app/js/MongoManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "MongoManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/MongoManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,MAAiB,OAAA,CAAQ,WAAR,CAAjB,EAAC,WAAD,EAAK;;EACL,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,YAAA,GAChB;IAAA,uBAAA,EAAyB,SAAC,MAAD,EAAS,QAAT;;QAAS,WAAW,SAAC,KAAD,EAAQ,MAAR,GAAA;;aAC5C,EAAE,CAAC,UACF,CAAC,IADF,CACO;QAAA,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAR;OADP,EAC4C;QAAC,IAAA,EAAM;UAAC,MAAA,EAAO,CAAC,CAAT;SAAP;OAD5C,CAEC,CAAC,IAFF,CAEQ;QAAA,CAAA,EAAG,CAAC,CAAJ;OAFR,CAGC,CAAC,KAHF,CAGQ,CAHR,CAIC,CAAC,OAJF,CAIU,SAAC,KAAD,EAAQ,iBAAR;QACR,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,iBAAkB,CAAA,CAAA,CAAlB,IAAwB,IAAvC;MAFQ,CAJV;IADwB,CAAzB;IASA,wBAAA,EAA0B,SAAC,MAAD,EAAS,QAAT;;QAAS,WAAW,SAAC,KAAD,EAAQ,MAAR,EAAgB,OAAhB,GAAA;;aAO7C,YAAY,CAAC,uBAAb,CAAqC,MAArC,EAA6C,SAAC,KAAD,EAAQ,MAAR;AAC5C,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,IAAG,cAAH;UACC,IAAG,MAAM,CAAC,MAAV;AACC,mBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EADR;WAAA,MAEK,IAAG,mBAAH;YACJ,IAAG,MAAM,CAAC,SAAV;AACC,qBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,wCAAmC,CAAE,UAArC,EADR;aAAA,MAAA;AAGC,qBAAO,QAAA,CAAS,IAAT,EAAe,MAAf,wCAAqC,CAAE,UAAvC,EAHR;aADI;WAAA,MAAA;AAMJ,mBAAO,QAAA,CAAS,IAAT,EAAe,MAAf,EAAuB,MAAM,CAAC,CAA9B,EANH;WAHN;SAAA,MAAA;iBAWC,WAAW,CAAC,oBAAZ,CAAiC,MAAjC,EAAyC,SAAC,KAAD,EAAQ,IAAR;YACxC,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,IAA2C,6CAAA,IAAgB,8CAA3D;AAAA,qBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EAAqB,IAAI,CAAC,KAA1B,EAAP;;mBACA,QAAA,CAAS,IAAT,EAAe,IAAf;UAHwC,CAAzC,EAXD;;MAF4C,CAA7C;IAPyB,CAT1B;IAkCA,iBAAA,EAAmB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;;QAAqB,WAAW,SAAC,KAAD,GAAA;;aAClD,EAAE,CAAC,UAAU,CAAC,MAAd,CAAqB;QACpB,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CADY;QAEpB,UAAA,EAAY;UAAE,OAAA,EAAS,KAAX;SAFQ;OAArB,EAGG;QACF,IAAA,EAAM;UAAE,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CAAd;SADJ;OAHH,EAKG;QACF,KAAA,EAAO,IADL;OALH,EAOG,QAPH;IADkB,CAlCnB;IA4CA,kBAAA,EAAoB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,QAAR,GAAA;;aAC3C,EAAE,CAAC,sBAAsB,CAAC,IAA1B,CAA+B;QAC9B,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CADkB;OAA/B,EAEG,SAAC,KAAD,EAAQ,OAAR;QACF,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,OAAQ,CAAA,CAAA,CAAvB;MAFE,CAFH;IADmB,CA5CpB;IAmDA,kBAAA,EAAoB,SAAC,UAAD,EAAa,QAAb,EAAuB,QAAvB;;QAAuB,WAAW,SAAC,KAAD,GAAA;;aACrD,EAAE,CAAC,sBAAsB,CAAC,MAA1B,CAAiC;QAChC,UAAA,EAAY,QAAA,CAAS,UAAT,CADoB;OAAjC,EAEG;QACF,IAAA,EAAM,QADJ;OAFH,EAIG;QACF,MAAA,EAAQ,IADN;OAJH,EAMG,QANH;IADmB,CAnDpB;IA4DA,cAAA,EAAgB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,GAAA;;aAEvC,EAAE,CAAC,UAAU,CAAC,MAAd,CAAqB;QACpB,UAAA,EAAY,QAAA,CAAS,UAAT,CADQ;QAEpB,SAAA,EAAW,IAFS;QAGpB,SAAA,EAAW;UAAC,OAAA,EAAS,IAAV;SAHS;OAArB,EAIG;QACF,IAAA,EAAM;UAAC,SAAA,EAAW,KAAZ;SADJ;QAEF,MAAA,EAAQ;UAAC,SAAA,EAAW,EAAZ;SAFN;OAJH,EAOG;QACF,KAAA,EAAO,IADL;OAPH,EASG,QATH;IAFe,CA5DhB;IAyEA,aAAA,EAAe,SAAA;MAEd,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,MAAA,EAAQ,CAAV;QAAa,CAAA,EAAG,CAAhB;OAA1B,EAA+C;QAAE,UAAA,EAAY,IAAd;OAA/C;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,UAAA,EAAY,CAAd;QAAiB,aAAA,EAAe,CAAhC;OAA1B,EAA+D;QAAE,UAAA,EAAY,IAAd;OAA/D;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,MAAA,EAAQ,CAAV;QAAa,UAAA,EAAY,CAAzB;OAA1B,EAAwD;QAAE,UAAA,EAAY,IAAd;OAAxD;MAEA,EAAE,CAAC,sBAAsB,CAAC,WAA1B,CAAsC;QAAE,UAAA,EAAY,CAAd;OAAtC,EAAyD;QAAE,UAAA,EAAY,IAAd;OAAzD;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,SAAA,EAAW,CAAb;OAA1B,EAA4C;QAAE,kBAAA,EAAoB,CAAtB;QAAyB,UAAA,EAAY,IAArC;OAA5C;MAEA,EAAE,CAAC,UAAU,CAAC,WAAd,CAA0B;QAAE,YAAA,EAAc,CAAhB;OAA1B,EAA+C;QAAE,UAAA,EAAY,IAAd;OAA/C;aAEA,EAAE,CAAC,eAAe,CAAC,WAAnB,CAA+B;QAAE,UAAA,EAAY,CAAd;OAA/B,EAAkD;QAAE,UAAA,EAAY,IAAd;OAAlD;IAdc,CAzEf;;;EA0FD,CACC,yBADD,EAEC,oBAFD,EAGC,oBAHD,CAIC,CAAC,GAJF,CAIM,SAAC,MAAD;WACL,OAAO,CAAC,eAAR,CAAwB,YAAxB,EAAsC,MAAtC,EAA8C,oBAA9C,EAAoE,MAApE;EADK,CAJN;AAlGA" -} \ No newline at end of file diff --git a/services/track-changes/app/js/PackManager.js.map b/services/track-changes/app/js/PackManager.js.map deleted file mode 100644 index bc7445c5e4..0000000000 --- a/services/track-changes/app/js/PackManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "PackManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/PackManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,MAAuB,OAAA,CAAQ,WAAR,CAAvB,EAAC,WAAD,EAAK,uBAAL,EAAe;;EACf,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,QAAA,GAAW,OAAA,CAAQ,YAAR;;EACX,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,eAAA,GAAkB,OAAA,CAAQ,mBAAR;;EAClB,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,IAAA,GAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;;EAoC3B,IAAA,GAAO,EAAA,GAAK,IAAL,GAAY;;EAEnB,MAAM,CAAC,OAAP,GAAiB,WAAA,GAEhB;IAAA,QAAA,EAAW,IAAA,GAAK,IAAhB;IACA,SAAA,EAAW,IADX;IAGA,uBAAA,EAAyB,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,UAAjC,EAA6C,SAA7C,EAAwD,QAAxD;AACxB,UAAA;;QADgF,WAAW,SAAC,KAAD,GAAA;;MAC3F,IAAqB,UAAU,CAAC,MAAX,KAAqB,CAA1C;AAAA,eAAO,QAAA,CAAA,EAAP;;MAGA,IAAqB,8DAAA,IAA2B,CAAI,SAApD;QAAA,UAAA,GAAa,KAAb;;MAEA,cAAA,GAAiB;MACjB,gBAAA,GAAmB,UAAU,CAAC,KAAX,CAAA;MAEnB,CAAA,yBAAI,UAAU,CAAE,WAAZ,IAAiB;MACrB,EAAA,yBAAK,UAAU,CAAE,YAAZ,IAAkB;AAEvB,aAAM,gBAAgB,CAAC,MAAjB,IAA4B,CAAA,GAAI,WAAW,CAAC,SAA5C,IAA0D,EAAA,GAAK,WAAW,CAAC,QAAjF;QACC,UAAA,GAAa,gBAAiB,CAAA,CAAA;QAC9B,cAAA,GAAiB,IAAI,CAAC,mBAAL,CAAyB,UAAzB;QACjB,IAAG,cAAA,GAAiB,EAAjB,GAAsB,WAAW,CAAC,QAAlC,IAA+C,CAAA,GAAI,CAAtD;AACC,gBADD;;QAEA,CAAA;QACA,EAAA,IAAM;QACN,cAAc,CAAC,IAAf,CAAoB,gBAAgB,CAAC,KAAjB,CAAA,CAApB;MAPD;aASA,WAAW,CAAC,sBAAZ,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,UAAvD,EAAmE,cAAnE,EAAmF,SAAnF,EAA8F,SAAC,KAAD;QAC7F,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,IAAxD,EAA8D,gBAA9D,EAAgF,SAAhF,EAA2F,QAA3F;MAF6F,CAA9F;IArBwB,CAHzB;IA4BA,sBAAA,EAAwB,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,UAAjC,EAA6C,SAA7C,EAAwD,QAAxD;AACvB,UAAA;;QAD+E,WAAW,SAAC,KAAD,GAAA;;MAC1F,IAAqB,UAAU,CAAC,MAAX,KAAqB,CAA1C;AAAA,eAAO,QAAA,CAAA,EAAP;;MAEA,SAAA,GAAY;MAEZ,IAAG,kBAAH;QACC,IAAG,CAAI,SAAJ,IAAsB,8BAAzB;UAEC,SAAA,GAAY,KAFb;;QAGA,GAAA,GAAM,IAAI,CAAC,GAAL,CAAA,CAAA,2CAA4B,CAAE;QACpC,IAAG,SAAA,IAAc,8BAAd,IAAwC,GAAA,GAAM,CAAA,GAAI,IAArD;UAEC,SAAA,GAAY,KAFb;SALD;;MASA,IAAG,SAAH;eACC,WAAW,CAAC,2BAAZ,CAAwC,UAAxC,EAAoD,MAApD,EAA4D,UAA5D,EAAwE,UAAxE,EAAoF,SAApF,EAA+F,QAA/F,EADD;OAAA,MAAA;eAGC,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,UAAzD,EAAqE,SAArE,EAAgF,QAAhF,EAHD;;IAduB,CA5BxB;IA+CA,wBAAA,EAA0B,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,SAAjC,EAA4C,QAA5C;AACzB,UAAA;;QADqE,WAAW,SAAC,KAAD,GAAA;;MAChF,KAAA,GAAQ,UAAW,CAAA,CAAA;MACnB,IAAA,GAAO,UAAW,CAAA,UAAU,CAAC,MAAX,GAAoB,CAApB;MAClB,CAAA,GAAI,UAAU,CAAC;MACf,EAAA,GAAK,IAAI,CAAC,mBAAL,CAAyB,UAAzB;MACL,OAAA,GACC;QAAA,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CAAZ;QACA,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CADR;QAEA,IAAA,EAAM,UAFN;QAGA,CAAA,EAAG,CAHH;QAIA,EAAA,EAAI,EAJJ;QAKA,IAAA,EACC;UAAA,QAAA,EAAU,KAAK,CAAC,IAAI,CAAC,QAArB;UACA,MAAA,EAAQ,IAAI,CAAC,IAAI,CAAC,MADlB;SAND;QAQA,CAAA,EAAG,KAAK,CAAC,CART;QASA,KAAA,EAAO,IAAI,CAAC,CATZ;QAUA,SAAA,EAAW,SAVX;;MAWD,IAAG,SAAH;QACC,OAAO,CAAC,SAAR,GAAoB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B;QACpB,OAAO,CAAC,YAAR,GAAuB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,EAAA,GAAK,IAA3B,EAFxB;;MAGA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,YAAA,UAArB;OAAX,EAA6C,iCAA7C;aACA,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,OAAnB,EAA4B,SAAC,GAAD,EAAM,MAAN;QAC3B,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,OAAO,CAAC,GAAR,CAAY,cAAA,GAAiB,CAAG,SAAH,GAAkB,WAAlB,GAAmC,WAAnC,CAA7B;QACA,IAAG,SAAH;AACC,iBAAO,QAAA,CAAA,EADR;SAAA,MAAA;iBAGC,WAAW,CAAC,WAAZ,CAAwB,UAAxB,EAAoC,MAApC,EAA4C,QAA5C,EAHD;;MAH2B,CAA5B;IArByB,CA/C1B;IA4EA,2BAAA,EAA6B,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,UAAjC,EAA6C,SAA7C,EAAwD,QAAxD;AAC5B,UAAA;;QADoF,WAAW,SAAC,KAAD,GAAA;;MAC/F,KAAA,GAAQ,UAAW,CAAA,CAAA;MACnB,IAAA,GAAO,UAAW,CAAA,UAAU,CAAC,MAAX,GAAoB,CAApB;MAClB,CAAA,GAAI,UAAU,CAAC;MACf,EAAA,GAAK,IAAI,CAAC,mBAAL,CAAyB,UAAzB;MACL,KAAA,GACC;QAAA,GAAA,EAAK,UAAU,CAAC,GAAhB;QACA,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CADZ;QAEA,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAFR;QAGA,IAAA,EAAM;UAAC,OAAA,EAAS,IAAV;SAHN;;MAID,MAAA,GACC;QAAA,KAAA,EACC;UAAA,MAAA,EAAQ;YAAC,KAAA,EAAO,UAAR;WAAR;SADD;QAEA,IAAA,EACC;UAAA,GAAA,EAAK,CAAL;UACA,IAAA,EAAO,EADP;SAHD;QAKA,IAAA,EACC;UAAA,aAAA,EAAe,IAAI,CAAC,IAAI,CAAC,MAAzB;UACA,OAAA,EAAS,IAAI,CAAC,CADd;SAND;;MAQD,IAAG,UAAU,CAAC,SAAX,IAAyB,SAA5B;QACC,MAAM,CAAC,IAAI,CAAC,SAAZ,GAAwB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B,EADzB;;MAEA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,YAAA,UAArB;QAAiC,YAAA,UAAjC;OAAX,EAAyD,oCAAzD;MACA,OAAO,CAAC,GAAR,CAAY,cAAA,GAAiB,CAAG,SAAH,GAAkB,WAAlB,GAAmC,WAAnC,CAA7B;aACA,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAAC,OAAA,KAAD;QAAQ,QAAA,MAAR;QAAgB,CAAA,GAAA,CAAA,EAAI,IAApB;QAA0B,MAAA,EAAO;UAAC,IAAA,EAAK,CAAN;UAAQ,KAAA,EAAM,CAAd;SAAjC;OAA5B,EAAgF,QAAhF;IAvB4B,CA5E7B;IAuGA,oBAAA,EAAsB,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,SAAlC,EAA6C,QAA7C;;QAA6C,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAC7E,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,WAAxD,EAAqE,SAArE,EAAgF,SAAC,KAAD;AAC/E,YAAA;QAAA,KAAA,GAAQ;UAAC,MAAA,EAAO,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAR;;QACR,IAA8B,iBAA9B;UAAA,KAAK,CAAC,CAAN,GAAU;YAAC,IAAA,EAAK,SAAN;YAAV;;QACA,IAAoC,mBAApC;UAAA,KAAK,CAAC,KAAN,GAAc;YAAC,IAAA,EAAK,WAAN;YAAd;;eAEA,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,KAAnB,CAAyB,CAAC,IAA1B,CAA+B;UAAC,CAAA,EAAE,CAAC,CAAJ;SAA/B,EAAuC,SAAC,GAAD,EAAM,MAAN;AACtC,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UAEA,OAAA,GAAU;UACV,SAAA,GAAY,SAAC,EAAD,EAAK,IAAL,EAAW,EAAX;YACX,IAAgB,qBAAA,IAAiB,EAAE,CAAC,CAAH,GAAO,WAAxC;AAAA,qBAAO,MAAP;;YACA,IAAgB,mBAAA,IAAe,EAAE,CAAC,CAAH,GAAO,SAAtC;AAAA,qBAAO,MAAP;;AACA,mBAAO;UAHI;AAIZ,eAAA,wCAAA;;AAEC;AAAA,iBAAA,wCAAA;;oBAAyC,SAAA,CAAU,EAAV,EAAc,WAAd,EAA2B,SAA3B;;;cACxC,EAAE,CAAC,UAAH,GAAgB,UAAU,CAAC;cAC3B,EAAE,CAAC,MAAH,GAAY,UAAU,CAAC;cAEvB,OAAO,CAAC,IAAR,CAAa,EAAb;AAJD;AAFD;iBAOA,QAAA,CAAS,IAAT,EAAe,OAAf;QAfsC,CAAvC;MAL+E,CAAhF;IADqB,CAvGtB;IA8HA,uBAAA,EAAyB,SAAC,UAAD,EAAa,MAAb,EAAqB,WAArB,EAAkC,SAAlC,EAA6C,QAA7C;aACxB,WAAW,CAAC,QAAZ,CAAqB,MAArB,EAA6B,SAAC,GAAD,EAAM,WAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,UAAA,0BAAa,WAAW,CAAE,eAAb,IAAsB;QACnC,WAAA,GAAc,SAAC,IAAD,EAAO,IAAP,EAAa,EAAb;UACb,IAAgB,qBAAA,IAAiB,IAAI,CAAC,KAAL,GAAa,WAA9C;AAAA,mBAAO,MAAP;;UACA,IAAgB,mBAAA,IAAe,IAAI,CAAC,CAAL,GAAS,SAAxC;AAAA,mBAAO,MAAP;;AACA,iBAAO;QAHM;QAId,SAAA;;AAAa;eAAA,4CAAA;;gBAAqC,WAAA,CAAY,IAAZ,EAAkB,WAAlB,EAA+B,SAA/B;2BAArC,IAAI,CAAC;;AAAL;;;QACb,IAAG,SAAS,CAAC,MAAb;iBACC,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAnD,EAA8D,QAA9D,EADD;SAAA,MAAA;iBAGC,QAAA,CAAA,EAHD;;MAR4B,CAA7B;IADwB,CA9HzB;IA4IA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB,EAA+B,QAA/B;AACnB,UAAA;aAAA,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB;QAAC,GAAA,EAAK;UAAC,GAAA;;AAAM;iBAAA,0CAAA;;2BAAA,QAAA,CAAS,EAAT;AAAA;;cAAP;SAAN;OAAnB,EAAoE;QAAC,GAAA,EAAI,CAAL;OAApE,EAA6E,SAAC,GAAD,EAAM,WAAN;AAC5E,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,UAAA;;AAAc;eAAA,0CAAA;;yBAAA,EAAE,CAAC,QAAH,CAAA;AAAA;;;QACd,aAAA;;AAAiB;eAAA,6CAAA;;yBAAA,IAAI,CAAC,GAAG,CAAC,QAAT,CAAA;AAAA;;;QACjB,cAAA,GAAiB,CAAC,CAAC,UAAF,CAAa,UAAb,EAAyB,aAAzB;QACjB,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,eAAA,aAArB;UAAoC,YAAA,UAApC;UAAgD,gBAAA,cAAhD;SAAX,EAA4E,gBAA5E;QACA,IAAqB,cAAc,CAAC,MAAf,KAAyB,CAA9C;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,KAAK,CAAC,SAAN,CAAgB,cAAhB,EAAgC,CAAhC,EAAmC,SAAC,OAAD,EAAU,EAAV;iBAClC,QAAQ,CAAC,aAAT,CAAuB,UAAvB,EAAmC,MAAnC,EAA2C,OAA3C,EAAoD,EAApD;QADkC,CAAnC,EAEE,SAAC,GAAD;UACD,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;WAAX,EAAiC,kBAAjC;iBACA,QAAA,CAAA;QAHC,CAFF;MAP4E,CAA7E;IADmB,CA5IpB;IA6JA,mBAAA,EAAqB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aAEpB,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB;QAAC,UAAA,EAAY,QAAA,CAAS,UAAT,CAAb;OAAnB,EAAsD;QAAC,IAAA,EAAK,KAAN;OAAtD,CAAmE,CAAC,IAApE,CAAyE;QAAC,aAAA,EAAc,CAAC,CAAhB;OAAzE,EAA6F,SAAC,GAAD,EAAM,KAAN;AAC5F,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,QAAA,GAAW;QACX,OAAA,GAAU;AACV,aAAA,uCAAA;;UACC,QAAQ,CAAC,IAAT,CAAc,IAAd;UACA,OAAQ,CAAA,IAAI,CAAC,GAAL,CAAR,GAAoB;AAFrB;eAGA,EAAE,CAAC,eAAe,CAAC,IAAnB,CAAwB;UAAC,UAAA,EAAY,QAAA,CAAS,UAAT,CAAb;SAAxB,EAA4D,SAAC,GAAD,EAAM,OAAN;AAC3D,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;AACA,eAAA,2CAAA;;AACC;AAAA,iBAAA,wCAAA;;oBAA6B,CAAI,OAAQ,CAAA,IAAI,CAAC,GAAL;;;cACxC,IAAI,CAAC,UAAL,GAAkB,KAAK,CAAC;cACxB,IAAI,CAAC,MAAL,GAAc,KAAK,CAAC;cACpB,IAAI,CAAC,SAAL,GAAiB;cACjB,QAAQ,CAAC,IAAT,CAAc,IAAd;cACA,OAAQ,CAAA,IAAI,CAAC,GAAL,CAAR,GAAoB;AALrB;AADD;iBAOA,QAAA,CAAS,IAAT,EAAe,IAAI,eAAJ,CAAoB,QAApB,EAA8B,MAA9B,EAAsC,WAAW,CAAC,WAAlD,CAAf;QAT2D,CAA5D;MAP4F,CAA7F;IAFoB,CA7JrB;IAiLA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aACZ,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB;QAAC,GAAA,EAAK,OAAN;OAAtB,EAAsC,SAAC,GAAD,EAAM,IAAN;QACrC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAO,YAAP;iBACC,QAAQ,CAAC,aAAT,CAAuB,UAAvB,EAAmC,MAAnC,EAA2C,OAA3C,EAAoD,QAApD,EADD;SAAA,MAEK,IAAG,wBAAA,IAAoB,IAAI,CAAC,SAAL,KAAkB,KAAzC;iBAGJ,WAAW,CAAC,WAAZ,CAAwB,IAAxB,EAA8B,QAA9B,EAHI;SAAA,MAAA;iBAOJ,QAAA,CAAS,IAAT,EAAe,IAAf,EAPI;;MAJgC,CAAtC;IADY,CAjLb;IA+LA,WAAA,EAAa,SAAC,IAAD,EAAO,QAAP;MACZ,IAAG,IAAI,CAAC,SAAL,GAAiB,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B,CAApB;eAEC,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;UAC3B,KAAA,EAAO;YAAC,GAAA,EAAK,IAAI,CAAC,GAAX;WADoB;UAE3B,MAAA,EAAQ;YAAC,IAAA,EAAM;cAAC,SAAA,EAAW,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B,CAAZ;aAAP;WAFmB;SAA5B,EAGG,SAAC,GAAD;AACF,iBAAO,QAAA,CAAS,GAAT,EAAc,IAAd;QADL,CAHH,EAFD;OAAA,MAAA;eAQC,QAAA,CAAS,IAAT,EAAe,IAAf,EARD;;IADY,CA/Lb;IA4MA,QAAA,EAAU,SAAC,MAAD,EAAS,QAAT;aACT,EAAE,CAAC,eAAe,CAAC,OAAnB,CAA2B;QAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;OAA3B,EAA8D,QAA9D;IADS,CA5MV;IA+MA,gBAAA,EAAkB,SAAC,MAAD,EAAS,OAAT,EAAkB,QAAlB;aACjB,EAAE,CAAC,eAAe,CAAC,OAAnB,CAA2B;QAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;QAAkC,WAAA,EAAa,OAA/C;OAA3B,EAAoF;QAAC,SAAA,EAAU,CAAX;OAApF,EAAmG,QAAnG;IADiB,CA/MlB;IAkNA,oBAAA,EAAsB,SAAC,MAAD,EAAS,QAAT;aACrB,EAAE,CAAC,eAAe,CAAC,OAAnB,CAA2B;QAAC,GAAA,EAAK,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAN;OAA3B,EAA+D;QAAC,KAAA,EAAM;UAAC,MAAA,EAAO,CAAC,CAAT;SAAP;OAA/D,EAAoF,SAAC,GAAD,EAAM,SAAN;QACnF,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,iBAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,QAAA,CAAS,IAAT,EAAc,SAAU,CAAA,CAAA,CAAxB;MAHmF,CAApF;IADqB,CAlNtB;IAwNA,gBAAA,EAAkB,SAAC,MAAD,EAAS,QAAT;aACjB,WAAW,CAAC,QAAZ,CAAqB,MAArB,EAA6B,SAAC,GAAD,EAAM,KAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;AACA;AAAA,aAAA,sCAAA;;UACC,KAAM,CAAA,IAAI,CAAC,GAAL,CAAN,GAAkB;AADnB;eAEA,QAAA,CAAS,IAAT,EAAe,KAAf;MAL4B,CAA7B;IADiB,CAxNlB;IAgOA,eAAA,EAAiB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aAChB,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,GAAD,EAAM,KAAN;QAElD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,KAA7D,EAAoE,QAApE;MAJkD,CAAnD;IADgB,CAhOjB;IAuOA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aAEZ,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,GAAD,EAAM,QAAN;QAClD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,kBAAJ,IAAiB,QAAQ,CAAC,MAAT,KAAmB,CAAzD;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,QAA7D,EAAuE,SAAC,GAAD;UACtE,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,UAAA,QAArB;WAAX,EAA2C,0BAA3C;iBACA,QAAA,CAAA;QAHsE,CAAvE;MAHkD,CAAnD;IAFY,CAvOb;IAiPA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;AACnB,UAAA;MAAA,KAAA,GAAQ;QAAE,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAV;QAAuC,SAAA,EAAW;UAAC,OAAA,EAAQ,KAAT;SAAlD;;aACR,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,KAAnB,EAA0B;QAAC,IAAA,EAAK,KAAN;OAA1B,CAAuC,CAAC,IAAxC,CAA6C;QAAC,CAAA,EAAE,CAAH;OAA7C,EAAoD,SAAC,GAAD,EAAM,KAAN;AACnD,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;QACA,IAAqB,kBAAI,KAAK,CAAE,gBAAhC;AAAA,iBAAO,QAAA,CAAA,EAAP;;QACA,IAAA,GAAO,KAAK,CAAC,GAAN,CAAA;QACP,IAAoB,IAAI,CAAC,SAAzB;UAAA,KAAK,CAAC,IAAN,CAAW,IAAX,EAAA;;eACA,QAAA,CAAS,IAAT,EAAe,KAAf;MANmD,CAApD;IAFmB,CAjPpB;IA2PA,SAAA,EAAW,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;AACV,UAAA;MAAA,KAAA,GAAQ;QAAE,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAV;QAAuC,SAAA,EAAW;UAAC,OAAA,EAAQ,KAAT;SAAlD;;aACR,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB,KAAnB,EAA0B;QAAC,IAAA,EAAK,KAAN;OAA1B,CAAuC,CAAC,IAAxC,CAA6C;QAAC,CAAA,EAAE,CAAH;OAA7C,EAAoD,SAAC,GAAD,EAAM,KAAN;QACnD,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAyB,aAAzB;AAAA,iBAAO,QAAA,CAAA,EAAP;;QACA,IAAqB,kBAAI,KAAK,CAAE,gBAAhC;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,KAAf;MAJmD,CAApD;IAFU,CA3PX;IAmQA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACnB,WAAW,CAAC,gBAAZ,CAA6B,MAA7B,EAAqC,SAAC,GAAD,EAAM,WAAN;QACpC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;eACA,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,GAAD,EAAM,YAAN;AAClD,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,IAAyB,oBAAzB;AAAA,mBAAO,QAAA,CAAA,EAAP;;UAEA,QAAA;;AAAY;iBAAA,8CAAA;;kBAAuC;6BAAvC;;AAAA;;;UACZ,QAAA;;AAAY;iBAAA,0CAAA;;2BAAA,CAAC,CAAC,IAAF,CAAO,IAAP,EAAa,QAAb,EAAuB,YAAvB,EAAqC,GAArC,EAA0C,IAA1C,EAAgD,cAAhD,EAAgE,WAAhE;AAAA;;;UACZ,IAAG,QAAQ,CAAC,MAAZ;YACC,MAAM,CAAC,GAAP,CAAW;cAAC,YAAA,UAAD;cAAa,QAAA,MAAb;cAAqB,CAAA,EAAG,QAAQ,CAAC,MAAjC;aAAX,EAAqD,iBAArD,EADD;;iBAEA,QAAA,CAAS,IAAT,EAAe,QAAf;QARkD,CAAnD;MAFoC,CAArC;IADmB,CAnQpB;IAgRA,4BAAA,EAA8B,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB,EAA+B,QAA/B;aAC7B,WAAW,CAAC,WAAZ,CACC,IAAI,CAAC,gBAAL,CAAsB;QAAC,QAAA,MAAD;OAAtB,CADD,EAEC,SAAC,WAAD;eACC,WAAW,CAAC,qBAAZ,CAAkC,UAAlC,EAA8C,MAA9C,EAAsD,QAAtD,EAAgE,WAAhE;MADD,CAFD,EAIC,QAJD;IAD6B,CAhR9B;IAwRA,qBAAA,EAAuB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB,EAA+B,QAA/B;aACtB,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;SADyB;QAEhC,MAAA,EACC;UAAA,YAAA,EAAc;YAAA,UAAA,EAAY,QAAA,CAAS,UAAU,CAAC,QAAX,CAAA,CAAT,CAAZ;WAAd;UACA,KAAA,EACC;YAAA,KAAA,EAAO;cAAC,KAAA,EAAO,QAAR;cAAkB,KAAA,EAAO;gBAAC,CAAA,EAAG,CAAJ;eAAzB;aAAP;WAFD;SAH+B;QAMhC,MAAA,EAAQ,IANwB;OAAjC,EAOG,QAPH;IADsB,CAxRvB;IAoSA,WAAA,EAAa,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;AACZ,UAAA;MAAA,gBAAA,GAAmB,SAAC,GAAD,EAAM,EAAN;QAClB,IAAG,WAAH;iBACC,WAAW,CAAC,4BAAZ,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,OAA7D,EAAsE,SAAC,IAAD;YACrE,IAAmB,YAAnB;AAAA,qBAAO,EAAA,CAAG,IAAH,EAAP;;AACA,mBAAO,EAAA,CAAG,GAAH;UAF8D,CAAtE,EADD;SAAA,MAAA;iBAKC,EAAA,CAAA,EALD;;MADkB;aAOnB,KAAK,CAAC,MAAN,CAAa;QACZ,SAAC,EAAD;iBACC,WAAW,CAAC,yBAAZ,CAAsC,UAAtC,EAAkD,MAAlD,EAA0D,OAA1D,EAAmE,EAAnE;QADD,CADY,EAGZ,SAAC,EAAD;iBACC,WAAW,CAAC,2BAAZ,CAAwC,UAAxC,EAAoD,MAApD,EAA4D,OAA5D,EAAqE,EAArE;QADD,CAHY,EAKZ,SAAC,EAAD;iBACC,QAAQ,CAAC,WAAT,CAAqB,UAArB,EAAiC,MAAjC,EAAyC,OAAzC,EAAkD,SAAC,GAAD;mBACjD,gBAAA,CAAiB,GAAjB,EAAsB,EAAtB;UADiD,CAAlD;QADD,CALY,EAQZ,SAAC,EAAD;iBACC,WAAW,CAAC,iBAAZ,CAA8B,UAA9B,EAA0C,MAA1C,EAAkD,OAAlD,EAA2D,SAAC,GAAD;mBAC1D,gBAAA,CAAiB,GAAjB,EAAsB,EAAtB;UAD0D,CAA3D;QADD,CARY,EAWZ,SAAC,EAAD;iBACC,WAAW,CAAC,kBAAZ,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,OAAnD,EAA4D,EAA5D;QADD,CAXY,EAaZ,SAAC,EAAD;iBACC,WAAW,CAAC,oBAAZ,CAAiC,UAAjC,EAA6C,MAA7C,EAAqD,OAArD,EAA8D,QAA9D;QADD,CAbY;OAAb,EAeG,QAfH;IARY,CApSb;IA8TA,iBAAA,EAAmB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aAClB,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB;QAAC,GAAA,EAAK,OAAN;OAAtB,EAAsC,SAAC,GAAD,EAAM,IAAN;QACrC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAmD,YAAnD;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gBAAV,CAAT,EAAP;;eACA,QAAQ,CAAC,gBAAT,CAA0B,UAA1B,EAAsC,MAAtC,EAA8C,OAA9C,EAAuD,SAAC,GAAD,EAAM,MAAN;AACtD,cAAA;UAAA,OAAO,MAAM,CAAC;UACd,OAAO,IAAI,CAAC;AAEZ;AAAA,eAAA,sCAAA;;YACC,IAA2B,MAAO,CAAA,GAAA,CAAI,CAAC,MAAZ,CAAmB,IAAK,CAAA,GAAA,CAAxB,CAA3B;cAAA,MAAO,CAAA,GAAA,CAAP,GAAc,IAAK,CAAA,GAAA,EAAnB;;AADD;AAEA;AAAA,eAAA,gDAAA;;YACC,IAA6B,gBAAA,IAAY,EAAE,CAAC,GAAG,CAAC,MAAP,CAAc,IAAI,CAAC,IAAK,CAAA,CAAA,CAAE,CAAC,GAA3B,CAAzC;cAAA,EAAE,CAAC,GAAH,GAAS,IAAI,CAAC,IAAK,CAAA,CAAA,CAAE,CAAC,IAAtB;;AADD;UAEA,IAAG,CAAC,CAAC,OAAF,CAAU,IAAV,EAAgB,MAAhB,CAAH;mBACC,QAAA,CAAA,EADD;WAAA,MAAA;YAGC,MAAM,CAAC,GAAP,CAAW;cAAC,MAAA,IAAD;cAAO,QAAA,MAAP;cAAe,QAAA,EAAU,IAAI,CAAC,SAAL,CAAe,IAAf,CAAA,KAAwB,IAAI,CAAC,SAAL,CAAe,MAAf,CAAjD;aAAX,EAAqF,iCAArF;mBACA,QAAA,CAAS,IAAI,KAAJ,CAAU,qDAAV,CAAT,EAJD;;QARsD,CAAvD;MAHqC,CAAtC;IADkB,CA9TnB;IAiVA,YAAA,EAAc,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACb,WAAW,CAAC,SAAZ,CAAsB,UAAtB,EAAkC,MAAlC,EAA0C,SAAC,GAAD,EAAM,KAAN;QACzC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAqB,kBAAI,KAAK,CAAE,gBAAhC;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,cAAZ,CAA2B,UAA3B,EAAuC,MAAvC,EAA+C,KAAM,CAAA,CAAA,CAAE,CAAC,GAAxD,EAA6D,QAA7D;MAHyC,CAA1C;IADa,CAjVd;IAuVA,YAAA,EAAc,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACb,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,IAAxD,EAA8D,IAA9D,EAAoE,QAApE;IADa,CAvVd;IA6VA,cAAA,EAAgB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;AACf,UAAA;MAAA,aAAA,GAAgB,SAAC,GAAD;eACf,WAAW,CAAC,iBAAZ,CAA8B,UAA9B,EAA0C,MAA1C,EAAkD,OAAlD,EAA2D,SAAC,IAAD;UAC1D,IAAyB,YAAzB;AAAA,mBAAO,QAAA,CAAS,IAAT,EAAP;;iBACA,QAAA,CAAS,GAAT;QAF0D,CAA3D;MADe;MAIhB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,sBAAjC;aACA,EAAE,CAAC,UAAU,CAAC,OAAd,CAAsB;QAAC,GAAA,EAAI,OAAL;OAAtB,EAAqC,SAAC,GAAD,EAAM,IAAN;QACpC,IAA6B,WAA7B;AAAA,iBAAO,aAAA,CAAc,GAAd,EAAP;;QACA,IAA8B,YAA9B;AAAA,iBAAO,aAAA,CAAA,EAAP;;QACA,IAAqB,sBAArB;AAAA,iBAAO,QAAA,CAAA,EAAP;;eACA,WAAW,CAAC,gBAAZ,CAA6B,UAA7B,EAAyC,MAAzC,EAAiD,IAAI,CAAC,GAAtD,EAA2D,IAA3D,EAAiE,SAAC,GAAD;UAChE,IAA6B,WAA7B;AAAA,mBAAO,aAAA,CAAc,GAAd,EAAP;;iBACA,WAAW,CAAC,mBAAZ,CAAgC,UAAhC,EAA4C,MAA5C,EAAoD,SAAC,GAAD;YACnD,IAA6B,WAA7B;AAAA,qBAAO,aAAA,CAAc,GAAd,EAAP;;mBACA,WAAW,CAAC,mBAAZ,CAAgC,UAAhC,EAA4C,MAA5C,EAAoD,SAAC,GAAD,EAAM,eAAN;cACnD,IAA6B,WAA7B;AAAA,uBAAO,aAAA,CAAc,GAAd,EAAP;;cACA,IAAG,4BAAI,eAAe,CAAE,gBAAxB;gBACC,MAAM,CAAC,GAAP,CAAW;kBAAC,YAAA,UAAD;kBAAa,QAAA,MAAb;iBAAX,EAAiC,yBAAjC;AACA,uBAAO,aAAA,CAAA,EAFR;;qBAGA,KAAK,CAAC,UAAN,CAAiB,eAAjB,EAAkC,SAAC,IAAD,EAAO,EAAP;uBACjC,WAAW,CAAC,WAAZ,CAAwB,UAAxB,EAAoC,MAApC,EAA4C,IAAI,CAAC,GAAjD,EAAsD,EAAtD;cADiC,CAAlC,EAEE,SAAC,GAAD;gBACD,IAA6B,WAA7B;AAAA,yBAAO,aAAA,CAAc,GAAd,EAAP;;gBACA,MAAM,CAAC,GAAP,CAAY;kBAAC,YAAA,UAAD;kBAAa,QAAA,MAAb;iBAAZ,EAAkC,iBAAlC;uBACA,aAAA,CAAA;cAHC,CAFF;YALmD,CAApD;UAFmD,CAApD;QAFgE,CAAjE;MAJoC,CAArC;IANe,CA7VhB;IAuXA,gBAAA,EAAkB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,IAA9B,EAAoC,QAApC;AACjB,UAAA;MAAA,EAAA,GAAK,IAAI,CAAC,EAAL,GAAU,CAAC,IAAA,GAAO,IAAR;MACf,CAAA,GAAI,IAAI,CAAC,CAAL,GAAS;MACb,GAAA,GAAM,CAAC,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,IAAI,CAAC,IAAI,CAAC,MAAxB,CAAA,GAAkC;MACxC,IAAG,GAAA,GAAM,EAAT;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;SAAX,EAA+C,uBAA/C;AACA,eAAO,QAAA,CAAA,EAFR;;MAIA,iBAAA,GAAoB,EAAA,GAAK;MACzB,IAAG,EAAA,GAAK,iBAAL,IAA0B,CAAA,GAAI,iBAA9B,IAAmD,GAAA,GAAM,EAA5D;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;UAAmC,mBAAA,iBAAnC;UAAsD,IAAA,EAAtD;UAA0D,GAAA,CAA1D;SAAX,EAAyE,yBAAzE;eACA,WAAW,CAAC,2BAAZ,CAAwC,UAAxC,EAAoD,MAApD,EAA4D,OAA5D,EAAqE,QAArE,EAFD;OAAA,MAAA;QAIC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;UAA8B,KAAA,GAA9B;UAAmC,mBAAA,iBAAnC;UAAsD,IAAA,EAAtD;UAA0D,GAAA,CAA1D;SAAX,EAAyE,iCAAzE;eACA,QAAA,CAAA,EALD;;IATiB,CAvXlB;IAuYA,2BAAA,EAA6B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aAC5B,WAAW,CAAC,WAAZ,CACC,IAAI,CAAC,WAAL,CAAiB;QAAC,QAAA,MAAD;OAAjB,CADD,EAEC,SAAC,WAAD;eACC,WAAW,CAAC,oBAAZ,CAAiC,UAAjC,EAA6C,MAA7C,EAAqD,OAArD,EAA8D,WAA9D;MADD,CAFD,EAIC,QAJD;IAD4B,CAvY7B;IA+YA,oBAAA,EAAsB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MACrB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,2BAA1C;aACA,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAC3B,KAAA,EAAO;UAAC,GAAA,EAAK,OAAN;SADoB;QAE3B,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,SAAA,EAAW,IAAZ;WAAP;SAFmB;OAA5B,EAGG,QAHH;IAFqB,CA/YtB;IAuZA,mBAAA,EAAqB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;MACpB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,qBAAjC;aACA,WAAW,CAAC,gBAAZ,CAA6B,MAA7B,EAAqC,SAAC,GAAD,EAAM,KAAN;QACpC,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAO,aAAP;iBACC,WAAW,CAAC,eAAZ,CAA4B,UAA5B,EAAwC,MAAxC,EAAgD,QAAhD,EADD;SAAA,MAAA;iBAGC,WAAW,CAAC,WAAZ,CAAwB,UAAxB,EAAoC,MAApC,EAA4C,QAA5C,EAHD;;MAFoC,CAArC;IAFoB,CAvZrB;IAgaA,iBAAA,EAAmB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAClB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,yBAA1C;aACA,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAC3B,KAAA,EAAO;UAAC,GAAA,EAAK,OAAN;SADoB;QAE3B,MAAA,EAAQ;UAAC,YAAA,EAAc;YAAC,cAAA,EAAe,IAAhB;WAAf;SAFmB;OAA5B,EAGG,QAHH;IAFkB,CAhanB;IAuaA,mBAAA,EAAqB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;aACpB,WAAW,CAAC,QAAZ,CAAqB,MAArB,EAA6B,SAAC,GAAD,EAAM,WAAN;AAC5B,YAAA;QAAA,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,UAAA,0BAAa,WAAW,CAAE,eAAb,IAAsB;QACnC,eAAA;;AAAmB;eAAA,4CAAA;;gBAAqC;2BAArC;;AAAA;;;QACnB,IAAG,eAAe,CAAC,MAAnB;UACC,MAAM,CAAC,GAAP,CAAW;YAAC,YAAA,UAAD;YAAa,QAAA,MAAb;YAAqB,CAAA,EAAG,eAAe,CAAC,MAAxC;WAAX,EAA4D,uBAA5D,EADD;;eAEA,QAAA,CAAS,IAAT,EAAe,eAAf;MAN4B,CAA7B;IADoB,CAvarB;IAkbA,yBAAA,EAA2B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAC1B,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,iCAA1C;aACA,WAAW,CAAC,gBAAZ,CAA6B,MAA7B,EAAqC,OAArC,EAA8C,SAAC,GAAD,EAAM,MAAN;QAC7C,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAA4D,cAA5D;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,yBAAV,CAAT,EAAP;;QACA,IAAG,MAAM,CAAC,IAAV;AACC,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,6BAAV,CAAT,EADR;SAAA,MAEK,IAAG,mBAAH;AACJ,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,oCAAV,CAAT,EADH;SAAA,MAAA;AAGJ,iBAAO,QAAA,CAAA,EAHH;;MALwC,CAA9C;IAF0B,CAlb3B;IA8bA,2BAAA,EAA6B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAC5B,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,4CAAjC;aACA,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;UAAmC,KAAA,EAAO;YAAC,UAAA,EAAY;cAAC,KAAA,EAAO,OAAR;cAAiB,IAAA,EAAM;gBAAC,OAAA,EAAQ,KAAT;eAAvB;aAAb;WAA1C;SADyB;QAEhC,MAAA,EAAS;UAAE,SAAA,EAAW,CAAb;SAFuB;QAGhC,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,cAAA,EAAe,KAAhB;WAAP;SAHwB;OAAjC,EAIG,SAAC,GAAD,EAAM,MAAN;QACF,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAmE,cAAnE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,gCAAV,CAAT,EAAP;;QACA,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,+BAA1C;eACA,QAAA,CAAA;MAJE,CAJH;IAF4B,CA9b7B;IA0cA,4BAAA,EAA8B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MAC7B,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,iCAA1C;aACA,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;UAAkC,OAAA,EAAU;YAAC,UAAA,EAAY;cAAC,KAAA,EAAO,OAAR;cAAiB,IAAA,EAAM,KAAvB;aAAb;WAA5C;SADyB;QAEhC,MAAA,EAAS;UAAE,SAAA,EAAW,CAAb;SAFuB;QAGhC,MAAA,EAAQ;UAAC,MAAA,EAAQ;YAAC,cAAA,EAAe,IAAhB;WAAT;SAHwB;OAAjC,EAIG,QAJH;IAF6B,CA1c9B;IAkdA,kBAAA,EAAoB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;MACnB,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;QAAqB,SAAA,OAArB;OAAX,EAA0C,0BAA1C;aACA,EAAE,CAAC,eAAe,CAAC,aAAnB,CAAiC;QAChC,KAAA,EAAO;UAAC,GAAA,EAAI,QAAA,CAAS,MAAM,CAAC,QAAP,CAAA,CAAT,CAAL;UAAkC,OAAA,EAAU;YAAC,UAAA,EAAY;cAAC,KAAA,EAAO,OAAR;cAAiB,IAAA,EAAM,KAAvB;aAAb;WAA5C;SADyB;QAEhC,MAAA,EAAQ;UAAE,SAAA,EAAW,CAAb;SAFwB;QAGhC,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,cAAA,EAAe,IAAhB;WAAP;SAHwB;OAAjC,EAIG,SAAC,GAAD,EAAM,MAAN;QACF,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;QACA,IAAsE,cAAtE;AAAA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,mCAAV,CAAT,EAAP;;QACA,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,oBAA1C;eACA,QAAA,CAAA;MAJE,CAJH;IAFmB,CAldpB;IA8dA,oBAAA,EAAsB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,QAA9B;aACrB,EAAE,CAAC,UAAU,CAAC,aAAd,CAA4B;QAC3B,KAAA,EAAO;UAAC,GAAA,EAAK,OAAN;SADoB;QAE3B,MAAA,EAAQ;UAAC,IAAA,EAAM;YAAC,SAAA,EAAW,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAE,IAAxB,CAAZ;WAAP;SAFmB;OAA5B,EAGG,SAAC,GAAD;QACF,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;UAAqB,SAAA,OAArB;SAAX,EAA0C,oBAA1C;eACA,QAAA,CAAA;MAFE,CAHH;IADqB,CA9dtB;;AAjDD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/PackWorker.js.map b/services/track-changes/app/js/PackWorker.js.map deleted file mode 100644 index ff2a9b07af..0000000000 --- a/services/track-changes/app/js/PackWorker.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "PackWorker.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/PackWorker.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,MAAuB,OAAA,CAAQ,WAAR,CAAvB,EAAC,WAAD,EAAK,uBAAL,EAAe;;EACf,EAAA,GAAK,OAAA,CAAQ,IAAR;;EACL,OAAA,GAAU,OAAA,CAAQ,oBAAR;;EACV,OAAO,CAAC,UAAR,CAAmB,eAAnB;;EACA,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,MAAM,CAAC,UAAP,CAAkB,0BAAlB;;EACA,IAAG,8DAAH;IACC,MAAM,CAAC,wBAAP,CAAgC,QAAQ,CAAC,MAAM,CAAC,GAAhD,EADD;;;EAGA,IAAA,GAAO,EAAA,GAAK,IAAL,GAAY;;EAEnB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,WAAA,GAAc,OAAA,CAAQ,eAAR;;EAKd,MAAA,GAAS,OAAO,CAAC,IAAK,CAAA,CAAA;;EACtB,mBAAA,GAAsB,MAAA,CAAO,OAAO,CAAC,IAAK,CAAA,CAAA,CAApB,CAAA,IAA2B;;EACjD,OAAA,GAAU,MAAA,CAAO,OAAO,CAAC,IAAK,CAAA,CAAA,CAApB,CAAA,IAA2B,EAAA,GAAG,EAAH,GAAM;;EAC3C,KAAA,GAAQ;;EACR,KAAA,GAAQ;;EAER,IAAG,CAAC,MAAM,CAAC,KAAP,CAAa,UAAb,CAAJ;IACC,IAAA,GAAO,EAAE,CAAC,YAAH,CAAgB,MAAhB;IACP,MAAA;;AAAS;AAAA;WAAA,sCAAA;;QACR,OAAuB,IAAI,CAAC,KAAL,CAAW,GAAX,CAAvB,EAAC,oBAAD,EAAa;sBACb;UAAC,QAAA,MAAD;UAAS,YAAA,UAAT;;AAFQ;;;IAGT,OAAA,GAAU,CAAC,CAAC,MAAF,CAAS,MAAT,EAAiB,SAAC,GAAD;AAAS,UAAA;6DAAW,CAAE,KAAb,CAAmB,gBAAnB;IAAT,CAAjB,EALX;GAAA,MAAA;IAOC,KAAA,GAAQ,MAAA,CAAO,OAAO,CAAC,IAAK,CAAA,CAAA,CAApB,CAAA,IAA2B,KAPpC;;;EASA,iBAAA,GAAoB;;EACpB,aAAA,GAAgB,UAAA,CAAW,SAAA;AAC1B,QAAA;IAAA,MAAM,CAAC,GAAP,CAAW,qCAAX;IAEA,iBAAA,GAAoB;IAEpB,WAAA,GAAc,UAAA,CAAW,SAAA;MACxB,MAAM,CAAC,KAAP,CAAa,qCAAb;aACA,OAAO,CAAC,IAAR,CAAA;IAFwB,CAAX,EAGZ,CAAA,GAAE,EAAF,GAAK,IAHO;WAId,WAAW,CAAC,KAAZ,CAAA;EAT0B,CAAX,EAUd,OAVc;;EAYhB,MAAM,CAAC,GAAP,CAAW,8BAAA,GAA+B,KAA/B,GAAqC,UAArC,GAA+C,mBAA/C,GAAmE,YAAnE,GAA+E,OAA1F;;EAGA,EAAE,CAAC,KAAH,GAAY,SAAC,QAAD;WACX,IAAI,CAAC,UAAL,CAAgB,SAAC,GAAD,EAAM,MAAN;MACf,IAAwB,WAAxB;AAAA,eAAO,QAAA,CAAS,GAAT,EAAP;;MACA,MAAA,GAAY,sBAAH,GAAwB,MAAxB,GAAoC,MAAM,CAAC;MACpD,MAAM,CAAC,OAAP,CAAe,IAAf,EAAqB,IAArB;aACA,QAAA,CAAA;IAJe,CAAhB;EADW;;EAOZ,MAAA,GAAS,SAAA;IACR,IAAG,qBAAH;MACC,MAAM,CAAC,GAAP,CAAW,oBAAX;MACA,YAAA,CAAa,aAAb,EAFD;;IAGA,MAAM,CAAC,GAAP,CAAW,YAAX;WACA,EAAE,CAAC,KAAH,CAAS,SAAA;MACR,MAAM,CAAC,GAAP,CAAW,sCAAX;aACA,WAAW,CAAC,KAAZ,CAAkB,SAAA;AACjB,YAAA;QAAA,MAAM,CAAC,GAAP,CAAW;UAAC,cAAA,EAAgB,KAAjB;UAAwB,QAAA,EAAU,KAAlC;SAAX,EAAqD,wCAArD;QACA,WAAA,GAAc,UAAA,CAAW,SAAA;UACxB,MAAM,CAAC,KAAP,CAAa,oCAAb;iBACA,OAAO,CAAC,IAAR,CAAa,CAAb;QAFwB,CAAX,EAGZ,CAAA,GAAE,IAHU;eAId,WAAW,CAAC,KAAZ,CAAA;MANiB,CAAlB;IAFQ,CAAT;EALQ;;EAeT,OAAO,CAAC,EAAR,CAAW,MAAX,EAAmB,SAAC,IAAD;WAClB,MAAM,CAAC,GAAP,CAAW;MAAC,MAAA,IAAD;KAAX,EAAmB,4BAAnB;EADkB,CAAnB;;EAGA,cAAA,GAAiB,SAAC,OAAD;WAChB,KAAK,CAAC,UAAN,CAAiB,OAAjB,EAA0B,SAAC,MAAD,EAAS,QAAT;AACzB,UAAA;MAAC,gBAAD,EAAM,8BAAN,EAAkB;MAClB,KAAA;MACA,MAAM,CAAC,GAAP,CAAW;QAAC,YAAA,UAAD;QAAa,QAAA,MAAb;OAAX,EAAiC,aAAA,GAAc,KAAd,GAAoB,GAApB,GAAuB,KAAxD;MACA,IAAO,oBAAJ,IAAuB,gBAA1B;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,YAAA,UAAD;UAAa,QAAA,MAAb;SAAX,EAAiC,uCAAjC;AACA,eAAO,QAAA,CAAA,EAFR;;MAGA,OAAA,GAAU,SAAC,GAAD,EAAM,MAAN;QACT,IAAG,aAAA,IAAS,GAAG,CAAC,IAAJ,KAAY,eAArB,IAAyC,GAAG,CAAC,SAAhD;UACC,MAAM,CAAC,IAAP,CAAY;YAAC,KAAA,GAAD;YAAM,QAAA,MAAN;WAAZ,EAA2B,0CAA3B;UAEA,GAAA,GAAM,KAHP;;QAIA,IAAG,WAAH;UACC,MAAM,CAAC,KAAP,CAAa;YAAC,KAAA,GAAD;YAAM,QAAA,MAAN;WAAb,EAA4B,8BAA5B;AACA,iBAAO,QAAA,CAAS,GAAT,EAFR;;QAGA,IAAG,iBAAH;UACC,MAAM,CAAC,IAAP,CAAY,mCAAZ;AACA,iBAAO,QAAA,CAAS,IAAI,KAAJ,CAAU,UAAV,CAAT,EAFR;;eAGA,UAAA,CAAW,SAAA;iBACV,QAAA,CAAS,GAAT,EAAc,MAAd;QADU,CAAX,EAEE,mBAFF;MAXS;MAcV,IAAO,WAAP;eACC,WAAW,CAAC,YAAZ,CAAyB,UAAzB,EAAqC,MAArC,EAA6C,OAA7C,EADD;OAAA,MAAA;eAGC,WAAW,CAAC,cAAZ,CAA2B,UAA3B,EAAuC,MAAvC,EAA+C,GAA/C,EAAoD,OAApD,EAHD;;IArByB,CAA1B,EAyBE,SAAC,GAAD,EAAM,OAAN;MACD,IAAG,aAAA,IAAS,GAAG,CAAC,OAAJ,KAAe,UAA3B;QACC,MAAM,CAAC,KAAP,CAAa;UAAC,KAAA,GAAD;SAAb,EAAoB,6CAApB,EADD;;aAEA,MAAA,CAAA;IAHC,CAzBF;EADgB;;EAiCjB,gBAAA,GAAoB,SAAC,IAAD;AACnB,QAAA;IAAA,EAAA,GAAK,IAAI,CAAC,KAAL,CAAW,IAAI,CAAC,OAAL,CAAA,CAAA,GAAiB,IAA5B,CAAiC,CAAC,QAAlC,CAA2C,EAA3C,CAAA,GAAiD;AACtD,WAAO,QAAA,CAAS,EAAT;EAFY;;EAQpB,IAAG,eAAH;IACC,MAAM,CAAC,GAAP,CAAW,MAAA,GAAO,OAAO,CAAC,MAAf,GAAsB,gBAAtB,GAAsC,MAAjD;IACA,cAAA,CAAe,OAAf,EAFD;GAAA,MAAA;IAIC,UAAA,GAAa,IAAI,IAAJ,CAAS,IAAI,CAAC,GAAL,CAAA,CAAA,GAAa,CAAA,GAAI,IAA1B;IACb,EAAE,CAAC,UAAU,CAAC,IAAd,CAAmB;MAClB,SAAA,EAAW;QAAC,OAAA,EAAS,KAAV;OADO;MAElB,UAAA,EAAY;QAAC,OAAA,EAAS,IAAV;OAFM;MAGlB,KAAA,EAAO;QAAC,OAAA,EAAS,IAAV;OAHW;MAIlB,GAAA,EAAK;QAAC,GAAA,EAAK,gBAAA,CAAiB,UAAjB,CAAN;OAJa;MAKlB,YAAA,EAAc;QAAC,GAAA,EAAK,UAAN;OALI;KAAnB,EAMG;MAAC,GAAA,EAAI,CAAL;MAAQ,MAAA,EAAO,CAAf;MAAkB,UAAA,EAAW,CAA7B;KANH,CAMmC,CAAC,IANpC,CAMyC;MACxC,YAAA,EAAa,CAD2B;KANzC,CAQE,CAAC,KARH,CAQS,KART,EAQgB,SAAC,GAAD,EAAM,OAAN;MACf,IAAG,WAAH;QACC,MAAM,CAAC,GAAP,CAAW;UAAC,KAAA,GAAD;SAAX,EAAkB,4BAAlB;QACA,MAAA,CAAA;AACA,eAHD;;MAIA,OAAA,GAAU,CAAC,CAAC,IAAF,CAAO,OAAP,EAAgB,KAAhB,EAAuB,SAAC,MAAD;eAAY,MAAM,CAAC,MAAM,CAAC,QAAd,CAAA;MAAZ,CAAvB;MACV,KAAA,GAAQ,OAAO,CAAC;MAChB,MAAM,CAAC,GAAP,CAAW,QAAA,GAAS,KAAT,GAAe,uBAA1B;aACA,cAAA,CAAe,OAAf;IARe,CARhB,EALD;;AArHA" -} \ No newline at end of file diff --git a/services/track-changes/app/js/ProjectIterator.js.map b/services/track-changes/app/js/ProjectIterator.js.map deleted file mode 100644 index f6aa31a7d1..0000000000 --- a/services/track-changes/app/js/ProjectIterator.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "ProjectIterator.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/ProjectIterator.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,IAAA,GAAO,OAAA,CAAQ,MAAR;;EAEP,MAAM,CAAC,OAAP,GAAiB,eAAA,GAEV;IACQ,yBAAC,KAAD,EAAQ,OAAR,EAAiB,aAAjB;AACZ,UAAA;MADoB,IAAC,CAAA,SAAD;MAAS,IAAC,CAAA,gBAAD;MAC7B,OAAA,GAAU,SAAC,CAAD,EAAG,CAAH;eAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAP,GAAgB,CAAC,CAAC,IAAI,CAAC,MAAxB,CAAA,IAAmC,CAAC,CAAC,CAAC,SAAF,GAAc,CAAC,CAAC,SAAjB;MAA5C;MACV,IAAC,CAAA,KAAD,GAAS,KAAK,CAAC,KAAN,CAAA,CAAa,CAAC,IAAd,CAAmB,OAAnB;MACT,IAAC,CAAA,KAAD,GAAS,IAAI,IAAJ,CAAS,OAAT;IAHG;;8BAKb,IAAA,GAAM,SAAC,QAAD;AAGL,UAAA;MAAA,QAAA,GAAW;MACX,MAAA,GAAS,IAAC,CAAA;MACV,KAAA,GAAQ,QAAQ,CAAC;MACjB,WAAA,GAAc;MACd,QAAA,GAAW,QAAQ,CAAC,KAAM,CAAA,CAAA;MAC1B,YAAA,uBAAe,QAAQ,CAAE,IAAI,CAAC,gBAAf,IAAyB;MACxC,QAAA,GAAW,KAAK,CAAC,IAAN,CAAA;AAOX,aAAM,gBAAA,wBAAY,QAAQ,CAAE,IAAI,CAAC,kBAAf,GAA0B,MAA5C;QAEC,QAAQ,CAAC,KAAK,CAAC,KAAf,CAAA;QACA,QAAA,GAAW,QAAQ,CAAC,KAAM,CAAA,CAAA;QAC1B,YAAA,uBAAe,QAAQ,CAAE,IAAI,CAAC,gBAAf,IAAyB;MAJzC;MAMA,IAAG,CAAC,KAAK,CAAC,KAAN,CAAA,CAAA,wBAAiB,QAAQ,CAAE,IAAI,CAAC,gBAAf,IAAyB,YAA3C,CAAA,IAA6D,kBAAhE;AAEC,eAAO,IAAC,CAAA,aAAD,CAAe,QAAQ,CAAC,UAAxB,EAAoC,QAAQ,CAAC,MAA7C,EAAqD,QAAQ,CAAC,GAA9D,EAAmE,SAAC,GAAD,EAAM,IAAN;AACzE,cAAA;UAAA,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;UACA,QAAQ,CAAC,KAAK,CAAC,KAAf,CAAA;AAEA;AAAA,eAAA,qCAAA;;kBAA8B,gBAAJ,IAAe,EAAE,CAAC,IAAI,CAAC,MAAR,GAAiB;;;YAEzD,EAAE,CAAC,MAAH,GAAY,QAAQ,CAAC;YACrB,EAAE,CAAC,UAAH,GAAgB,QAAQ,CAAC;YACzB,KAAK,CAAC,IAAN,CAAW,EAAX;AAJD;AAMA,iBAAO,QAAQ,CAAC,IAAT,CAAc,QAAd;QAVkE,CAAnE,EAFR;;AAeA,aAAM,kBAAA,IAAc,qBAAC,QAAQ,CAAE,IAAI,CAAC,gBAAf,GAAwB,YAAzB,CAApB;QACC,WAAW,CAAC,IAAZ,CAAiB,QAAjB;QACA,KAAK,CAAC,GAAN,CAAA;QACA,QAAA,GAAW,KAAK,CAAC,IAAN,CAAA;MAHZ;MAQA,IAAG,KAAK,CAAC,KAAN,CAAA,CAAA,IAAsB,kBAAzB;QACC,QAAQ,CAAC,KAAT,GAAiB,KADlB;;aAGA,QAAA,CAAS,IAAT,EAAe,WAAf;IAhDK;;8BAkDN,IAAA,GAAM,SAAA;AACL,aAAO,IAAC,CAAA;IADH;;;;;AA5DR" -} \ No newline at end of file diff --git a/services/track-changes/app/js/RedisManager.js.map b/services/track-changes/app/js/RedisManager.js.map deleted file mode 100644 index a0e01b35f6..0000000000 --- a/services/track-changes/app/js/RedisManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "RedisManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/RedisManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,KAAA,GAAQ,OAAA,CAAQ,kBAAR;;EACR,OAAA,GAAU,KAAK,CAAC,YAAN,CAAmB,QAAQ,CAAC,KAAK,CAAC,OAAlC;;EACV,IAAA,GAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;;EAC9B,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EAER,MAAM,CAAC,OAAP,GAAiB,YAAA,GAEhB;IAAA,mBAAA,EAAqB,SAAC,MAAD,EAAS,SAAT,EAAoB,QAApB;AACpB,UAAA;;QADwC,WAAW,SAAC,KAAD,EAAQ,WAAR,GAAA;;MACnD,GAAA,GAAM,IAAI,CAAC,sBAAL,CAA4B;QAAC,QAAA,MAAD;OAA5B;aACN,OAAO,CAAC,MAAR,CAAe,GAAf,EAAoB,CAApB,EAAuB,SAAA,GAAY,CAAnC,EAAsC,QAAtC;IAFoB,CAArB;IAIA,gBAAA,EAAkB,SAAC,WAAD,EAAc,QAAd;AACjB,UAAA;;QAD+B,WAAW,SAAC,KAAD,EAAQ,UAAR,GAAA;;AAC1C;QACC,UAAA;;AAAe;AAAA;eAAA,qCAAA;;0BAAA,IAAI,CAAC,KAAL,CAAW,MAAX;AAAA;;aADhB;OAAA,cAAA;QAEM;AACL,eAAO,QAAA,CAAS,CAAT,EAHR;;aAIA,QAAA,CAAS,IAAT,EAAe,UAAf;IALiB,CAJlB;IAWA,uBAAA,EAAyB,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,QAAjC;AACxB,UAAA;;QADyD,WAAW,SAAC,KAAD,GAAA;;MACpE,KAAA,GAAQ,OAAO,CAAC,KAAR,CAAA;AAER;AAAA,WAAA,qCAAA;;QACC,KAAK,CAAC,IAAN,CAAW,IAAI,CAAC,sBAAL,CAA4B;UAAC,QAAA,MAAD;SAA5B,CAAX,EAAkD,CAAlD,EAAqD,MAArD;AADD;aAEA,KAAK,CAAC,IAAN,CAAW,SAAC,KAAD,EAAQ,OAAR;QACV,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eAGA,OAAO,CAAC,IAAR,CAAa,IAAI,CAAC,kBAAL,CAAwB;UAAC,YAAA,UAAD;SAAxB,CAAb,EAAoD,MAApD,EAA4D,SAAC,KAAD;UAC3D,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT;QAF2D,CAA5D;MAJU,CAAX;IALwB,CAXzB;IAwBA,uBAAA,EAAyB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAChD,OAAO,CAAC,QAAR,CAAiB,IAAI,CAAC,kBAAL,CAAwB;QAAC,YAAA,UAAD;OAAxB,CAAjB,EAAwD,QAAxD;IADwB,CAxBzB;IA6BA,QAAA,EAAU,SAAC,OAAD,EAAU,QAAV;AACT,UAAA;MAAA,KAAA,0CAAQ,OAAO,CAAC,MAAO,mBAAf,IAA4B,CAAE,OAAF;MACpC,kBAAA,GAAqB,SAAC,IAAD,EAAO,EAAP;eACpB,YAAY,CAAC,gBAAb,CAA8B,IAA9B,EAAoC,OAApC,EAA6C,EAA7C;MADoB;aAErB,KAAK,CAAC,YAAN,CAAmB,KAAnB,EAA0B,kBAA1B,EAA8C,QAA9C;IAJS,CA7BV;IAmCA,gBAAA,EAAkB,SAAC,IAAD,EAAO,OAAP,EAAgB,QAAhB;AACjB,UAAA;MAAA,MAAA,GAAS;MACT,MAAA,GAAS;MAET,WAAA,GAAc,SAAC,EAAD;eACb,IAAI,CAAC,IAAL,CAAU,MAAV,EAAkB,OAAlB,EAA2B,OAA3B,EAAoC,OAApC,EAA6C,IAA7C,EAAmD,SAAC,KAAD,EAAQ,KAAR;AAClD,cAAA;UAAA,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACC,iBAAD,EAAS;AACT,eAAA,sCAAA;;YACC,MAAO,CAAA,GAAA,CAAP,GAAc;AADf;UAEA,IAAG,MAAA,KAAU,GAAb;AACC,mBAAO,QAAA,CAAS,IAAT,EAAe,MAAM,CAAC,IAAP,CAAY,MAAZ,CAAf,EADR;WAAA,MAAA;mBAGC,WAAA,CAAA,EAHD;;QALkD,CAAnD;MADa;aAUd,WAAA,CAAA;IAdiB,CAnClB;IAqDA,WAAA,EAAa,SAAC,OAAD;AACZ,UAAA;MAAA,GAAA;;AAAM;aAAA,yCAAA;;UACL,CAAA,GAAI,GAAG,CAAC,KAAJ,CAAU,uBAAV;wBACJ,CAAE,CAAA,CAAA;AAFG;;;AAGN,aAAO;IAJK,CArDb;IA2DA,2BAAA,EAA6B,SAAC,QAAD;;QAAC,WAAW,SAAC,KAAD,EAAQ,WAAR,GAAA;;aACxC,YAAY,CAAC,QAAb,CAAsB,IAAI,CAAC,kBAAL,CAAwB;QAAC,UAAA,EAAW,GAAZ;OAAxB,CAAtB,EAAiE,SAAC,KAAD,EAAQ,YAAR;AAChE,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,WAAA,GAAc,YAAY,CAAC,WAAb,CAAyB,YAAzB;eACd,QAAA,CAAS,KAAT,EAAgB,WAAhB;MAHgE,CAAjE;IAD4B,CA3D7B;IAiEA,0BAAA,EAA4B,SAAC,QAAD;;QAAC,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAGvC,YAAY,CAAC,QAAb,CAAsB,IAAI,CAAC,sBAAL,CAA4B;QAAC,MAAA,EAAO,GAAR;OAA5B,CAAtB,EAAiE,SAAC,KAAD,EAAQ,QAAR;AAChE,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,OAAA,GAAU,YAAY,CAAC,WAAb,CAAyB,QAAzB;eACV,QAAA,CAAS,KAAT,EAAgB,OAAhB;MAHgE,CAAjE;IAH2B,CAjE5B;;AARD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/RestoreManager.js.map b/services/track-changes/app/js/RestoreManager.js.map deleted file mode 100644 index b9ab8c044c..0000000000 --- a/services/track-changes/app/js/RestoreManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "RestoreManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/RestoreManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,sBAAA,GAAyB,OAAA,CAAQ,0BAAR;;EACzB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,cAAA,GAChB;IAAA,sBAAA,EAAwB,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAA8B,OAA9B,EAAuC,QAAvC;;QAAuC,WAAW,SAAC,KAAD,GAAA;;MACzE,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;QAAwB,MAAA,EAAQ,MAAhC;QAAwC,OAAA,EAAS,OAAjD;QAA0D,OAAA,EAAS,OAAnE;OAAX,EAAuF,oBAAvF;aACA,WAAW,CAAC,wBAAZ,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,OAAzD,EAAkE,SAAC,KAAD,EAAQ,OAAR;QACjE,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,sBAAsB,CAAC,WAAvB,CAAmC,UAAnC,EAA+C,MAA/C,EAAuD,OAAvD,EAAgE,OAAhE,EAAyE,SAAC,KAAD;UACxE,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAA;QAFwE,CAAzE;MAFiE,CAAlE;IAFuB,CAAxB;;AALD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/UpdateCompressor.js.map b/services/track-changes/app/js/UpdateCompressor.js.map deleted file mode 100644 index 6ff4402373..0000000000 --- a/services/track-changes/app/js/UpdateCompressor.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "UpdateCompressor.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/UpdateCompressor.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,SAAA,GAAY,SAAC,EAAD,EAAK,GAAL,EAAU,EAAV;WAAiB,EAAG,cAAH,GAAa,EAAb,GAAkB,EAAG;EAAtC;;EACZ,SAAA,GAAY,SAAC,EAAD,EAAK,GAAL,EAAU,MAAV;WAAqB,EAAG,cAAH,GAAa,EAAG;EAArC;;EAEZ,gBAAA,GAAmB,OAAA,CAAQ,yBAAR,CAAkC,CAAC;;EACtD,GAAA,GAAM,IAAI,gBAAJ,CAAA;;EAEN,MAAM,CAAC,OAAP,GAAiB,gBAAA,GAChB;IAAA,IAAA,EAAM,MAAN;IAgBA,wBAAA,EAA0B,SAAC,OAAD;AACzB,UAAA;MAAA,YAAA,GAAe;AACf,WAAA,yCAAA;;QAEC,GAAA,GAAM,MAAM,CAAC,EAAE,CAAC,MAAV,CAAiB,SAAC,CAAD;iBAAO,aAAA,IAAQ;QAAf,CAAjB;QACN,IAAG,GAAG,CAAC,MAAJ,KAAc,CAAjB;UACC,YAAY,CAAC,IAAb,CACC;YAAA,EAAA,EAAI,gBAAgB,CAAC,IAArB;YACA,IAAA,EACC;cAAA,QAAA,EAAU,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAA9C;cACA,MAAA,EAAU,MAAM,CAAC,IAAI,CAAC,MAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAD9C;cAEA,OAAA,EAAU,MAAM,CAAC,IAAI,CAAC,OAFtB;aAFD;YAKA,CAAA,EAAG,MAAM,CAAC,CALV;WADD,EADD;SAAA,MAAA;AASC,eAAA,uCAAA;;YACC,YAAY,CAAC,IAAb,CACC;cAAA,EAAA,EAAI,EAAJ;cACA,IAAA,EACC;gBAAA,QAAA,EAAU,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAA9C;gBACA,MAAA,EAAU,MAAM,CAAC,IAAI,CAAC,MAAZ,IAAwB,MAAM,CAAC,IAAI,CAAC,EAD9C;gBAEA,OAAA,EAAU,MAAM,CAAC,IAAI,CAAC,OAFtB;eAFD;cAKA,CAAA,EAAG,MAAM,CAAC,CALV;aADD;AADD,WATD;;AAHD;AAoBA,aAAO;IAtBkB,CAhB1B;IAwCA,4BAAA,EAA8B,SAAC,OAAD;AAC7B,UAAA;MAAA,gBAAA,GAAmB;AACnB,WAAA,yCAAA;;QACC,UAAA,GAAa,gBAAiB,CAAA,gBAAgB,CAAC,MAAjB,GAA0B,CAA1B;QAC9B,IAAG,oBAAA,IAAgB,UAAU,CAAC,CAAX,KAAgB,MAAM,CAAC,CAA1C;UACC,IAAoC,MAAM,CAAC,EAAP,KAAa,gBAAgB,CAAC,IAAlE;YAAA,UAAU,CAAC,EAAE,CAAC,IAAd,CAAmB,MAAM,CAAC,EAA1B,EAAA;WADD;SAAA,MAAA;UAGC,UAAA,GACC;YAAA,EAAA,EAAM,EAAN;YACA,IAAA,EAAM,MAAM,CAAC,IADb;YAEA,CAAA,EAAM,MAAM,CAAC,CAFb;;UAGD,IAAoC,MAAM,CAAC,EAAP,KAAa,gBAAgB,CAAC,IAAlE;YAAA,UAAU,CAAC,EAAE,CAAC,IAAd,CAAmB,MAAM,CAAC,EAA1B,EAAA;;UACA,gBAAgB,CAAC,IAAjB,CAAsB,UAAtB,EARD;;AAFD;AAWA,aAAO;IAbsB,CAxC9B;IAuDA,kBAAA,EAAoB,SAAC,kBAAD,EAAqB,UAArB;AACnB,UAAA;MAAA,6EAAyB,CAAE,yBAAxB,GAAiC,CAApC;AAGC,eAAO,CAAC,kBAAD,CAAoB,CAAC,MAArB,CAA4B,gBAAgB,CAAC,kBAAjB,CAAoC,IAApC,EAAyC,UAAzC,CAA5B,EAHR;;MAIA,IAAG,0BAAH;QACC,UAAA,GAAa,CAAC,kBAAD,CAAoB,CAAC,MAArB,CAA4B,UAA5B,EADd;;MAEA,OAAA,GAAU,gBAAgB,CAAC,wBAAjB,CAA0C,UAA1C;MACV,OAAA,GAAU,gBAAgB,CAAC,eAAjB,CAAiC,OAAjC;AACV,aAAO,gBAAgB,CAAC,4BAAjB,CAA8C,OAA9C;IATY,CAvDpB;IAkEA,eAAA,EAAiB,SAAC,OAAD;AAChB,UAAA;MAAA,IAAa,OAAO,CAAC,MAAR,KAAkB,CAA/B;AAAA,eAAO,GAAP;;MAEA,iBAAA,GAAoB,CAAC,OAAO,CAAC,KAAR,CAAA,CAAD;AACpB,WAAA,yCAAA;;QACC,oBAAA,GAAuB,iBAAiB,CAAC,GAAlB,CAAA;QACvB,IAAG,4BAAH;UACC,iBAAA,GAAoB,iBAAiB,CAAC,MAAlB,CAAyB,gBAAgB,CAAC,iBAAjB,CAAmC,oBAAnC,EAAyD,MAAzD,CAAzB,EADrB;SAAA,MAAA;UAGC,iBAAiB,CAAC,IAAlB,CAAuB,MAAvB,EAHD;;AAFD;AAOA,aAAO;IAXS,CAlEjB;IA+EA,wBAAA,EAA0B,SAAA,GAAY,EAAA,GAAK,IA/E3C;IAgFA,eAAA,EAAiB,YAAA,GAAe,CAAA,GAAG,IAAH,GAAU,IAhF1C;IAkFA,iBAAA,EAAmB,SAAC,WAAD,EAAc,YAAd;AAClB,UAAA;MAAA,WAAA,GACC;QAAA,EAAA,EAAI,WAAW,CAAC,EAAhB;QACA,IAAA,EACC;UAAA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAAjB,IAA4B,IAAtC;UACA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAAjB,IAA6B,WAAW,CAAC,IAAI,CAAC,EADxD;UAEA,MAAA,EAAU,WAAW,CAAC,IAAI,CAAC,MAAjB,IAA6B,WAAW,CAAC,IAAI,CAAC,EAFxD;SAFD;QAKA,CAAA,EAAG,WAAW,CAAC,CALf;;MAMD,YAAA,GACC;QAAA,EAAA,EAAI,YAAY,CAAC,EAAjB;QACA,IAAA,EACC;UAAA,OAAA,EAAU,YAAY,CAAC,IAAI,CAAC,OAAlB,IAA6B,IAAvC;UACA,QAAA,EAAU,YAAY,CAAC,IAAI,CAAC,QAAlB,IAA8B,YAAY,CAAC,IAAI,CAAC,EAD1D;UAEA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAAlB,IAA8B,YAAY,CAAC,IAAI,CAAC,EAF1D;SAFD;QAKA,CAAA,EAAG,YAAY,CAAC,CALhB;;MAOD,IAAG,WAAW,CAAC,IAAI,CAAC,OAAjB,KAA4B,YAAY,CAAC,IAAI,CAAC,OAAjD;AACC,eAAO,CAAC,WAAD,EAAc,YAAd,EADR;;MAGA,IAAG,YAAY,CAAC,IAAI,CAAC,QAAlB,GAA6B,WAAW,CAAC,IAAI,CAAC,MAA9C,GAAuD,gBAAgB,CAAC,wBAA3E;AACC,eAAO,CAAC,WAAD,EAAc,YAAd,EADR;;MAGA,OAAA,GAAU,WAAW,CAAC;MACtB,QAAA,GAAW,YAAY,CAAC;MAExB,SAAA,mCAAqB,CAAE,gBAAX,sCAA8B,CAAE;MAC5C,UAAA,sCAAuB,CAAE,gBAAZ,uCAAgC,CAAE;MAG/C,IAAG,mBAAA,IAAe,oBAAf,IAA+B,CAAA,OAAO,CAAC,CAAR,YAAa,QAAQ,CAAC,EAAtB,QAAA,IAA2B,CAAC,OAAO,CAAC,CAAR,GAAY,OAAO,CAAC,CAAC,CAAC,MAAvB,CAA3B,CAA/B,IAA6F,SAAA,GAAY,UAAZ,GAAyB,gBAAgB,CAAC,eAA1I;AACC,eAAO;UACN;YAAA,IAAA,EACC;cAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;cACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;cAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;aADD;YAIA,EAAA,EACC;cAAA,CAAA,EAAG,OAAO,CAAC,CAAX;cACA,CAAA,EAAG,SAAA,CAAU,OAAO,CAAC,CAAlB,EAAqB,QAAQ,CAAC,CAAT,GAAa,OAAO,CAAC,CAA1C,EAA6C,QAAQ,CAAC,CAAtD,CADH;aALD;YAOA,CAAA,EAAG,YAAY,CAAC,CAPhB;WADM;UADR;OAAA,MAYK,IAAG,mBAAA,IAAe,oBAAf,IAA+B,CAAA,QAAQ,CAAC,CAAT,YAAc,OAAO,CAAC,EAAtB,QAAA,IAA2B,CAAC,QAAQ,CAAC,CAAT,GAAa,QAAQ,CAAC,CAAC,CAAC,MAAzB,CAA3B,CAA/B,IAA+F,SAAA,GAAY,UAAZ,GAAyB,gBAAgB,CAAC,eAA5I;AACJ,eAAO;UACN;YAAA,IAAA,EACC;cAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;cACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;cAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;aADD;YAIA,EAAA,EACC;cAAA,CAAA,EAAG,QAAQ,CAAC,CAAZ;cACA,CAAA,EAAG,SAAA,CAAU,QAAQ,CAAC,CAAnB,EAAsB,OAAO,CAAC,CAAR,GAAY,QAAQ,CAAC,CAA3C,EAA8C,OAAO,CAAC,CAAtD,CADH;aALD;YAOA,CAAA,EAAG,YAAY,CAAC,CAPhB;WADM;UADH;OAAA,MAYA,IAAG,mBAAA,IAAe,oBAAf,IAA+B,CAAA,OAAO,CAAC,CAAR,YAAa,QAAQ,CAAC,EAAtB,QAAA,IAA2B,CAAC,OAAO,CAAC,CAAR,GAAY,OAAO,CAAC,CAAC,CAAC,MAAvB,CAA3B,CAAlC;QACJ,MAAA,GAAS,QAAQ,CAAC,CAAT,GAAa,OAAO,CAAC;QAC9B,YAAA,GAAe,OAAO,CAAC,CAAC,CAAC,KAAV,CAAgB,MAAhB,EAAwB,MAAA,GAAS,QAAQ,CAAC,CAAC,CAAC,MAA5C;QAEf,IAAG,YAAA,KAAgB,QAAQ,CAAC,CAA5B;UACC,MAAA,GAAS,SAAA,CAAU,OAAO,CAAC,CAAlB,EAAqB,MAArB,EAA6B,QAAQ,CAAC,CAAC,CAAC,MAAxC;AACT,iBAAO;YACN;cAAA,IAAA,EACC;gBAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;gBACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;gBAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;eADD;cAIA,EAAA,EACC;gBAAA,CAAA,EAAG,OAAO,CAAC,CAAX;gBACA,CAAA,EAAG,MADH;eALD;cAOA,CAAA,EAAG,YAAY,CAAC,CAPhB;aADM;YAFR;SAAA,MAAA;AAcC,iBAAO,CAAC,WAAD,EAAc,YAAd,EAdR;SAJI;OAAA,MAqBA,IAAG,mBAAA,IAAe,oBAAf,IAA+B,OAAO,CAAC,CAAR,KAAa,QAAQ,CAAC,CAAxD;QACJ,MAAA,GAAS,OAAO,CAAC;QACjB,QAAA,GAAW,IAAC,CAAA,gBAAD,CAAkB,OAAO,CAAC,CAA1B,EAA6B,QAAQ,CAAC,CAAtC;QACX,IAAG,QAAQ,CAAC,MAAT,KAAmB,CAAtB;AACC,iBAAO;YAAC;cACP,IAAA,EACC;gBAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;gBACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;gBAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;eAFM;cAKP,EAAA,EACC;gBAAA,CAAA,EAAG,OAAO,CAAC,CAAX;gBACA,CAAA,EAAG,EADH;eANM;cAQP,CAAA,EAAG,YAAY,CAAC,CART;aAAD;YADR;SAAA,MAAA;AAYC,iBAAO,QAAQ,CAAC,GAAT,CAAa,SAAC,EAAD;YACnB,EAAE,CAAC,CAAH,IAAQ;AACR,mBAAO;cACN,IAAA,EACC;gBAAA,QAAA,EAAU,WAAW,CAAC,IAAI,CAAC,QAA3B;gBACA,MAAA,EAAU,YAAY,CAAC,IAAI,CAAC,MAD5B;gBAEA,OAAA,EAAU,WAAW,CAAC,IAAI,CAAC,OAF3B;eAFK;cAKN,EAAA,EAAI,EALE;cAMN,CAAA,EAAG,YAAY,CAAC,CANV;;UAFY,CAAb,EAZR;SAHI;OAAA,MAAA;AA2BJ,eAAO,CAAC,WAAD,EAAc,YAAd,EA3BH;;IA1Ea,CAlFnB;IAyLA,KAAA,EAAO,CAzLP;IA0LA,OAAA,EAAS,CAAC,CA1LV;IA2LA,SAAA,EAAW,CA3LX;IA4LA,gBAAA,EAAkB,SAAC,MAAD,EAAS,KAAT,EAAgB,QAAhB;AACjB,UAAA;;QADiC,WAAW,SAAC,KAAD,EAAQ,GAAR,GAAA;;MAC5C,KAAA,GAAQ,GAAG,CAAC,SAAJ,CAAc,MAAd,EAAsB,KAAtB;MACR,GAAG,CAAC,oBAAJ,CAAyB,KAAzB;MAEA,GAAA,GAAM;MACN,QAAA,GAAW;AACX,WAAA,uCAAA;;QACC,IAAA,GAAO,IAAK,CAAA,CAAA;QACZ,OAAA,GAAU,IAAK,CAAA,CAAA;QACf,IAAG,IAAA,KAAQ,IAAC,CAAA,KAAZ;UACC,GAAG,CAAC,IAAJ,CACC;YAAA,CAAA,EAAG,OAAH;YACA,CAAA,EAAG,QADH;WADD;UAGA,QAAA,IAAY,OAAO,CAAC,OAJrB;SAAA,MAKK,IAAG,IAAA,KAAQ,IAAC,CAAA,OAAZ;UACJ,GAAG,CAAC,IAAJ,CACC;YAAA,CAAA,EAAG,OAAH;YACA,CAAA,EAAG,QADH;WADD,EADI;SAAA,MAIA,IAAG,IAAA,KAAQ,IAAC,CAAA,SAAZ;UACJ,QAAA,IAAY,OAAO,CAAC,OADhB;SAAA,MAAA;AAGJ,gBAAM,eAHF;;AAZN;AAgBA,aAAO;IAtBU,CA5LlB;;AAPD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/UpdateTrimmer.js.map b/services/track-changes/app/js/UpdateTrimmer.js.map deleted file mode 100644 index 72f24be153..0000000000 --- a/services/track-changes/app/js/UpdateTrimmer.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "UpdateTrimmer.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/UpdateTrimmer.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,YAAA,GAAe,OAAA,CAAQ,gBAAR;;EACf,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,aAAA,GAChB;IAAA,iBAAA,EAAmB,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,UAAR,GAAA;;aAC1C,YAAY,CAAC,kBAAb,CAAgC,UAAhC,EAA4C,SAAC,KAAD,EAAQ,QAAR;QAC3C,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,uBAAG,QAAQ,CAAE,wBAAb;AACC,iBAAO,QAAA,CAAS,IAAT,EAAe,KAAf,EADR;SAAA,MAAA;iBAGC,aAAa,CAAC,iBAAd,CAAgC,UAAhC,EAA4C,SAAC,KAAD,EAAQ,OAAR;AAC3C,gBAAA;YAAA,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,MAAM,CAAC,GAAP,CAAW;cAAA,UAAA,EAAY,UAAZ;cAAwB,OAAA,EAAS,OAAjC;aAAX,EAAqD,aAArD;YACA,4DAAoB,CAAE,4BAAtB;qBACC,YAAY,CAAC,kBAAb,CAAgC,UAAhC,EAA4C;gBAAA,eAAA,EAAiB,IAAjB;eAA5C,EAAmE,SAAC,KAAD;gBAClE,IAA0B,aAA1B;AAAA,yBAAO,QAAA,CAAS,KAAT,EAAP;;uBACA,YAAY,CAAC,cAAb,CAA4B,UAA5B,EAAwC,SAAC,KAAD;kBACvC,IAA0B,aAA1B;AAAA,2BAAO,QAAA,CAAS,KAAT,EAAP;;yBACA,QAAA,CAAS,IAAT,EAAe,KAAf;gBAFuC,CAAxC;cAFkE,CAAnE,EADD;aAAA,MAAA;qBAOC,QAAA,CAAS,IAAT,EAAe,IAAf,EAPD;;UAH2C,CAA5C,EAHD;;MAF2C,CAA5C;IADkB,CAAnB;;AALD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/UpdatesManager.js.map b/services/track-changes/app/js/UpdatesManager.js.map deleted file mode 100644 index 32c9cdd909..0000000000 --- a/services/track-changes/app/js/UpdatesManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "UpdatesManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/UpdatesManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,YAAA,GAAe,OAAA,CAAQ,gBAAR;;EACf,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,YAAA,GAAe,OAAA,CAAQ,gBAAR;;EACf,gBAAA,GAAmB,OAAA,CAAQ,oBAAR;;EACnB,WAAA,GAAc,OAAA,CAAQ,eAAR;;EACd,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,aAAA,GAAgB,OAAA,CAAQ,iBAAR;;EAChB,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,KAAA,GAAQ,OAAA,CAAQ,OAAR;;EACR,CAAA,GAAI,OAAA,CAAQ,YAAR;;EACJ,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,IAAA,GAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;;EAE3B,MAAM,CAAC,OAAP,GAAiB,cAAA,GAChB;IAAA,yBAAA,EAA2B,SAAC,UAAD,EAAa,MAAb,EAAqB,UAArB,EAAiC,SAAjC,EAA4C,QAA5C;AAC1B,UAAA;;QADsE,WAAW,SAAC,KAAD,GAAA;;MACjF,MAAA,GAAS,UAAU,CAAC;MACpB,IAAG,MAAA,KAAU,CAAb;AACC,eAAO,QAAA,CAAA,EADR;;AAIA,WAAA,oDAAA;;cAA6B,CAAA,GAAI;;;QAChC,WAAA,gBAAc,EAAE,CAAE;QAClB,WAAA,0CAA6B,CAAE;QAC/B,IAAG,CAAI,CAAC,WAAA,GAAc,WAAf,CAAP;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,UAAA,EAAY,UAAZ;YAAwB,MAAA,EAAQ,MAAhC;YAAwC,UAAA,EAAW,UAAnD;YAA+D,SAAA,EAAW,SAA1E;YAAqF,WAAA,EAAY,WAAjG;YAA8G,WAAA,EAAY,WAA1H;WAAb,EAAoJ,0BAApJ,EADD;;AAHD;aAQA,YAAY,CAAC,wBAAb,CAAsC,MAAtC,EAA8C,SAAC,KAAD,EAAQ,oBAAR,EAA8B,WAA9B;AAQ7C,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QAGA,IAAG,mBAAH;UACC,gBAAA,GAAmB;UACnB,UAAA,GAAa,UAAU,CAAC,KAAX,CAAiB,CAAjB;AACb,iBAAM,uBAAA,IAAmB,UAAW,CAAA,CAAA,CAAE,CAAC,CAAd,IAAmB,WAA5C;YACC,gBAAgB,CAAC,IAAjB,CAAsB,UAAU,CAAC,KAAX,CAAA,CAAtB;UADD;UAEA,IAAG,gBAAgB,CAAC,MAApB;YACC,MAAM,CAAC,KAAP,CAAa;cAAA,UAAA,EAAY,UAAZ;cAAwB,MAAA,EAAQ,MAAhC;cAAwC,gBAAA,EAAkB,gBAA1D;cAA4E,SAAA,EAAW,SAAvF;cAAkG,WAAA,EAAa,WAA/G;aAAb,EAAyI,mCAAzI,EADD;;UAGA,IAAG,uBAAA,IAAmB,UAAW,CAAA,CAAA,CAAE,CAAC,CAAd,KAAmB,WAAA,GAAc,CAAvD;YACC,EAAA,mFAA+B,CAAE;YACjC,cAAA,GAAoB,UAAH,GAAY,IAAI,IAAJ,CAAS,EAAT,CAAZ,GAA8B;YAC/C,KAAA,GAAQ,IAAI,KAAJ,CAAU,mCAAA,GAAoC,UAAW,CAAA,CAAA,CAAE,CAAC,CAAlD,GAAoD,0CAApD,GAA8F,WAA9F,GAA0G,QAA1G,GAAkH,cAA5H;YACR,MAAM,CAAC,KAAP,CAAa;cAAA,GAAA,EAAK,KAAL;cAAY,MAAA,EAAQ,MAApB;cAA4B,UAAA,EAAY,UAAxC;cAAoD,WAAA,EAAa,EAAjE;cAAqE,SAAA,EAAW,SAAhF;cAA2F,oBAAA,EAAsB,oBAAjH;aAAb,EAAoJ,2BAApJ;YACA,kDAAwB,CAAE,yBAAvB,IAA2C,UAAW,CAAA,CAAA,CAAE,CAAC,CAAd,GAAkB,WAAA,GAAc,CAA9E;cAEC,oBAAA,GAAuB,KAFxB;aAAA,MAAA;AAIC,qBAAO,QAAA,CAAS,KAAT,EAJR;aALD;WARD;;QAmBA,IAAG,UAAU,CAAC,MAAX,KAAqB,CAAxB;AACC,iBAAO,QAAA,CAAA,EADR;;QAKA,oBAAA,GAAuB,CAAA,GAAI,IAAJ,GAAW;AAClC,aAAA,8CAAA;;UACC,OAAA;;AAAW;AAAA;iBAAA,wCAAA;;yDAAK,CAAE,gBAAN,iCAAoB,CAAE;AAAvB;;;UACX,IAAA,GAAO,CAAC,CAAC,GAAF,CAAM,OAAN;UACP,IAAG,IAAA,GAAO,oBAAV;YACC,KAAA,GAAQ,IAAI,KAAJ,CAAU,+CAAA,GAAgD,oBAA1D;YACR,MAAM,CAAC,KAAP,CAAa;cAAA,GAAA,EAAK,KAAL;cAAY,MAAA,EAAQ,MAApB;cAA4B,UAAA,EAAY,UAAxC;cAAoD,IAAA,EAAM,IAA1D;cAAgE,SAAA,EAAW,SAA3E;aAAb,EAAmG,sBAAnG;YACA,SAAS,CAAC,EAAV,GAAe,GAHhB;;AAHD;QAQA,iBAAA,GAAoB,gBAAgB,CAAC,kBAAjB,CAAoC,IAApC,EAA0C,UAA1C;eACpB,WAAW,CAAC,uBAAZ,CAAoC,UAApC,EAAgD,MAAhD,EAAwD,oBAAxD,EAA8E,iBAA9E,EAAiG,SAAjG,EAA4G,SAAC,KAAD,EAAQ,MAAR;UAC3G,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACA,IAAmH,cAAnH;YAAA,MAAM,CAAC,GAAP,CAAW;cAAC,YAAA,UAAD;cAAa,QAAA,MAAb;cAAqB,MAAA,iCAAQ,oBAAoB,CAAE,UAAnD;cAAsD,KAAA,EAAO,MAAM,CAAC,CAApE;aAAX,EAAmF,4BAAnF,EAAA;;iBACA,QAAA,CAAA;QAH2G,CAA5G;MA7C6C,CAA9C;IAd0B,CAA3B;IAiEA,yBAAA,EAA2B,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,EAAQ,SAAR,GAAA;;aAClD,aAAa,CAAC,iBAAd,CAAgC,UAAhC,EAA4C,SAAC,KAAD,EAAQ,SAAR;QAC3C,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,SAAf;MAF2C,CAA5C;IAD0B,CAjE3B;IAuEA,qBAAA,EAAuB,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;;QAAqB,WAAW,SAAC,KAAD,GAAA;;aACtD,YAAY,CAAC,iBAAb,CAA+B,UAA/B,EAA2C,MAA3C,EAAmD,SAAC,KAAD;QAClD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,QAAA,CAAS,IAAT;MAFkD,CAAnD;IADsB,CAvEvB;IA6EA,qBAAA,EAAuB,GA7EvB;IA8EA,0BAAA,EAA4B,SAAC,UAAD,EAAa,MAAb,EAAqB,SAArB,EAAgC,QAAhC;;QAAgC,WAAW,SAAC,KAAD,GAAA;;aAEtE,YAAY,CAAC,mBAAb,CAAiC,MAAjC,EAAyC,cAAc,CAAC,qBAAxD,EAA+E,SAAC,KAAD,EAAQ,UAAR;AAC9E,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,MAAA,GAAS,UAAU,CAAC;eAEpB,YAAY,CAAC,gBAAb,CAA8B,UAA9B,EAA0C,SAAC,KAAD,EAAQ,UAAR;UACzC,IAAG,aAAH;YACC,MAAM,CAAC,GAAP,CAAW;cAAA,UAAA,EAAY,UAAZ;cAAwB,MAAA,EAAQ,MAAhC;cAAwC,UAAA,EAAY,UAApD;aAAX,EAA2E,4BAA3E;AACA,mBAAO,QAAA,CAAS,KAAT,EAFR;;UAGA,MAAM,CAAC,GAAP,CAAW;YAAA,UAAA,EAAY,UAAZ;YAAwB,MAAA,EAAQ,MAAhC;YAAwC,UAAA,EAAY,UAApD;WAAX,EAA2E,kCAA3E;iBACA,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,MAArD,EAA6D,UAA7D,EAAyE,SAAzE,EAAoF,SAAC,KAAD;YACnF,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,MAAM,CAAC,GAAP,CAAW;cAAA,UAAA,EAAY,UAAZ;cAAwB,MAAA,EAAQ,MAAhC;aAAX,EAAmD,kCAAnD;mBAEA,YAAY,CAAC,uBAAb,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,UAAzD,EAAqE,SAAC,KAAD;cACpE,IAA0B,aAA1B;AAAA,uBAAO,QAAA,CAAS,KAAT,EAAP;;cACA,IAAG,MAAA,KAAU,cAAc,CAAC,qBAA5B;gBAEC,MAAM,CAAC,GAAP,CAAW;kBAAA,UAAA,EAAY,UAAZ;kBAAwB,MAAA,EAAQ,MAAhC;iBAAX,EAAmD,+BAAnD;uBACA,UAAA,CAAW,SAAA;yBACV,cAAc,CAAC,0BAAf,CAA0C,UAA1C,EAAsD,MAAtD,EAA8D,SAA9D,EAAyE,QAAzE;gBADU,CAAX,EAEE,CAFF,EAHD;eAAA,MAAA;gBAOC,MAAM,CAAC,GAAP,CAAW;kBAAA,UAAA,EAAY,UAAZ;kBAAwB,MAAA,EAAQ,MAAhC;iBAAX,EAAmD,2BAAnD;uBACA,QAAA,CAAA,EARD;;YAFoE,CAArE;UAJmF,CAApF;QALyC,CAA1C;MAJ8E,CAA/E;IAF2B,CA9E5B;IA0GA,kCAAA,EAAoC,SAAC,UAAD,EAAa,MAAb,EAAqB,QAArB;;QAAqB,WAAW,SAAC,KAAD,GAAA;;aACnE,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,SAAC,KAAD,EAAQ,SAAR;QACpD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,cAAc,CAAC,yCAAf,CAAyD,UAAzD,EAAqE,MAArE,EAA6E,SAA7E,EAAwF,QAAxF;MAFoD,CAArD;IADmC,CA1GpC;IAiHA,yCAAA,EAA2C,SAAC,UAAD,EAAa,MAAb,EAAqB,SAArB,EAAgC,QAAhC;;QAAgC,WAAW,SAAC,KAAD,GAAA;;aACrF,cAAc,CAAC,qBAAf,CAAqC,UAArC,EAAiD,MAAjD,EAAyD,SAAC,KAAD;QACxD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,WAAW,CAAC,WAAZ,CACC,IAAI,CAAC,WAAL,CAAiB;UAAC,QAAA,MAAD;SAAjB,CADD,EAEC,SAAC,WAAD;iBACC,cAAc,CAAC,0BAAf,CAA0C,UAA1C,EAAsD,MAAtD,EAA8D,SAA9D,EAAyE,WAAzE;QADD,CAFD,EAIC,QAJD;MAFwD,CAAzD;IAD0C,CAjH3C;IA4HA,oCAAA,EAAsC,SAAC,UAAD,EAAa,QAAb;;QAAa,WAAW,SAAC,KAAD,GAAA;;aAC7D,YAAY,CAAC,uBAAb,CAAqC,UAArC,EAAiD,SAAC,KAAD,EAAQ,OAAR;QAChD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,cAAc,CAAC,yBAAf,CAAyC,UAAzC,EAAqD,SAAC,KAAD,EAAQ,SAAR;AACpD,cAAA;UAAA,IAAA,GAAO;eAEH,SAAC,MAAD;mBACF,IAAI,CAAC,IAAL,CAAU,SAAC,EAAD;qBACT,cAAc,CAAC,yCAAf,CAAyD,UAAzD,EAAqE,MAArE,EAA6E,SAA7E,EAAwF,EAAxF;YADS,CAAV;UADE;AADJ,eAAA,yCAAA;;eACK;AADL;iBAIA,KAAK,CAAC,aAAN,CAAoB,IAApB,EAA0B,CAA1B,EAA6B,QAA7B;QANoD,CAArD;MAFgD,CAAjD;IADqC,CA5HtC;IAwIA,QAAA,EAAU,SAAC,KAAD,EAAQ,QAAR;;QAAQ,WAAW,SAAC,KAAD,EAAQ,MAAR,GAAA;;aAC5B,YAAY,CAAC,2BAAb,CAAyC,SAAC,KAAD,EAAQ,WAAR;AACxC,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;QACA,MAAM,CAAC,GAAP,CAAW;UAAC,KAAA,wBAAO,WAAW,CAAE,eAArB;UAA6B,WAAA,EAAa,WAA1C;SAAX,EAAmE,gBAAnE;QACA,IAAA,GAAO;QACP,WAAA,GAAc,CAAC,CAAC,OAAF,CAAU,WAAV;QACd,gBAAA,GAAsB,KAAA,GAAQ,CAAX,GAAkB,WAAlB,GAAmC,WAAY;aAE9D,SAAC,UAAD;iBACF,IAAI,CAAC,IAAL,CAAU,SAAC,EAAD;mBACT,cAAc,CAAC,oCAAf,CAAoD,UAApD,EAAgE,SAAC,GAAD;AAC/D,qBAAO,EAAA,CAAG,IAAH,EAAS;gBAAC,MAAA,EAAQ,WAAT;gBAAe,UAAA,EAAY,UAA3B;eAAT;YADwD,CAAhE;UADS,CAAV;QADE;AADJ,aAAA,kDAAA;;aACK;AADL;eAKA,KAAK,CAAC,MAAN,CAAa,IAAb,EAAmB,SAAC,KAAD,EAAQ,MAAR;AAClB,cAAA;UAAA,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UACA,cAAA;;AAAkB;iBAAA,0CAAA;;kBAAkC,CAAC,CAAC;8BAApC,CAAC,CAAC;;AAAF;;;UAClB,iBAAA;;AAAqB;iBAAA,0CAAA;;kBAAkC,CAAI,CAAC,CAAC;8BAAxC,CAAC,CAAC;;AAAF;;;iBACrB,QAAA,CAAS,IAAT,EAAe;YAAC,MAAA,EAAQ,cAAT;YAAyB,SAAA,EAAW,iBAApC;YAAuD,GAAA,EAAK,WAA5D;WAAf;QAJkB,CAAnB;MAXwC,CAAzC;IADS,CAxIV;IA0JA,kBAAA,EAAoB,SAAC,QAAD;;QAAC,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAC/B,YAAY,CAAC,0BAAb,CAAwC,SAAC,KAAD,EAAQ,WAAR;QACvC,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,YAAY,CAAC,2BAAb,CAAyC,SAAC,KAAD,EAAQ,eAAR;AACxC,cAAA;UAAA,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;UAEA,IAAA,GAAO,SAAC,EAAD;mBAAQ,KAAK,CAAC,YAAN,CAAmB,eAAnB,EAAoC,YAAY,CAAC,uBAAjD,EAA0E,EAA1E;UAAR;iBAEP,IAAA,CAAK,SAAC,KAAD,EAAQ,eAAR;AACJ,gBAAA;YAAA,gBAAA,GAAmB,CAAC,CAAC,UAAF,CAAa,WAAb,EAA0B,eAA1B;YACnB,MAAM,CAAC,GAAP,CAAW;cAAC,WAAA,EAAa,WAAd;cAA2B,eAAA,EAAiB,eAA5C;cAA6D,eAAA,EAAiB,eAA9E;cAA+F,gBAAA,EAAkB,gBAAjH;aAAX,EAA+I,+BAA/I;mBACA,QAAA,CAAS,IAAT,EAAe,gBAAf;UAHI,CAAL;QALwC,CAAzC;MAFuC,CAAxC;IADmB,CA1JpB;IAuKA,aAAA,EAAe,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAAmC,QAAnC;;QAAqB,UAAU;;;QAAI,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aAC5D,cAAc,CAAC,kCAAf,CAAkD,UAAlD,EAA8D,MAA9D,EAAsE,SAAC,KAAD;QACrE,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eAEA,WAAW,CAAC,oBAAZ,CAAiC,UAAjC,EAA6C,MAA7C,EAAqD,OAAO,CAAC,IAA7D,EAAmE,OAAO,CAAC,EAA3E,EAA+E,SAAC,KAAD,EAAQ,OAAR;UAC9E,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT,EAAe,OAAf;QAF8E,CAA/E;MAHqE,CAAtE;IADc,CAvKf;IA+KA,yBAAA,EAA2B,SAAC,UAAD,EAAa,MAAb,EAAqB,OAArB,EAAmC,QAAnC;;QAAqB,UAAU;;;QAAI,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;aACxE,cAAc,CAAC,aAAf,CAA6B,UAA7B,EAAyC,MAAzC,EAAiD,OAAjD,EAA0D,SAAC,KAAD,EAAQ,OAAR;QACzD,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,cAAc,CAAC,YAAf,CAA4B,OAA5B,EAAqC,SAAC,KAAD,EAAQ,OAAR;UACpC,IAA0B,aAA1B;AAAA,mBAAO,QAAA,CAAS,KAAT,EAAP;;iBACA,QAAA,CAAS,IAAT,EAAe,OAAf;QAFoC,CAArC;MAFyD,CAA1D;IAD0B,CA/K3B;IAsLA,2BAAA,EAA6B,SAAC,UAAD,EAAa,OAAb,EAA2B,QAA3B;AAC5B,UAAA;;QADyC,UAAU;;;QAAI,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAClE,OAAO,CAAC,cAAR,OAAO,CAAC,YAAc;MACtB,iBAAA,GAAoB;MACpB,MAAA,GAAS,OAAO,CAAC;MACjB,mBAAA,GAAsB;aACtB,cAAc,CAAC,oCAAf,CAAoD,UAApD,EAAgE,SAAC,KAAD;QAC/D,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;eACA,WAAW,CAAC,mBAAZ,CAAgC,UAAhC,EAA4C,MAA5C,EAAoD,SAAC,GAAD,EAAM,QAAN;UACnD,IAAwB,WAAxB;AAAA,mBAAO,QAAA,CAAS,GAAT,EAAP;;iBAEA,KAAK,CAAC,MAAN,CAAa,SAAA;AAEZ,mBAAO,iBAAiB,CAAC,MAAlB,GAA2B,OAAO,CAAC,SAAnC,IAAiD,CAAI,QAAQ,CAAC,IAAT,CAAA;UAFhD,CAAb,EAGE,SAAC,EAAD;mBACD,QAAQ,CAAC,IAAT,CAAc,SAAC,GAAD,EAAM,cAAN;cACb,IAAwB,WAAxB;AAAA,uBAAO,QAAA,CAAS,GAAT,EAAP;;cAEA,IAAe,cAAc,CAAC,MAAf,KAAyB,CAAxC;AAAA,uBAAO,EAAA,CAAA,EAAP;;cACA,mBAAA,GAAsB,cAAe,CAAA,cAAc,CAAC,MAAf,GAAwB,CAAxB,CAA0B,CAAC,IAAI,CAAC;cAErE,iBAAA,GAAoB,cAAc,CAAC,iBAAf,CAAiC,cAAjC,EAAiD,iBAAjD;qBACpB,EAAA,CAAA;YAPa,CAAd;UADC,CAHF,EAYE,SAAA;mBAGD,cAAc,CAAC,sBAAf,CAAsC,iBAAtC,EAAyD,SAAC,GAAD,EAAM,OAAN;cACxD,IAAwB,WAAxB;AAAA,uBAAO,QAAA,CAAS,GAAT,EAAP;;qBACA,QAAA,CAAS,IAAT,EAAe,OAAf,EAA2B,CAAI,QAAQ,CAAC,IAAT,CAAA,CAAP,GAA4B,mBAA5B,GAAqD,MAA7E;YAFwD,CAAzD;UAHC,CAZF;QAHmD,CAApD;MAF+D,CAAhE;IAL4B,CAtL7B;IAmNA,aAAA,EAAe,SAAC,KAAD,EAAQ,QAAR;AACd,UAAA;;QADsB,WAAW,SAAC,KAAD,EAAQ,eAAR,GAAA;;MACjC,IAAA,GAAO;MACP,eAAA,GAAkB;WAEd,SAAC,OAAD;eACF,IAAI,CAAC,IAAL,CAAU,SAAC,QAAD;iBACT,aAAa,CAAC,WAAd,CAA0B,OAA1B,EAAmC,SAAC,KAAD,EAAQ,QAAR;YAClC,IAA0B,aAA1B;AAAA,qBAAO,QAAA,CAAS,KAAT,EAAP;;YACA,eAAgB,CAAA,OAAA,CAAhB,GAA2B;mBAC3B,QAAA,CAAA;UAHkC,CAAnC;QADS,CAAV;MADE;AADJ,WAAA,gBAAA;WACK;AADL;aAQA,KAAK,CAAC,MAAN,CAAa,IAAb,EAAmB,SAAC,GAAD;QAClB,IAAwB,WAAxB;AAAA,iBAAO,QAAA,CAAS,GAAT,EAAP;;eACA,QAAA,CAAS,IAAT,EAAe,eAAf;MAFkB,CAAnB;IAXc,CAnNf;IAkOA,YAAA,EAAc,SAAC,OAAD,EAAU,QAAV;AACb,UAAA;;QADuB,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAClC,KAAA,GAAQ;AACR,WAAA,yCAAA;;QACC,OAAA,GAAU,MAAM,CAAC,IAAI,CAAC;QACtB,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;UACC,KAAM,CAAA,OAAA,CAAN,GAAiB,KADlB;;AAFD;aAKA,cAAc,CAAC,aAAf,CAA6B,KAA7B,EAAoC,SAAC,KAAD,EAAQ,eAAR;AACnC,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;AACA,aAAA,2CAAA;;UACC,OAAA,GAAU,MAAM,CAAC,IAAI,CAAC;UACtB,OAAO,MAAM,CAAC,IAAI,CAAC;UACnB,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;YACC,MAAM,CAAC,IAAI,CAAC,IAAZ,GAAmB,eAAgB,CAAA,OAAA,EADpC;;AAHD;eAKA,QAAA,CAAS,IAAT,EAAe,OAAf;MAPmC,CAApC;IAPa,CAlOd;IAkPA,sBAAA,EAAwB,SAAC,OAAD,EAAU,QAAV;AACvB,UAAA;;QADiC,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAC5C,KAAA,GAAQ;AACR,WAAA,yCAAA;;QACC,QAAA,GAAW,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB;AACnC,aAAA,4CAAA;;UACC,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;YACC,KAAM,CAAA,OAAA,CAAN,GAAiB,KADlB;;AADD;AAFD;aAMA,cAAc,CAAC,aAAf,CAA6B,KAA7B,EAAoC,SAAC,KAAD,EAAQ,eAAR;AACnC,YAAA;QAAA,IAA0B,aAA1B;AAAA,iBAAO,QAAA,CAAS,KAAT,EAAP;;AACA,aAAA,2CAAA;;UACC,QAAA,GAAW,MAAM,CAAC,IAAI,CAAC,QAAZ,IAAwB;UACnC,MAAM,CAAC,IAAI,CAAC,KAAZ,GAAoB;UACpB,OAAO,MAAM,CAAC,IAAI,CAAC;AACnB,eAAA,4CAAA;;YACC,IAAG,cAAc,CAAC,YAAf,CAA4B,OAA5B,CAAH;cACC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAlB,CAAuB,eAAgB,CAAA,OAAA,CAAvC,EADD;aAAA,MAAA;cAGC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAlB,CAAuB,IAAvB,EAHD;;AADD;AAJD;eASA,QAAA,CAAS,IAAT,EAAe,OAAf;MAXmC,CAApC;IARuB,CAlPxB;IAuQA,YAAA,EAAc,SAAC,OAAD;MACb,IAAI,eAAJ;AACC,eAAO,MADR;OAAA,MAAA;AAGC,eAAO,CAAC,CAAC,OAAO,CAAC,KAAR,CAAc,gBAAd,EAHV;;IADa,CAvQd;IA6QA,6BAAA,EAA+B,WAAA,GAAc,CAAA,GAAI,EAAJ,GAAS,IA7QtD;IA8QA,oBAAA,EAAsB,EA9QtB;IA+QA,iBAAA,EAAmB,SAAC,OAAD,EAAU,yBAAV;AAClB,UAAA;;QAD4B,4BAA4B;;MACxD,iBAAA,GAAoB,yBAAyB,CAAC,KAA1B,CAAA;MACpB,0BAAA,GAA6B;AAC7B,WAAA,yCAAA;;QACC,cAAA,GAAiB,iBAAkB,CAAA,iBAAiB,CAAC,MAAlB,GAA2B,CAA3B;QACnC,YAAA,GAAe;QAQf,IAAG,0BAAH;UACC,YAAA,GAAe,MADhB;SAAA,MAEK,IAAG,cAAA,IAAmB,cAAc,CAAC,IAAI,CAAC,MAApB,GAA6B,MAAM,CAAC,IAAI,CAAC,QAAzC,GAAoD,IAAC,CAAA,6BAA3E;UAGJ,YAAA,GAAe,KAHX;;QAKL,WAAA,GAAc;AACd;AAAA,aAAA,uCAAA;;UACC,IAAG,cAAA,IAAU,EAAE,CAAC,CAAC,CAAC,MAAL,GAAc,IAAC,CAAA,oBAA5B;YACC,WAAA,GAAc,KADf;;AADD;QAIA,0BAAA,GAA6B;QAE7B,IAAG,YAAH;UAGC,cAAc,CAAC,IAAI,CAAC,QAApB,GAA+B,CAAC,CAAC,KAAF,CAAQ,cAAc,CAAC,IAAI,CAAC,QAA5B,EAAsC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAb,CAAtC;UAE/B,MAAA,GAAS,MAAM,CAAC,MAAM,CAAC,QAAd,CAAA;UACT,GAAA,GAAM,cAAc,CAAC,IAAK,CAAA,MAAA;UAC1B,IAAG,WAAH;YACC,GAAG,CAAC,KAAJ,GAAY,IAAI,CAAC,GAAL,CAAS,GAAG,CAAC,KAAb,EAAoB,MAAM,CAAC,CAA3B;YACZ,GAAG,CAAC,GAAJ,GAAU,IAAI,CAAC,GAAL,CAAS,GAAG,CAAC,GAAb,EAAkB,MAAM,CAAC,CAAzB,EAFX;WAAA,MAAA;YAIC,cAAc,CAAC,IAAK,CAAA,MAAA,CAApB,GACC;cAAA,KAAA,EAAO,MAAM,CAAC,CAAd;cACA,GAAA,EAAK,MAAM,CAAC,CADZ;cALF;;UAQA,cAAc,CAAC,IAAI,CAAC,QAApB,GAA+B,IAAI,CAAC,GAAL,CAAS,cAAc,CAAC,IAAI,CAAC,QAA7B,EAAuC,MAAM,CAAC,IAAI,CAAC,QAAnD;UAC/B,cAAc,CAAC,IAAI,CAAC,MAApB,GAA+B,IAAI,CAAC,GAAL,CAAS,cAAc,CAAC,IAAI,CAAC,MAA7B,EAAqC,MAAM,CAAC,IAAI,CAAC,MAAjD,EAhBhC;SAAA,MAAA;UAkBC,SAAA,GACC;YAAA,IAAA,EACC;cAAA,QAAA,EAAU,EAAV;cACA,QAAA,EAAU,MAAM,CAAC,IAAI,CAAC,QADtB;cAEA,MAAA,EAAQ,MAAM,CAAC,IAAI,CAAC,MAFpB;aADD;YAIA,IAAA,EAAM,EAJN;;UAMD,SAAS,CAAC,IAAK,CAAA,MAAM,CAAC,MAAM,CAAC,QAAd,CAAA,CAAA,CAAf,GACC;YAAA,KAAA,EAAO,MAAM,CAAC,CAAd;YACA,GAAA,EAAK,MAAM,CAAC,CADZ;;UAED,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAxB,CAA6B,MAAM,CAAC,IAAI,CAAC,OAAzC;UACA,iBAAiB,CAAC,IAAlB,CAAuB,SAAvB,EA7BD;;AAxBD;AAuDA,aAAO;IA1DW,CA/QnB;;AAdD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/WebApiManager.js.map b/services/track-changes/app/js/WebApiManager.js.map deleted file mode 100644 index 1ef0953f13..0000000000 --- a/services/track-changes/app/js/WebApiManager.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "WebApiManager.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/WebApiManager.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,OAAA,GAAU,OAAA,CAAQ,cAAR;;EACV,MAAA,GAAS,OAAA,CAAQ,mBAAR;;EACT,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EAGX,uBAAA,GAA0B;;EAO1B,MAAM,CAAC,OAAP,GAAiB,aAAA,GAChB;IAAA,WAAA,EAAa,SAAC,GAAD,EAAM,QAAN;;QAAM,WAAW,SAAC,KAAD,EAAQ,IAAR,GAAA;;aAC7B,OAAO,CAAC,GAAR,CAAY;QACX,GAAA,EAAK,EAAA,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAArB,GAA2B,GADrB;QAEX,OAAA,EAAS,uBAFE;QAGX,WAAA,EAAa,CAHF;QAIX,IAAA,EACC;UAAA,IAAA,EAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAxB;UACA,IAAA,EAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IADxB;UAEA,eAAA,EAAiB,IAFjB;SALU;OAAZ,EAQG,SAAC,KAAD,EAAQ,GAAR,EAAa,IAAb;QACF,IAAG,aAAH;AACC,iBAAO,QAAA,CAAS,KAAT,EADR;;QAEA,IAAG,GAAG,CAAC,UAAJ,KAAkB,GAArB;UACC,MAAM,CAAC,GAAP,CAAW;YAAA,GAAA,EAAK,GAAL;WAAX,EAAqB,sBAArB;AACA,iBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EAFR;;QAGA,IAAG,GAAG,CAAC,UAAJ,IAAkB,GAAlB,IAA0B,GAAG,CAAC,UAAJ,GAAiB,GAA9C;AACC,iBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EADR;SAAA,MAAA;UAGC,KAAA,GAAQ,IAAI,KAAJ,CAAU,0CAAA,GAA2C,GAAG,CAAC,UAA/C,GAA0D,cAA1D,GAAwE,GAAG,CAAC,QAA5E,GAAqF,GAA/F;iBACR,QAAA,CAAS,KAAT,EAJD;;MANE,CARH;IADY,CAAb;IAqBA,WAAA,EAAa,SAAC,OAAD,EAAU,QAAV;AACZ,UAAA;;QADsB,WAAW,SAAC,KAAD,EAAQ,QAAR,GAAA;;MACjC,GAAA,GAAM,QAAA,GAAS,OAAT,GAAiB;MACvB,MAAM,CAAC,GAAP,CAAW;QAAA,OAAA,EAAS,OAAT;OAAX,EAA6B,4BAA7B;aACA,aAAa,CAAC,WAAd,CAA0B,GAA1B,EAA+B,SAAC,KAAD,EAAQ,IAAR;AAC9B,YAAA;QAAA,IAAG,aAAH;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,OAAA,EAAS,OAArB;YAA8B,GAAA,EAAK,GAAnC;WAAb,EAAqD,qBAArD;AACA,iBAAO,QAAA,CAAS,KAAT,EAFR;;QAIA,IAAG,IAAA,KAAQ,IAAX;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,OAAA,EAAS,OAAT;YAAkB,GAAA,EAAK,GAAvB;WAAb,EAAyC,eAAzC;AACA,iBAAO,QAAA,CAAS,IAAT,EAAe,IAAf,EAFR;;AAGA;UACC,IAAA,GAAO,IAAI,CAAC,KAAL,CAAW,IAAX,EADR;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,KAAT,EAHR;;eAIA,QAAA,CAAS,IAAT,EAAe;UACd,EAAA,EAAI,IAAI,CAAC,EADK;UAEd,KAAA,EAAO,IAAI,CAAC,KAFE;UAGd,UAAA,EAAY,IAAI,CAAC,UAHH;UAId,SAAA,EAAW,IAAI,CAAC,SAJF;SAAf;MAZ8B,CAA/B;IAHY,CArBb;IA2CA,iBAAA,EAAmB,SAAC,UAAD,EAAa,QAAb;AAClB,UAAA;;QAD+B,WAAW,SAAC,KAAD,EAAQ,OAAR,GAAA;;MAC1C,GAAA,GAAM,WAAA,GAAY,UAAZ,GAAuB;MAC7B,MAAM,CAAC,GAAP,CAAW;QAAA,UAAA,EAAY,UAAZ;OAAX,EAAmC,kCAAnC;aACA,aAAa,CAAC,WAAd,CAA0B,GAA1B,EAA+B,SAAC,KAAD,EAAQ,IAAR;AAC9B,YAAA;QAAA,IAAG,aAAH;UACC,MAAM,CAAC,KAAP,CAAa;YAAA,GAAA,EAAK,KAAL;YAAY,UAAA,EAAY,UAAxB;YAAoC,GAAA,EAAK,GAAzC;WAAb,EAA2D,qBAA3D;AACA,iBAAO,QAAA,CAAS,KAAT,EAFR;;AAIA;UACC,OAAA,GAAU,IAAI,CAAC,KAAL,CAAW,IAAX,EADX;SAAA,cAAA;UAEM;AACL,iBAAO,QAAA,CAAS,KAAT,EAHR;;eAIA,QAAA,CAAS,IAAT,EAAe,OAAf;MAT8B,CAA/B;IAHkB,CA3CnB;;AAbD" -} \ No newline at end of file diff --git a/services/track-changes/app/js/mongojs.js.map b/services/track-changes/app/js/mongojs.js.map deleted file mode 100644 index 9c2dbd94d8..0000000000 --- a/services/track-changes/app/js/mongojs.js.map +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 3, - "file": "mongojs.js", - "sourceRoot": "../..", - "sources": [ - "app/coffee/mongojs.coffee" - ], - "names": [], - "mappings": ";AAAA;AAAA,MAAA;;EAAA,QAAA,GAAW,OAAA,CAAQ,qBAAR;;EACX,OAAA,GAAU,OAAA,CAAQ,SAAR;;EACV,IAAA,GAAO,OAAA,CAAQ,MAAR;;EACP,EAAA,GAAK,OAAA,CAAQ,QAAQ,CAAC,KAAK,CAAC,GAAvB,EAA4B,CAAC,YAAD,EAAe,wBAAf,EAAyC,iBAAzC,CAA5B;;EACL,MAAM,CAAC,OAAP,GACC;IAAA,EAAA,EAAI,EAAJ;IACA,QAAA,EAAU,OAAO,CAAC,QADlB;IAEA,IAAA,EAAM,IAAI,IAAI,CAAC,QAAT,CAAA,CAFN;;AALD" -} \ No newline at end of file From 0f00aa4d47e65219a67e0986c2d76e5681182056 Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Fri, 19 Jun 2020 19:24:59 +0200 Subject: [PATCH 508/549] Added *.js.map entry to .gitignore --- services/track-changes/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index 1cdbe5e293..9d98c9ecf2 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -1,3 +1,4 @@ **.swp node_modules/ forever/ +*.js.map \ No newline at end of file From e96538f7ba7af451b357a37449342557cf98ac2e Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Fri, 19 Jun 2020 19:25:40 +0200 Subject: [PATCH 509/549] Added new line at the end of .gitignore --- services/track-changes/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index 9d98c9ecf2..72cd163116 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -1,4 +1,4 @@ **.swp node_modules/ forever/ -*.js.map \ No newline at end of file +*.js.map From 4dba0a3170e0329d5fb0135814cb90818c4e2861 Mon Sep 17 00:00:00 2001 From: Miguel Serrano Date: Mon, 29 Jun 2020 12:59:38 +0200 Subject: [PATCH 510/549] Removed pinned version of adobe/s3mock docker image --- services/track-changes/docker-compose.ci.yml | 2 +- services/track-changes/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 99232575ca..4e41056770 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -52,7 +52,7 @@ services: mongo: image: mongo:3.6 s3: - image: adobe/s3mock:2.1.20 + image: adobe/s3mock environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket healthcheck: diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 85d9c9e915..27652dedab 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -51,7 +51,7 @@ services: image: mongo:3.6 s3: - image: adobe/s3mock:2.1.20 + image: adobe/s3mock environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket healthcheck: From abe365328617d15d3d7e19700d4b1e179ae83d16 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 28 Jul 2020 09:25:30 +0100 Subject: [PATCH 511/549] Upgrade redis-sharelatex to 1.0.13 --- services/track-changes/package-lock.json | 14 +++++++------- services/track-changes/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 39ef59a0b9..62572977ac 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -3612,9 +3612,9 @@ } }, "ioredis": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.16.1.tgz", - "integrity": "sha512-g76Mm9dE7BLuewncu1MimGZw5gDDjDwjoRony/VoSxSJEKAhuYncDEwYKYjtHi2NWsTNIB6XXRjE64uVa/wpKQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.17.3.tgz", + "integrity": "sha512-iRvq4BOYzNFkDnSyhx7cmJNOi1x/HWYe+A4VXHBu4qpwJaGT1Mp+D2bVGJntH9K/Z/GeOM/Nprb8gB3bmitz1Q==", "requires": { "cluster-key-slot": "^1.1.0", "debug": "^4.1.1", @@ -6139,13 +6139,13 @@ } }, "redis-sharelatex": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.12.tgz", - "integrity": "sha512-Z+LDGaRNgZ+NiDaCC/R0N3Uy6SCtbKXqiXlvCwAbIQRSZUc69OVx/cQ3i5qDF7zeERhh+pnTd+zGs8nVfa5p+Q==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.13.tgz", + "integrity": "sha512-sAQNofqfcMlIxzxNJF1qUspJKDM1VuuIOrGZQX9nb5JtcJ5cusa5sc+Oyb51eymPV5mZGWT3u07tKtv4jdXVIg==", "requires": { "async": "^2.5.0", "coffee-script": "1.8.0", - "ioredis": "~4.16.1", + "ioredis": "~4.17.3", "redis-sentinel": "0.1.1", "underscore": "1.7.0" }, diff --git a/services/track-changes/package.json b/services/track-changes/package.json index a87cb01e17..91e8c160b1 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -32,7 +32,7 @@ "mongo-uri": "^0.1.2", "mongojs": "3.1.0", "redis": "~0.10.1", - "redis-sharelatex": "^1.0.12", + "redis-sharelatex": "^1.0.13", "request": "~2.88.2", "requestretry": "^4.1.0", "s3-streams": "^0.4.0", From 05d2712eb121d4376926089cddb58725aebef1cc Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 10 Aug 2020 17:23:17 +0100 Subject: [PATCH 512/549] [misc] bump the dev-env to 3.3.2 --- services/track-changes/.eslintrc | 2 +- services/track-changes/.github/dependabot.yml | 17 +++ services/track-changes/.gitignore | 3 + services/track-changes/Dockerfile | 4 +- services/track-changes/Jenkinsfile | 131 ------------------ services/track-changes/Makefile | 48 +++++-- services/track-changes/buildscript.txt | 4 +- services/track-changes/docker-compose.ci.yml | 10 +- services/track-changes/docker-compose.yml | 14 +- services/track-changes/nodemon.json | 1 - services/track-changes/package.json | 8 +- .../test/acceptance/deps/Dockerfile.s3mock | 4 + .../test/acceptance/deps/healthcheck.sh | 9 ++ 13 files changed, 91 insertions(+), 164 deletions(-) create mode 100644 services/track-changes/.github/dependabot.yml delete mode 100644 services/track-changes/Jenkinsfile create mode 100644 services/track-changes/test/acceptance/deps/Dockerfile.s3mock create mode 100644 services/track-changes/test/acceptance/deps/healthcheck.sh diff --git a/services/track-changes/.eslintrc b/services/track-changes/.eslintrc index 2e945d6ffb..76dad1561d 100644 --- a/services/track-changes/.eslintrc +++ b/services/track-changes/.eslintrc @@ -8,7 +8,7 @@ "prettier/standard" ], "parserOptions": { - "ecmaVersion": 2017 + "ecmaVersion": 2018 }, "plugins": [ "mocha", diff --git a/services/track-changes/.github/dependabot.yml b/services/track-changes/.github/dependabot.yml new file mode 100644 index 0000000000..c6f98d843d --- /dev/null +++ b/services/track-changes/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "daily" + + pull-request-branch-name: + # Separate sections of the branch name with a hyphen + # Docker images use the branch name and do not support slashes in tags + # https://github.com/overleaf/google-ops/issues/822 + # https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#pull-request-branch-nameseparator + separator: "-" + + # Block informal upgrades -- security upgrades use a separate queue. + # https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#open-pull-requests-limit + open-pull-requests-limit: 0 diff --git a/services/track-changes/.gitignore b/services/track-changes/.gitignore index 72cd163116..008dc714ba 100644 --- a/services/track-changes/.gitignore +++ b/services/track-changes/.gitignore @@ -2,3 +2,6 @@ node_modules/ forever/ *.js.map + +# managed by dev-environment$ bin/update_build_scripts +.npmrc diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 7b47fb9ee2..78a715757d 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -11,12 +11,10 @@ FROM base as app #wildcard as some files may not be in all repos COPY package*.json npm-shrink*.json /app/ -RUN npm install --quiet +RUN npm ci --quiet COPY . /app - - FROM base COPY --from=app /app /app diff --git a/services/track-changes/Jenkinsfile b/services/track-changes/Jenkinsfile deleted file mode 100644 index 71f95c3c14..0000000000 --- a/services/track-changes/Jenkinsfile +++ /dev/null @@ -1,131 +0,0 @@ -String cron_string = BRANCH_NAME == "master" ? "@daily" : "" - -pipeline { - agent any - - environment { - GIT_PROJECT = "track-changes" - JENKINS_WORKFLOW = "track-changes-sharelatex" - TARGET_URL = "${env.JENKINS_URL}blue/organizations/jenkins/${JENKINS_WORKFLOW}/detail/$BRANCH_NAME/$BUILD_NUMBER/pipeline" - GIT_API_URL = "https://api.github.com/repos/overleaf/${GIT_PROJECT}/statuses/$GIT_COMMIT" - } - - triggers { - pollSCM('* * * * *') - cron(cron_string) - } - - stages { - - stage('Install') { - steps { - withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { - sh "curl $GIT_API_URL \ - --data '{ \ - \"state\" : \"pending\", \ - \"target_url\": \"$TARGET_URL\", \ - \"description\": \"Your build is underway\", \ - \"context\": \"ci/jenkins\" }' \ - -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" - } - } - } - - stage('Build') { - steps { - sh 'make build' - } - } - - stage('Linting') { - steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make format' - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make lint' - } - } - - stage('Unit Tests') { - steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_unit' - } - } - - stage('Acceptance Tests') { - steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' - } - } - - stage('Package and docker push') { - steps { - sh 'echo ${BUILD_NUMBER} > build_number.txt' - sh 'touch build.tar.gz' // Avoid tar warning about files changing during read - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make tar' - - withCredentials([file(credentialsId: 'gcr.io_overleaf-ops', variable: 'DOCKER_REPO_KEY_PATH')]) { - sh 'docker login -u _json_key --password-stdin https://gcr.io/overleaf-ops < ${DOCKER_REPO_KEY_PATH}' - } - sh 'DOCKER_REPO=gcr.io/overleaf-ops make publish' - sh 'docker logout https://gcr.io/overleaf-ops' - - } - } - - stage('Publish to s3') { - steps { - sh 'echo ${BRANCH_NAME}-${BUILD_NUMBER} > build_number.txt' - withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { - s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") - } - withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { - // The deployment process uses this file to figure out the latest build - s3Upload(file:'build_number.txt', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/latest") - } - } - } - } - - post { - always { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_clean' - sh 'make clean' - } - - success { - withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { - sh "curl $GIT_API_URL \ - --data '{ \ - \"state\" : \"success\", \ - \"target_url\": \"$TARGET_URL\", \ - \"description\": \"Your build succeeded!\", \ - \"context\": \"ci/jenkins\" }' \ - -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" - } - } - - failure { - mail(from: "${EMAIL_ALERT_FROM}", - to: "${EMAIL_ALERT_TO}", - subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", - body: "Build: ${BUILD_URL}") - withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { - sh "curl $GIT_API_URL \ - --data '{ \ - \"state\" : \"failure\", \ - \"target_url\": \"$TARGET_URL\", \ - \"description\": \"Your build failed\", \ - \"context\": \"ci/jenkins\" }' \ - -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" - } - } - } - - // The options directive is for configuration that applies to the whole job. - options { - // we'd like to make sure remove old builds, so we don't fill up our storage! - buildDiscarder(logRotator(numToKeepStr:'50')) - - // And we'd really like to be sure that this build doesn't hang forever, so let's time it out after: - timeout(time: 30, unit: 'MINUTES') - } -} diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index e1b4843fea..eaff3428ca 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -5,6 +5,8 @@ BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) PROJECT_NAME = track-changes +BUILD_DIR_NAME = $(shell pwd | xargs basename | tr -cd '[a-zA-Z0-9_.\-]') + DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ BRANCH_NAME=$(BRANCH_NAME) \ @@ -12,39 +14,63 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ MOCHA_GREP=${MOCHA_GREP} \ docker-compose ${DOCKER_COMPOSE_FLAGS} +DOCKER_COMPOSE_TEST_ACCEPTANCE = \ + COMPOSE_PROJECT_NAME=test_acceptance_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) + +DOCKER_COMPOSE_TEST_UNIT = \ + COMPOSE_PROJECT_NAME=test_unit_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) + clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) format: - $(DOCKER_COMPOSE) run --rm test_unit npm run format + $(DOCKER_COMPOSE) run --rm test_unit npm run --silent format format_fix: - $(DOCKER_COMPOSE) run --rm test_unit npm run format:fix + $(DOCKER_COMPOSE) run --rm test_unit npm run --silent format:fix lint: - $(DOCKER_COMPOSE) run --rm test_unit npm run lint + $(DOCKER_COMPOSE) run --rm test_unit npm run --silent lint test: format lint test_unit test_acceptance test_unit: - @[ ! -d test/unit ] && echo "track-changes has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit +ifneq (,$(wildcard test/unit)) + $(DOCKER_COMPOSE_TEST_UNIT) run --rm test_unit + $(MAKE) test_unit_clean +endif -test_acceptance: test_clean test_acceptance_pre_run test_acceptance_run +test_clean: test_unit_clean +test_unit_clean: +ifneq (,$(wildcard test/unit)) + $(DOCKER_COMPOSE_TEST_UNIT) down -v -t 0 +endif -test_acceptance_debug: test_clean test_acceptance_pre_run test_acceptance_run_debug +test_acceptance: test_acceptance_clean test_acceptance_pre_run test_acceptance_run + $(MAKE) test_acceptance_clean + +test_acceptance_debug: test_acceptance_clean test_acceptance_pre_run test_acceptance_run_debug + $(MAKE) test_acceptance_clean test_acceptance_run: - @[ ! -d test/acceptance ] && echo "track-changes has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance +ifneq (,$(wildcard test/acceptance)) + $(DOCKER_COMPOSE_TEST_ACCEPTANCE) run --rm test_acceptance +endif test_acceptance_run_debug: - @[ ! -d test/acceptance ] && echo "track-changes has no acceptance tests" || $(DOCKER_COMPOSE) run -p 127.0.0.9:19999:19999 --rm test_acceptance npm run test:acceptance -- --inspect=0.0.0.0:19999 --inspect-brk +ifneq (,$(wildcard test/acceptance)) + $(DOCKER_COMPOSE_TEST_ACCEPTANCE) run -p 127.0.0.9:19999:19999 --rm test_acceptance npm run test:acceptance -- --inspect=0.0.0.0:19999 --inspect-brk +endif -test_clean: - $(DOCKER_COMPOSE) down -v -t 0 +test_clean: test_acceptance_clean +test_acceptance_clean: + $(DOCKER_COMPOSE_TEST_ACCEPTANCE) down -v -t 0 test_acceptance_pre_run: - @[ ! -f test/acceptance/js/scripts/pre-run ] && echo "track-changes has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/js/scripts/pre-run +ifneq (,$(wildcard test/acceptance/js/scripts/pre-run)) + $(DOCKER_COMPOSE_TEST_ACCEPTANCE) run --rm test_acceptance test/acceptance/js/scripts/pre-run +endif build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 1551e8fbf3..1750deaa0e 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -1,10 +1,8 @@ track-changes ---acceptance-creds=None --dependencies=mongo,redis,s3 --docker-repos=gcr.io/overleaf-ops --env-add=AWS_BUCKET=bucket --env-pass-through= ---language=es --node-version=10.21.0 --public-repo=True ---script-version=2.0.0 +--script-version=3.3.2 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 4e41056770..6717530161 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -11,6 +11,7 @@ services: command: npm run test:unit:_run environment: NODE_ENV: test + NODE_OPTIONS: "--unhandled-rejections=strict" test_acceptance: @@ -27,6 +28,7 @@ services: AWS_SECRET_ACCESS_KEY: fake MOCHA_GREP: ${MOCHA_GREP} NODE_ENV: test + NODE_OPTIONS: "--unhandled-rejections=strict" AWS_BUCKET: bucket depends_on: mongo: @@ -50,10 +52,10 @@ services: image: redis mongo: - image: mongo:3.6 + image: mongo:4.0 s3: - image: adobe/s3mock + build: + context: test/acceptance/deps + dockerfile: Dockerfile.s3mock environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9090"] diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 27652dedab..ba2b13aad6 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -13,7 +13,8 @@ services: environment: MOCHA_GREP: ${MOCHA_GREP} NODE_ENV: test - command: npm run test:unit + NODE_OPTIONS: "--unhandled-rejections=strict" + command: npm run --silent test:unit user: node test_acceptance: @@ -33,6 +34,7 @@ services: MOCHA_GREP: ${MOCHA_GREP} LOG_LEVEL: ERROR NODE_ENV: test + NODE_OPTIONS: "--unhandled-rejections=strict" AWS_BUCKET: bucket user: node depends_on: @@ -42,17 +44,17 @@ services: condition: service_healthy s3: condition: service_healthy - command: npm run test:acceptance + command: npm run --silent test:acceptance redis: image: redis mongo: - image: mongo:3.6 + image: mongo:4.0 s3: - image: adobe/s3mock + build: + context: test/acceptance/deps + dockerfile: Dockerfile.s3mock environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9090"] diff --git a/services/track-changes/nodemon.json b/services/track-changes/nodemon.json index 5826281b84..e3e8817d90 100644 --- a/services/track-changes/nodemon.json +++ b/services/track-changes/nodemon.json @@ -8,7 +8,6 @@ "execMap": { "js": "npm run start" }, - "watch": [ "app/js/", "app.js", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 91e8c160b1..28f8d26406 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -13,7 +13,7 @@ "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", "test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP", "nodemon": "nodemon --config nodemon.json", - "lint": "node_modules/.bin/eslint .", + "lint": "node_modules/.bin/eslint --max-warnings 0 .", "format": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --list-different", "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" }, @@ -45,8 +45,8 @@ "chai": "~4.2.0", "cli": "^1.0.1", "eslint": "^6.8.0", - "eslint-config-prettier": "^6.10.1", - "eslint-config-standard": "^14.1.1", + "eslint-config-prettier": "^6.10.0", + "eslint-config-standard": "^14.1.0", "eslint-config-standard-jsx": "^8.1.0", "eslint-config-standard-react": "^9.2.0", "eslint-plugin-chai-expect": "^2.1.0", @@ -61,7 +61,7 @@ "eslint-plugin-standard": "^4.0.1", "memorystream": "0.3.1", "mocha": "^7.1.1", - "prettier": "^2.0.1", + "prettier": "^2.0.0", "prettier-eslint-cli": "^5.0.0", "sandboxed-module": "~2.0.3", "sinon": "~9.0.1", diff --git a/services/track-changes/test/acceptance/deps/Dockerfile.s3mock b/services/track-changes/test/acceptance/deps/Dockerfile.s3mock new file mode 100644 index 0000000000..15eda4dd4b --- /dev/null +++ b/services/track-changes/test/acceptance/deps/Dockerfile.s3mock @@ -0,0 +1,4 @@ +FROM adobe/s3mock +RUN apk add --update --no-cache curl +COPY healthcheck.sh /healthcheck.sh +HEALTHCHECK --interval=1s --timeout=1s --retries=30 CMD /healthcheck.sh http://localhost:9090 diff --git a/services/track-changes/test/acceptance/deps/healthcheck.sh b/services/track-changes/test/acceptance/deps/healthcheck.sh new file mode 100644 index 0000000000..cd19cea637 --- /dev/null +++ b/services/track-changes/test/acceptance/deps/healthcheck.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# health check to allow 404 status code as valid +STATUSCODE=$(curl --silent --output /dev/null --write-out "%{http_code}" $1) +# will be 000 on non-http error (e.g. connection failure) +if test $STATUSCODE -ge 500 || test $STATUSCODE -lt 200; then + exit 1 +fi +exit 0 From f39e31c6fd6a24f9441c85a0cdda3c2fc89b7a55 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Wed, 12 Aug 2020 16:16:42 +0100 Subject: [PATCH 513/549] [misc] bump logger-sharelatex to version 2.2.0 --- services/track-changes/package-lock.json | 376 ++++++++++++++--------- services/track-changes/package.json | 2 +- 2 files changed, 239 insertions(+), 139 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 62572977ac..a3545e5560 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -153,6 +153,24 @@ "google-auth-library": "^5.5.0", "retry-request": "^4.0.0", "teeny-request": "^6.0.0" + }, + "dependencies": { + "google-auth-library": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + } + } } }, "@google-cloud/debug-agent": { @@ -375,6 +393,22 @@ "type-fest": "^0.12.0" }, "dependencies": { + "google-auth-library": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + } + }, "type-fest": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", @@ -383,12 +417,12 @@ } }, "@google-cloud/logging-bunyan": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/logging-bunyan/-/logging-bunyan-2.0.3.tgz", - "integrity": "sha512-8n9MwsCRd4v8WZg17+d3m7qInud7lYTm5rpwXHY0/lzWEJYjeiztT09BiCYh56EEhHr+ynymJnzUDZKazkywlg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/logging-bunyan/-/logging-bunyan-3.0.0.tgz", + "integrity": "sha512-ZLVXEejNQ27ktGcA3S/sd7GPefp7kywbn+/KoBajdb1Syqcmtc98jhXpYQBXVtNP2065iyu77s4SBaiYFbTC5A==", "requires": { "@google-cloud/logging": "^7.0.0", - "google-auth-library": "^5.0.0" + "google-auth-library": "^6.0.0" } }, "@google-cloud/paginator": { @@ -795,9 +829,9 @@ } }, "@grpc/grpc-js": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.6.18.tgz", - "integrity": "sha512-uAzv/tM8qpbf1vpx1xPMfcUMzbfdqJtdCYAqY/LsLeQQlnTb4vApylojr+wlCyr7bZeg3AFfHvtihnNOQQt/nA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.5.tgz", + "integrity": "sha512-Hm+xOiqAhcpT9RYM8lc15dbQD7aQurM7ZU8ulmulepiPlN7iwBXXwP3vSBUimoFoApRqz7pSIisXU8pZaCB4og==", "requires": { "semver": "^6.2.0" }, @@ -810,9 +844,9 @@ } }, "@grpc/proto-loader": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.3.tgz", - "integrity": "sha512-8qvUtGg77G2ZT2HqdqYoM/OY97gQd/0crSG34xNmZ4ZOsv3aQT/FQV9QfZPazTGna6MIoyUd+u6AxsoZjJ/VMQ==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.5.tgz", + "integrity": "sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ==", "requires": { "lodash.camelcase": "^4.3.0", "protobufjs": "^6.8.6" @@ -860,9 +894,9 @@ } }, "@overleaf/o-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-2.1.0.tgz", - "integrity": "sha512-Zd9sks9LrLw8ErHt/cXeWIkyxWAqNAvNGn7wIjLQJH6TTEEW835PWOhpch+hQwwWsTxWIx/JDj+IpZ3ouw925g==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.0.0.tgz", + "integrity": "sha512-LsM2s6Iy9G97ktPo0ys4VxtI/m3ahc1ZHwjo5XnhXtjeIkkkVAehsrcRRoV/yWepPjymB0oZonhcfojpjYR/tg==" }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -969,9 +1003,9 @@ "dev": true }, "@tootallnate/once": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", - "integrity": "sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, "@types/caseless": { "version": "0.12.2", @@ -1004,9 +1038,9 @@ "dev": true }, "@types/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", + "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", "requires": { "@types/node": "*" } @@ -1166,9 +1200,9 @@ "dev": true }, "agent-base": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", - "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.1.tgz", + "integrity": "sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==", "requires": { "debug": "4" } @@ -1668,6 +1702,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -1887,10 +1926,15 @@ "which": "^1.2.9" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, "d64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d64/-/d64-1.0.0.tgz", - "integrity": "sha512-5eNy3WZziVYnrogqgXhcdEmqcDB2IHurTqLcrgssJsfkMVCUoUaZpK6cJjxxvLV2dUm5SuJMNcYfVGoin9UIRw==" + "integrity": "sha1-QAKofoUMv8n52XBrYPymE6MzbpA=" }, "damerau-levenshtein": { "version": "1.0.6", @@ -3135,9 +3179,9 @@ "dev": true }, "gaxios": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.2.tgz", - "integrity": "sha512-K/+py7UvKRDaEwEKlLiRKrFr+wjGjsMz5qH7Vs549QJS7cpSCOT/BbWL7pzqECflc46FcNPipjSfB+V1m8PAhw==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz", + "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==", "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -3216,37 +3260,100 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "google-auth-library": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", - "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.0.6.tgz", + "integrity": "sha512-fWYdRdg55HSJoRq9k568jJA1lrhg9i2xgfhVIMJbskUmbDpJGHsbv9l41DGhCDXM21F9Kn4kUwdysgxSYBYJUw==", "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "fast-text-encoding": "^1.0.0", - "gaxios": "^2.1.0", - "gcp-metadata": "^3.4.0", - "gtoken": "^4.1.0", + "gaxios": "^3.0.0", + "gcp-metadata": "^4.1.0", + "gtoken": "^5.0.0", "jws": "^4.0.0", - "lru-cache": "^5.0.0" + "lru-cache": "^6.0.0" }, "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, + "gaxios": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.1.0.tgz", + "integrity": "sha512-DDTn3KXVJJigtz+g0J3vhcfbDbKtAroSTxauWsdnP57sM5KZ3d2c/3D9RKFJ86s43hfw6WULg6TXYw/AYiBlpA==", "requires": { - "yallist": "^3.0.2" + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" } + }, + "gcp-metadata": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.1.4.tgz", + "integrity": "sha512-5J/GIH0yWt/56R3dNaNWPGQ/zXsZOddYECfJaqxFWgrZ9HC2Kvc5vl9upOgUUHKzURjAVf2N+f6tEJiojqXUuA==", + "requires": { + "gaxios": "^3.0.0", + "json-bigint": "^1.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.2.tgz", + "integrity": "sha512-tbjzndQvSIHGBLzHnhDs3cL4RBjLbLXc2pYvGH+imGVu5b4RMAttUTdnmW2UH0t11QeBTXZ7wlXPS7hrypO/tg==", + "requires": { + "node-forge": "^0.9.0" + } + }, + "gtoken": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.0.3.tgz", + "integrity": "sha512-Nyd1wZCMRc2dj/mAD0LlfQLcAO06uKdpKJXvK85SGrF5+5+Bpfil9u/2aw35ltvEHjvl0h5FMKN5knEU+9JrOg==", + "requires": { + "gaxios": "^3.0.0", + "google-p12-pem": "^3.0.0", + "jws": "^4.0.0", + "mime": "^2.2.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, "google-gax": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.1.tgz", - "integrity": "sha512-1T1PwSZWnbdRusA+NCZMSe56iU6swGvuZuy54eYl9vEHiRXTLYbQmUkWY2CqgYD9Fd/T4WBkUl22+rZG80unyw==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.15.3.tgz", + "integrity": "sha512-3JKJCRumNm3x2EksUTw4P1Rad43FTpqrtW9jzpf3xSMYXx+ogaqTM1vGo7VixHB4xkAyATXVIa3OcNSh8H9zsQ==", "requires": { - "@grpc/grpc-js": "^0.6.18", + "@grpc/grpc-js": "~1.0.3", "@grpc/proto-loader": "^0.5.1", "@types/fs-extra": "^8.0.1", "@types/long": "^4.0.0", @@ -3263,6 +3370,22 @@ "walkdir": "^0.4.0" }, "dependencies": { + "google-auth-library": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -3301,9 +3424,9 @@ }, "dependencies": { "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" } } }, @@ -4044,12 +4167,12 @@ "lodash.at": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", - "integrity": "sha512-GOTh0SEp+Yosnlpjic+8cl2WM9MykorogkGA9xyIFkkObQ3H3kNZqZ+ohuq4K3FrSVo7hMcZBMataJemrxC3BA==" + "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=" }, "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, "lodash.defaults": { "version": "4.2.0", @@ -4075,7 +4198,7 @@ "lodash.has": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", - "integrity": "sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g==" + "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=" }, "lodash.memoize": { "version": "4.1.2", @@ -4115,79 +4238,33 @@ } }, "logger-sharelatex": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-1.9.0.tgz", - "integrity": "sha512-yVTuha82047IiMOQLgQHCZGKkJo6I2+2KtiFKpgkIooR2yZaoTEvAeoMwBesSDSpGUpvUJ/+9UI+PmRyc+PQKQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logger-sharelatex/-/logger-sharelatex-2.2.0.tgz", + "integrity": "sha512-ko+OmE25XHJJCiz1R9EgwlfM7J/5olpunUfR3WcfuqOQrcUqsdBrDA2sOytngT0ViwjCR0Fh4qZVPwEWfmrvwA==", "requires": { - "@google-cloud/logging-bunyan": "^2.0.0", - "@overleaf/o-error": "^2.0.0", - "bunyan": "1.8.12", - "raven": "1.1.3", - "request": "2.88.0", - "yn": "^3.1.1" + "@google-cloud/logging-bunyan": "^3.0.0", + "@overleaf/o-error": "^3.0.0", + "bunyan": "^1.8.14", + "node-fetch": "^2.6.0", + "raven": "^2.6.4", + "yn": "^4.0.0" }, "dependencies": { "bunyan": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", - "integrity": "sha512-dmDUbGHeGcvCDLRFOscZkwx1ZO/aFz3bJOCi5nCgzdhFGPxwK+y5AcDBnqagNGlJZ7lje/l6JUEz9mQcutttdg==", + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz", + "integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==", "requires": { "dtrace-provider": "~0.8", - "moment": "^2.10.6", + "moment": "^2.19.3", "mv": "~2", "safe-json-stringify": "~1" } }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "yn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz", + "integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg==" } } }, @@ -4271,11 +4348,6 @@ "yallist": "^3.0.2" } }, - "lsmod": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", - "integrity": "sha512-Y+6V75r+mGWzWEPr9h6PFmStielICu5JBHLUg18jCsD2VFmEfgHbq/EgnY4inElsUD9eKL9id1qp34w46rSIKQ==" - }, "lynx": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", @@ -4317,6 +4389,23 @@ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==" }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6015,26 +6104,31 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raven": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.1.3.tgz", - "integrity": "sha512-RYov4wAaflZasWiCrZuizd3jNXxCOkW1WrXgWsGVb8kRpdHNZ+vPY27R6RhVtqzWp+DG9a5l6iP0QUPK4EgzaQ==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/raven/-/raven-2.6.4.tgz", + "integrity": "sha512-6PQdfC4+DQSFncowthLf+B6Hr0JpPsFBgTVYTAOq7tCmx/kR4SXbeawtPch20+3QfUcQDoJBLjWW1ybvZ4kXTw==", "requires": { "cookie": "0.3.1", - "json-stringify-safe": "5.0.1", - "lsmod": "1.0.0", - "stack-trace": "0.0.9", - "uuid": "3.0.0" + "md5": "^2.2.1", + "stack-trace": "0.0.10", + "timed-out": "4.0.1", + "uuid": "3.3.2" }, "dependencies": { "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==" + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "uuid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", - "integrity": "sha512-rqE1LoOVLv3QrZMjb4NkF5UWlkurCfPyItVnFPNKDDGkHw4dQUdE4zMcLqx28+0Kcf3+bnUk4PisaiRJT4aiaQ==" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" } } }, @@ -6616,9 +6710,9 @@ } }, "snakecase-keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.1.2.tgz", - "integrity": "sha512-NrzHj8ctStnd1LYx3+L4buS7yildFum7WAbQQxkhPCNi3Qeqv7hoBne2c9n++HWxDG9Nv23pNEyyLCITZTv24Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.0.tgz", + "integrity": "sha512-WTJ0NhCH/37J+PU3fuz0x5b6TvtWQChTcKPOndWoUy0pteKOe0hrHMzSRsJOWSIP48EQkzUEsgQPmrG3W8pFNQ==", "requires": { "map-obj": "^4.0.0", "to-snake-case": "^1.0.0" @@ -6770,7 +6864,8 @@ "stack-trace": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==" + "integrity": "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ==", + "dev": true }, "standard-as-callback": { "version": "2.0.1", @@ -6893,7 +6988,7 @@ "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" }, "supports-color": { "version": "6.0.0", @@ -6986,9 +7081,9 @@ }, "dependencies": { "uuid": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", - "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" } } }, @@ -7127,6 +7222,11 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, "timekeeper": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.2.0.tgz", @@ -7155,7 +7255,7 @@ "to-no-case": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==" + "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" }, "to-regex-range": { "version": "5.0.1", @@ -7169,7 +7269,7 @@ "to-snake-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", - "integrity": "sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==", + "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=", "requires": { "to-space-case": "^1.0.0" } @@ -7177,7 +7277,7 @@ "to-space-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", + "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", "requires": { "to-no-case": "^1.0.0" } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 28f8d26406..e67d309a35 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -27,7 +27,7 @@ "express": "4.17.1", "heap": "^0.2.6", "line-reader": "^0.4.0", - "logger-sharelatex": "^1.9.0", + "logger-sharelatex": "^2.2.0", "metrics-sharelatex": "^2.6.2", "mongo-uri": "^0.1.2", "mongojs": "3.1.0", From e74af0bee6127fd660ecc79136b633d9320476e6 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 10 Sep 2020 11:10:59 +0100 Subject: [PATCH 514/549] [misc] PackWorker: drop workaround for db.close in mongojs@1 REF: 3ed3932ee837920e95661b0308dee012f40a71dd --- services/track-changes/app/js/PackWorker.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index e6a02fef3d..c0784f097f 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -78,18 +78,6 @@ logger.log( `checking for updates, limit=${LIMIT}, delay=${DOCUMENT_PACK_DELAY}, timeout=${TIMEOUT}` ) -// work around for https://github.com/mafintosh/mongojs/issues/224 -db.close = function (callback) { - return this._getServer(function (err, server) { - if (err != null) { - return callback(err) - } - server = server.destroy != null ? server : server.topology - server.destroy(true, true) - return callback() - }) -} - const finish = function () { if (shutDownTimer != null) { logger.log('cancelling timeout') From b86acf3ea1429a4c22f71b69ed792cabeb5b4203 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 10 Sep 2020 14:18:14 +0100 Subject: [PATCH 515/549] [misc] mongodb: change findAndModify queries to updateOne There is no need to fetch mongo data when the call-site does not look at it. Also handle mongo errors in `setTTLOnArchivedPack`. --- services/track-changes/app/js/PackManager.js | 67 ++++++++----------- .../unit/js/PackManager/PackManagerTests.js | 19 +++--- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 5d753aebb2..14df91d074 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -273,10 +273,7 @@ module.exports = PackManager = { 'appending updates to existing pack' ) Metrics.inc(`append-pack-${temporary ? 'temporary' : 'permanent'}`) - return db.docHistory.findAndModify( - { query, update, new: true, fields: { meta: 1, v_end: 1 } }, - callback - ) + return db.docHistory.updateOne(query, update, callback) }, // Retrieve all changes for a document @@ -497,11 +494,9 @@ module.exports = PackManager = { increaseTTL(pack, callback) { if (pack.expiresAt < new Date(Date.now() + 6 * DAYS)) { // update cache expiry since we are using this pack - return db.docHistory.findAndModify( - { - query: { _id: pack._id }, - update: { $set: { expiresAt: new Date(Date.now() + 7 * DAYS) } } - }, + return db.docHistory.updateOne( + { _id: pack._id }, + { $set: { expiresAt: new Date(Date.now() + 7 * DAYS) } }, (err) => callback(err, pack) ) } else { @@ -727,15 +722,15 @@ module.exports = PackManager = { }, _insertPacksIntoIndex(project_id, doc_id, newPacks, callback) { - return db.docHistoryIndex.findAndModify( + return db.docHistoryIndex.updateOne( + { _id: ObjectId(doc_id.toString()) }, + { + $setOnInsert: { project_id: ObjectId(project_id.toString()) }, + $push: { + packs: { $each: newPacks, $sort: { v: 1 } } + } + }, { - query: { _id: ObjectId(doc_id.toString()) }, - update: { - $setOnInsert: { project_id: ObjectId(project_id.toString()) }, - $push: { - packs: { $each: newPacks, $sort: { v: 1 } } - } - }, upsert: true }, callback @@ -993,11 +988,9 @@ module.exports = PackManager = { _markPackAsFinalised(project_id, doc_id, pack_id, callback) { logger.log({ project_id, doc_id, pack_id }, 'marking pack as finalised') - return db.docHistory.findAndModify( - { - query: { _id: pack_id }, - update: { $set: { finalised: true } } - }, + return db.docHistory.updateOne( + { _id: pack_id }, + { $set: { finalised: true } }, callback ) }, @@ -1018,11 +1011,9 @@ module.exports = PackManager = { markPackAsChecked(project_id, doc_id, pack_id, callback) { logger.log({ project_id, doc_id, pack_id }, 'marking pack as checked') - return db.docHistory.findAndModify( - { - query: { _id: pack_id }, - update: { $currentDate: { last_checked: true } } - }, + return db.docHistory.updateOne( + { _id: pack_id }, + { $currentDate: { last_checked: true } }, callback ) }, @@ -1115,15 +1106,12 @@ module.exports = PackManager = { { project_id, doc_id, pack_id }, 'clearing as archive in progress' ) - return db.docHistoryIndex.findAndModify( + return db.docHistoryIndex.updateOne( { - query: { - _id: ObjectId(doc_id.toString()), - packs: { $elemMatch: { _id: pack_id, inS3: false } } - }, - fields: { 'packs.$': 1 }, - update: { $unset: { 'packs.$.inS3': true } } + _id: ObjectId(doc_id.toString()), + packs: { $elemMatch: { _id: pack_id, inS3: false } } }, + { $unset: { 'packs.$.inS3': true } }, callback ) }, @@ -1153,12 +1141,13 @@ module.exports = PackManager = { }, setTTLOnArchivedPack(project_id, doc_id, pack_id, callback) { - return db.docHistory.findAndModify( - { - query: { _id: pack_id }, - update: { $set: { expiresAt: new Date(Date.now() + 1 * DAYS) } } - }, + return db.docHistory.updateOne( + { _id: pack_id }, + { $set: { expiresAt: new Date(Date.now() + 1 * DAYS) } }, function (err) { + if (err) { + return callback(err) + } logger.log({ project_id, doc_id, pack_id }, 'set expiry on pack') return callback() } diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index 6a5708fd9f..a34ccbfa07 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -68,6 +68,7 @@ describe('PackManager', function () { return (this.db.docHistory = { save: sinon.stub().callsArg(1), insert: sinon.stub().callsArg(1), + updateOne: sinon.stub().yields(), findAndModify: sinon.stub().callsArg(1) }) }) @@ -443,23 +444,21 @@ describe('PackManager', function () { return describe('for a small update that will expire', function () { it('should append the update in mongo', function () { - return this.db.docHistory.findAndModify - .calledWithMatch({ - query: { _id: this.lastUpdate._id }, - update: { + return this.db.docHistory.updateOne + .calledWithMatch( + { _id: this.lastUpdate._id }, + { $push: { pack: { $each: this.newUpdates } }, $set: { v_end: this.newUpdates[this.newUpdates.length - 1].v } } - }) + ) .should.equal(true) }) it('should set an expiry time in the future', function () { - return this.db.docHistory.findAndModify - .calledWithMatch({ - update: { - $set: { expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) } - } + return this.db.docHistory.updateOne + .calledWithMatch(sinon.match.any, { + $set: { expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) } }) .should.equal(true) }) From 6d3fa2ee5b347a2867d9416a64bfc7cc9f72aa26 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 10 Sep 2020 14:54:18 +0100 Subject: [PATCH 516/549] [misc] mongodb: use findOne and find().toArray() --- services/track-changes/app/js/MongoManager.js | 9 +--- services/track-changes/app/js/PackManager.js | 41 ++++++++----------- services/track-changes/app/js/PackWorker.js | 3 +- .../unit/js/MongoManager/MongoManagerTests.js | 4 +- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/services/track-changes/app/js/MongoManager.js b/services/track-changes/app/js/MongoManager.js index 6a84fe7bc1..94ae462722 100644 --- a/services/track-changes/app/js/MongoManager.js +++ b/services/track-changes/app/js/MongoManager.js @@ -115,16 +115,11 @@ module.exports = MongoManager = { if (callback == null) { callback = function (error, metadata) {} } - return db.projectHistoryMetaData.find( + return db.projectHistoryMetaData.findOne( { project_id: ObjectId(project_id.toString()) }, - function (error, results) { - if (error != null) { - return callback(error) - } - return callback(null, results[0]) - } + callback ) }, diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 5d753aebb2..418d20283e 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -301,7 +301,8 @@ module.exports = PackManager = { // console.log "query:", query return db.docHistory .find(query) - .sort({ v: -1 }, function (err, result) { + .sort({ v: -1 }) + .toArray(function (err, result) { if (err != null) { return callback(err) } @@ -380,20 +381,9 @@ module.exports = PackManager = { fetchPacksIfNeeded(project_id, doc_id, pack_ids, callback) { let id - return db.docHistory.find( - { - _id: { - $in: (() => { - const result = [] - for (id of Array.from(pack_ids)) { - result.push(ObjectId(id)) - } - return result - })() - } - }, - { _id: 1 }, - function (err, loadedPacks) { + return db.docHistory + .find({ _id: { $in: pack_ids.map(ObjectId) } }, { _id: 1 }) + .toArray(function (err, loadedPacks) { if (err != null) { return callback(err) } @@ -428,8 +418,7 @@ module.exports = PackManager = { return callback() } ) - } - ) + }) }, // Retrieve all changes across a project @@ -438,7 +427,8 @@ module.exports = PackManager = { // get all the docHistory Entries return db.docHistory .find({ project_id: ObjectId(project_id) }, { pack: false }) - .sort({ 'meta.end_ts': -1 }, function (err, packs) { + .sort({ 'meta.end_ts': -1 }) + .toArray(function (err, packs) { let pack if (err != null) { return callback(err) @@ -449,9 +439,9 @@ module.exports = PackManager = { allPacks.push(pack) seenIds[pack._id] = true } - return db.docHistoryIndex.find( - { project_id: ObjectId(project_id) }, - function (err, indexes) { + return db.docHistoryIndex + .find({ project_id: ObjectId(project_id) }) + .toArray(function (err, indexes) { if (err != null) { return callback(err) } @@ -470,8 +460,7 @@ module.exports = PackManager = { null, new ProjectIterator(allPacks, before, PackManager.getPackById) ) - } - ) + }) }) }, @@ -617,7 +606,8 @@ module.exports = PackManager = { } return db.docHistory .find(query, { pack: false }) - .sort({ v: 1 }, function (err, packs) { + .sort({ v: 1 }) + .toArray(function (err, packs) { if (err != null) { return callback(err) } @@ -642,7 +632,8 @@ module.exports = PackManager = { } return db.docHistory .find(query, { pack: false }) - .sort({ v: 1 }, function (err, packs) { + .sort({ v: 1 }) + .toArray(function (err, packs) { if (err != null) { return callback(err) } diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index e6a02fef3d..59df90cf29 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -191,7 +191,8 @@ if (pending != null) { .sort({ last_checked: 1 }) - .limit(LIMIT, function (err, results) { + .limit(LIMIT) + .toArray(function (err, results) { if (err != null) { logger.log({ err }, 'error checking for updates') finish() diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index dc044cf288..aeed0b0fca 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -190,7 +190,7 @@ describe('MongoManager', function () { beforeEach(function () { this.metadata = { mock: 'metadata' } this.db.projectHistoryMetaData = { - find: sinon.stub().callsArgWith(1, null, [this.metadata]) + findOne: sinon.stub().callsArgWith(1, null, this.metadata) } return this.MongoManager.getProjectMetaData( this.project_id, @@ -199,7 +199,7 @@ describe('MongoManager', function () { }) it('should look up the meta data in the db', function () { - return this.db.projectHistoryMetaData.find + return this.db.projectHistoryMetaData.findOne .calledWith({ project_id: ObjectId(this.project_id) }) .should.equal(true) }) From c42561c38a97db4d88dddd045eef2016ff9a7aaa Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 10 Sep 2020 14:23:04 +0100 Subject: [PATCH 517/549] [misc] update the bson package --- services/track-changes/app/js/PackManager.js | 4 +++- services/track-changes/app/js/PackWorker.js | 2 +- services/track-changes/app/js/mongojs.js | 4 +--- services/track-changes/package-lock.json | 13 +++---------- services/track-changes/package.json | 2 +- .../test/unit/js/PackManager/PackManagerTests.js | 5 ++--- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 5d753aebb2..fcdf48dbf7 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -16,7 +16,9 @@ let PackManager const async = require('async') const _ = require('underscore') -const { db, ObjectId, BSON } = require('./mongojs') +const Bson = require('bson') +const BSON = new Bson() +const { db, ObjectId } = require('./mongojs') const logger = require('logger-sharelatex') const LockManager = require('./LockManager') const MongoAWS = require('./MongoAWS') diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index e6a02fef3d..a0baa3e0df 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -18,7 +18,7 @@ let project_id, doc_id const Settings = require('settings-sharelatex') const async = require('async') const _ = require('underscore') -const { db, ObjectId, BSON } = require('./mongojs') +const { db, ObjectId } = require('./mongojs') const fs = require('fs') const Metrics = require('metrics-sharelatex') Metrics.initialize('track-changes') diff --git a/services/track-changes/app/js/mongojs.js b/services/track-changes/app/js/mongojs.js index 6b99f87a4c..718d873873 100644 --- a/services/track-changes/app/js/mongojs.js +++ b/services/track-changes/app/js/mongojs.js @@ -2,7 +2,6 @@ // Sanity-check the conversion and remove this comment. const Settings = require('settings-sharelatex') const mongojs = require('mongojs') -const bson = require('bson') const db = mongojs(Settings.mongo.url, [ 'docHistory', 'projectHistoryMetaData', @@ -10,6 +9,5 @@ const db = mongojs(Settings.mongo.url, [ ]) module.exports = { db, - ObjectId: mongojs.ObjectId, - BSON: new bson.BSONPure() + ObjectId: mongojs.ObjectId } diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index a3545e5560..3560f12032 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1572,9 +1572,9 @@ "dev": true }, "bson": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz", - "integrity": "sha512-xMUimhLm6y4t9BTW6BQGRHs9PODB9082EUX/Gkx6M9T2ktuJ5LvMxY/20ukuk0Uc+WPL37pbMIy731XF7eTxjg==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" }, "buffer": { "version": "4.9.1", @@ -4727,13 +4727,6 @@ "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" - }, - "dependencies": { - "bson": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", - "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" - } } }, "mongojs": { diff --git a/services/track-changes/package.json b/services/track-changes/package.json index e67d309a35..f1e4146cbe 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -22,7 +22,7 @@ "async": "^2.6.3", "aws-sdk": "^2.643.0", "body-parser": "^1.19.0", - "bson": "^0.4.20", + "bson": "^1.1.5", "byline": "^5.0.0", "express": "4.17.1", "heap": "^0.2.6", diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index 6a5708fd9f..ce53e3e5b0 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -18,8 +18,6 @@ const { expect } = chai const modulePath = '../../../../app/js/PackManager.js' const SandboxedModule = require('sandboxed-module') const { ObjectId } = require('mongojs') -const bson = require('bson') -const BSON = new bson.BSONPure() const _ = require('underscore') const tk = require('timekeeper') @@ -29,7 +27,8 @@ describe('PackManager', function () { tk.freeze(new Date()) this.PackManager = SandboxedModule.require(modulePath, { requires: { - './mongojs': { db: (this.db = {}), ObjectId, BSON }, + bson: require('bson'), + './mongojs': { db: (this.db = {}), ObjectId }, './LockManager': {}, './MongoAWS': {}, 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, From c630cbb4e36a15aaf6251fd9fab968107b217bf3 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 10 Sep 2020 12:58:06 +0100 Subject: [PATCH 518/549] [misc] migrate the app to the native mongo driver acceptance tests to follow in a separate commit. --- services/track-changes/app.js | 33 +++++++----- .../track-changes/app/js/HealthChecker.js | 2 +- services/track-changes/app/js/MongoAWS.js | 8 ++- services/track-changes/app/js/MongoManager.js | 20 ++++---- services/track-changes/app/js/PackManager.js | 50 ++++++++++--------- services/track-changes/app/js/PackWorker.js | 4 +- services/track-changes/app/js/mongodb.js | 30 +++++++++++ .../track-changes/config/settings.defaults.js | 4 ++ services/track-changes/package-lock.json | 14 +++--- services/track-changes/package.json | 1 + .../acceptance/js/helpers/TrackChangesApp.js | 10 ++-- .../test/unit/js/DocArchive/MongoAWS.js | 10 ++-- .../unit/js/MongoManager/MongoManagerTests.js | 15 +++--- .../unit/js/PackManager/PackManagerTests.js | 18 +++---- .../js/UpdatesManager/UpdatesManagerTests.js | 2 +- 15 files changed, 135 insertions(+), 86 deletions(-) create mode 100644 services/track-changes/app/js/mongodb.js diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 3ce4c30b40..3794e8f655 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -44,6 +44,7 @@ Metrics.memory.monitor(logger) const childProcess = require('child_process') +const mongodb = require('./app/js/mongodb') const HttpController = require('./app/js/HttpController') const express = require('express') const bodyParser = require('body-parser') @@ -128,18 +129,26 @@ const host = if (!module.parent) { // Called directly - app.listen(port, host, function (error) { - if (error != null) { - return logger.error( - { err: error }, - 'could not start track-changes server' - ) - } else { - return logger.info( - `trackchanges starting up, listening on ${host}:${port}` - ) - } - }) + mongodb + .waitForDb() + .then(() => { + app.listen(port, host, function (error) { + if (error != null) { + return logger.error( + { err: error }, + 'could not start track-changes server' + ) + } else { + return logger.info( + `trackchanges starting up, listening on ${host}:${port}` + ) + } + }) + }) + .catch((err) => { + logger.fatal({ err }, 'Cannot connect to mongo. Exiting.') + process.exit(1) + }) } module.exports = app diff --git a/services/track-changes/app/js/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js index a4331b4eac..afc7ff7fc8 100644 --- a/services/track-changes/app/js/HealthChecker.js +++ b/services/track-changes/app/js/HealthChecker.js @@ -10,7 +10,7 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const { ObjectId } = require('mongojs') +const { ObjectId } = require('./mongodb') const request = require('request') const async = require('async') const settings = require('settings-sharelatex') diff --git a/services/track-changes/app/js/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js index bcf2a49715..713f76c9f1 100644 --- a/services/track-changes/app/js/MongoAWS.js +++ b/services/track-changes/app/js/MongoAWS.js @@ -18,7 +18,7 @@ const settings = require('settings-sharelatex') const logger = require('logger-sharelatex') const AWS = require('aws-sdk') const S3S = require('s3-streams') -const { db, ObjectId } = require('./mongojs') +const { db, ObjectId } = require('./mongodb') const JSONStream = require('JSONStream') const ReadlineStream = require('byline') const zlib = require('zlib') @@ -187,7 +187,11 @@ module.exports = MongoAWS = { // allow the object to expire, we can always retrieve it again object.expiresAt = new Date(Date.now() + 7 * DAYS) logger.log({ project_id, doc_id, pack_id }, 'inserting object from s3') - return db.docHistory.insert(object, callback) + return db.docHistory.insertOne(object, (err, confirmation) => { + if (err) return callback(err) + object._id = confirmation.insertedId + callback(null, object) + }) }) } } diff --git a/services/track-changes/app/js/MongoManager.js b/services/track-changes/app/js/MongoManager.js index 94ae462722..72a87bdd0a 100644 --- a/services/track-changes/app/js/MongoManager.js +++ b/services/track-changes/app/js/MongoManager.js @@ -12,7 +12,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let MongoManager -const { db, ObjectId } = require('./mongojs') +const { db, ObjectId } = require('./mongodb') const PackManager = require('./PackManager') const async = require('async') const _ = require('underscore') @@ -25,7 +25,11 @@ module.exports = MongoManager = { callback = function (error, update) {} } return db.docHistory - .find({ doc_id: ObjectId(doc_id.toString()) }, { pack: { $slice: -1 } }) // only return the last entry in a pack + .find( + { doc_id: ObjectId(doc_id.toString()) }, + // only return the last entry in a pack + { projection: { pack: { $slice: -1 } } } + ) .sort({ v: -1 }) .limit(1) .toArray(function (error, compressedUpdates) { @@ -96,7 +100,7 @@ module.exports = MongoManager = { if (callback == null) { callback = function (error) {} } - return db.docHistory.update( + return db.docHistory.updateMany( { doc_id: ObjectId(doc_id.toString()), project_id: { $exists: false } @@ -104,9 +108,6 @@ module.exports = MongoManager = { { $set: { project_id: ObjectId(project_id.toString()) } }, - { - multi: true - }, callback ) }, @@ -127,7 +128,7 @@ module.exports = MongoManager = { if (callback == null) { callback = function (error) {} } - return db.projectHistoryMetaData.update( + return db.projectHistoryMetaData.updateOne( { project_id: ObjectId(project_id) }, @@ -146,7 +147,7 @@ module.exports = MongoManager = { if (callback == null) { callback = function (error) {} } - return db.docHistory.update( + return db.docHistory.updateMany( { project_id: ObjectId(project_id), temporary: true, @@ -156,9 +157,6 @@ module.exports = MongoManager = { $set: { temporary: false }, $unset: { expiresAt: '' } }, - { - multi: true - }, callback ) }, diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index df7742f819..fc7a483729 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -18,7 +18,7 @@ const async = require('async') const _ = require('underscore') const Bson = require('bson') const BSON = new Bson() -const { db, ObjectId } = require('./mongojs') +const { db, ObjectId } = require('./mongodb') const logger = require('logger-sharelatex') const LockManager = require('./LockManager') const MongoAWS = require('./MongoAWS') @@ -220,7 +220,7 @@ module.exports = PackManager = { { project_id, doc_id, newUpdates }, 'inserting updates into new pack' ) - return db.docHistory.save(newPack, function (err, result) { + return db.docHistory.insertOne(newPack, function (err) { if (err != null) { return callback(err) } @@ -381,7 +381,10 @@ module.exports = PackManager = { fetchPacksIfNeeded(project_id, doc_id, pack_ids, callback) { let id return db.docHistory - .find({ _id: { $in: pack_ids.map(ObjectId) } }, { _id: 1 }) + .find( + { _id: { $in: pack_ids.map(ObjectId) } }, + { projection: { _id: 1 } } + ) .toArray(function (err, loadedPacks) { if (err != null) { return callback(err) @@ -425,7 +428,10 @@ module.exports = PackManager = { makeProjectIterator(project_id, before, callback) { // get all the docHistory Entries return db.docHistory - .find({ project_id: ObjectId(project_id) }, { pack: false }) + .find( + { project_id: ObjectId(project_id) }, + { projection: { pack: false } } + ) .sort({ 'meta.end_ts': -1 }) .toArray(function (err, packs) { let pack @@ -507,7 +513,7 @@ module.exports = PackManager = { getPackFromIndex(doc_id, pack_id, callback) { return db.docHistoryIndex.findOne( { _id: ObjectId(doc_id.toString()), 'packs._id': pack_id }, - { 'packs.$': 1 }, + { projection: { 'packs.$': 1 } }, callback ) }, @@ -515,7 +521,7 @@ module.exports = PackManager = { getLastPackFromIndex(doc_id, callback) { return db.docHistoryIndex.findOne( { _id: ObjectId(doc_id.toString()) }, - { packs: { $slice: -1 } }, + { projection: { packs: { $slice: -1 } } }, function (err, indexPack) { if (err != null) { return callback(err) @@ -602,7 +608,7 @@ module.exports = PackManager = { expiresAt: { $exists: false } } return db.docHistory - .find(query, { pack: false }) + .find(query, { projection: { pack: false } }) .sort({ v: 1 }) .toArray(function (err, packs) { if (err != null) { @@ -628,7 +634,7 @@ module.exports = PackManager = { expiresAt: { $exists: false } } return db.docHistory - .find(query, { pack: false }) + .find(query, { projection: { pack: false } }) .sort({ v: 1 }) .toArray(function (err, packs) { if (err != null) { @@ -1069,20 +1075,18 @@ module.exports = PackManager = { { project_id, doc_id }, 'marking pack as archive in progress status' ) - return db.docHistoryIndex.findAndModify( + return db.docHistoryIndex.findOneAndUpdate( { - query: { - _id: ObjectId(doc_id.toString()), - packs: { $elemMatch: { _id: pack_id, inS3: { $exists: false } } } - }, - fields: { 'packs.$': 1 }, - update: { $set: { 'packs.$.inS3': false } } + _id: ObjectId(doc_id.toString()), + packs: { $elemMatch: { _id: pack_id, inS3: { $exists: false } } } }, + { $set: { 'packs.$.inS3': false } }, + { projection: { 'packs.$': 1 } }, function (err, result) { if (err != null) { return callback(err) } - if (result == null) { + if (!result.value) { return callback(new Error('archive is already in progress')) } logger.log( @@ -1111,20 +1115,18 @@ module.exports = PackManager = { markPackAsArchived(project_id, doc_id, pack_id, callback) { logger.log({ project_id, doc_id, pack_id }, 'marking pack as archived') - return db.docHistoryIndex.findAndModify( + return db.docHistoryIndex.findOneAndUpdate( { - query: { - _id: ObjectId(doc_id.toString()), - packs: { $elemMatch: { _id: pack_id, inS3: false } } - }, - fields: { 'packs.$': 1 }, - update: { $set: { 'packs.$.inS3': true } } + _id: ObjectId(doc_id.toString()), + packs: { $elemMatch: { _id: pack_id, inS3: false } } }, + { $set: { 'packs.$.inS3': true } }, + { projection: { 'packs.$': 1 } }, function (err, result) { if (err != null) { return callback(err) } - if (result == null) { + if (!result.value) { return callback(new Error('archive is not marked as progress')) } logger.log({ project_id, doc_id, pack_id }, 'marked as archived') diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index d826df0a2b..74b5368368 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -18,7 +18,7 @@ let project_id, doc_id const Settings = require('settings-sharelatex') const async = require('async') const _ = require('underscore') -const { db, ObjectId } = require('./mongojs') +const { db, ObjectId } = require('./mongodb') const fs = require('fs') const Metrics = require('metrics-sharelatex') Metrics.initialize('track-changes') @@ -174,7 +174,7 @@ if (pending != null) { _id: { $lt: ObjectIdFromDate(oneWeekAgo) }, last_checked: { $lt: oneWeekAgo } }, - { _id: 1, doc_id: 1, project_id: 1 } + { projection: { _id: 1, doc_id: 1, project_id: 1 } } ) .sort({ last_checked: 1 diff --git a/services/track-changes/app/js/mongodb.js b/services/track-changes/app/js/mongodb.js new file mode 100644 index 0000000000..38ebe0462a --- /dev/null +++ b/services/track-changes/app/js/mongodb.js @@ -0,0 +1,30 @@ +const Settings = require('settings-sharelatex') +const { MongoClient, ObjectId } = require('mongodb') + +const clientPromise = MongoClient.connect( + Settings.mongo.url, + Settings.mongo.options +) + +let setupDbPromise +async function waitForDb() { + if (!setupDbPromise) { + setupDbPromise = setupDb() + } + await setupDbPromise +} + +const db = {} +async function setupDb() { + const internalDb = (await clientPromise).db() + + db.docHistory = internalDb.collection('docHistory') + db.docHistoryIndex = internalDb.collection('docHistoryIndex') + db.projectHistoryMetaData = internalDb.collection('projectHistoryMetaData') +} + +module.exports = { + db, + ObjectId, + waitForDb +} diff --git a/services/track-changes/config/settings.defaults.js b/services/track-changes/config/settings.defaults.js index 426592ed26..38ecce723a 100755 --- a/services/track-changes/config/settings.defaults.js +++ b/services/track-changes/config/settings.defaults.js @@ -4,6 +4,10 @@ const TMP_DIR = module.exports = { mongo: { + options: { + useUnifiedTopology: + (process.env.MONGO_USE_UNIFIED_TOPOLOGY || 'true') === 'true' + }, url: process.env.MONGO_CONNECTION_STRING || `mongodb://${process.env.MONGO_HOST || 'localhost'}/sharelatex` diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 3560f12032..600951b57b 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1496,9 +1496,9 @@ "integrity": "sha512-tbaUB1QpTIj4cKY8c1rvNAvEQXA+ekzHmbe4jzNfW3QWsF9GnnP/BRWyl6/qqS53heoYJ93naaFcm/jooONH8g==" }, "bl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", - "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -4717,12 +4717,12 @@ "integrity": "sha512-FehPVi2Dv7VPvAkLnN9haM1aarj1E9w08rkn2MAbbQJF5EbcOckdOHRAD9T35yUkfLVcs0YzYluNX4/+G8HaIw==" }, "mongodb": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.5.tgz", - "integrity": "sha512-GCjDxR3UOltDq00Zcpzql6dQo1sVry60OXJY3TDmFc2SWFY6c8Gn1Ardidc5jDirvJrx2GC3knGOImKphbSL3A==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.1.tgz", + "integrity": "sha512-uH76Zzr5wPptnjEKJRQnwTsomtFOU/kQEU8a9hKHr2M7y9qVk7Q4Pkv0EQVp88742z9+RwvsdTw6dRjDZCNu1g==", "requires": { "bl": "^2.2.0", - "bson": "^1.1.1", + "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index f1e4146cbe..b14882656f 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -30,6 +30,7 @@ "logger-sharelatex": "^2.2.0", "metrics-sharelatex": "^2.6.2", "mongo-uri": "^0.1.2", + "mongodb": "^3.6.0", "mongojs": "3.1.0", "redis": "~0.10.1", "redis-sharelatex": "^1.0.13", diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js index 81efb5c912..6ef1127f06 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js @@ -13,6 +13,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const app = require('../../../../app') +const { waitForDb } = require('../../../../app/js/mongodb') require('logger-sharelatex') const logger = require('logger-sharelatex') const Settings = require('settings-sharelatex') @@ -29,9 +30,10 @@ module.exports = { return callback() } else if (this.initing) { return this.callbacks.push(callback) - } else { - this.initing = true - this.callbacks.push(callback) + } + this.initing = true + this.callbacks.push(callback) + waitForDb().then(() => { return app.listen( __guard__( Settings.internal != null @@ -56,7 +58,7 @@ module.exports = { })() } ) - } + }) } } function __guard__(value, transform) { diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index 3eec0c1bbe..6bd9b903a2 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -14,7 +14,7 @@ chai.should() const sinon = require('sinon') const modulePath = '../../../../app/js/MongoAWS.js' const SandboxedModule = require('sandboxed-module') -const { ObjectId } = require('mongojs') +const { ObjectId } = require('mongodb') const MemoryStream = require('memorystream') const zlib = require('zlib') @@ -44,7 +44,7 @@ describe('MongoAWS', function () { 'aws-sdk': (this.awssdk = {}), fs: (this.fs = {}), 's3-streams': (this.S3S = {}), - './mongojs': { db: (this.db = {}), ObjectId }, + './mongodb': { db: (this.db = {}), ObjectId }, JSONStream: (this.JSONStream = {}), 'readline-stream': (this.readline = sinon.stub()), 'metrics-sharelatex': { inc() {} } @@ -92,7 +92,9 @@ describe('MongoAWS', function () { this.S3S.ReadStream = () => MemoryStream.createReadStream(zbuf, { readable: true }) this.db.docHistory = {} - this.db.docHistory.insert = sinon.stub().callsArgWith(1, null, 'pack') + this.db.docHistory.insertOne = sinon + .stub() + .yields(null, { insertedId: ObjectId() }) return this.MongoAWS.unArchivePack( this.project_id, @@ -107,7 +109,7 @@ describe('MongoAWS', function () { }) return it('should call db.docHistory.insert', function () { - return this.db.docHistory.insert.called.should.equal(true) + return this.db.docHistory.insertOne.called.should.equal(true) }) }) }) diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index aeed0b0fca..43a51aaf57 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -16,7 +16,7 @@ const { expect } = chai const modulePath = '../../../../app/js/MongoManager.js' const packModulePath = '../../../../app/js/PackManager.js' const SandboxedModule = require('sandboxed-module') -const { ObjectId } = require('mongojs') +const { ObjectId } = require('mongodb') const tk = require('timekeeper') describe('MongoManager', function () { @@ -24,7 +24,7 @@ describe('MongoManager', function () { tk.freeze(new Date()) this.MongoManager = SandboxedModule.require(modulePath, { requires: { - './mongojs': { db: (this.db = {}), ObjectId }, + './mongodb': { db: (this.db = {}), ObjectId }, './PackManager': (this.PackManager = {}), 'metrics-sharelatex': { timeAsyncMethod() {} }, 'logger-sharelatex': { log() {} } @@ -156,7 +156,7 @@ describe('MongoManager', function () { describe('backportProjectId', function () { beforeEach(function () { - this.db.docHistory = { update: sinon.stub().callsArg(3) } + this.db.docHistory = { updateMany: sinon.stub().yields() } return this.MongoManager.backportProjectId( this.project_id, this.doc_id, @@ -165,7 +165,7 @@ describe('MongoManager', function () { }) it("should insert the project_id into all entries for the doc_id which don't have it set", function () { - return this.db.docHistory.update + return this.db.docHistory.updateMany .calledWith( { doc_id: ObjectId(this.doc_id), @@ -173,9 +173,6 @@ describe('MongoManager', function () { }, { $set: { project_id: ObjectId(this.project_id) } - }, - { - multi: true } ) .should.equal(true) @@ -213,7 +210,7 @@ describe('MongoManager', function () { beforeEach(function () { this.metadata = { mock: 'metadata' } this.db.projectHistoryMetaData = { - update: sinon.stub().callsArgWith(3, null, [this.metadata]) + updateOne: sinon.stub().yields() } return this.MongoManager.setProjectMetaData( this.project_id, @@ -223,7 +220,7 @@ describe('MongoManager', function () { }) it('should upsert the metadata into the DB', function () { - return this.db.projectHistoryMetaData.update + return this.db.projectHistoryMetaData.updateOne .calledWith( { project_id: ObjectId(this.project_id) diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index b3e29c535d..e045f72716 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -17,7 +17,7 @@ const should = chai.should() const { expect } = chai const modulePath = '../../../../app/js/PackManager.js' const SandboxedModule = require('sandboxed-module') -const { ObjectId } = require('mongojs') +const { ObjectId } = require('mongodb') const _ = require('underscore') const tk = require('timekeeper') @@ -28,7 +28,7 @@ describe('PackManager', function () { this.PackManager = SandboxedModule.require(modulePath, { requires: { bson: require('bson'), - './mongojs': { db: (this.db = {}), ObjectId }, + './mongodb': { db: (this.db = {}), ObjectId }, './LockManager': {}, './MongoAWS': {}, 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, @@ -65,7 +65,7 @@ describe('PackManager', function () { { op: 'op-4', meta: 'meta-4', v: 4 } ] return (this.db.docHistory = { - save: sinon.stub().callsArg(1), + insertOne: sinon.stub().yields(), insert: sinon.stub().callsArg(1), updateOne: sinon.stub().yields(), findAndModify: sinon.stub().callsArg(1) @@ -390,7 +390,7 @@ describe('PackManager', function () { return describe('for a small update that will expire', function () { it('should insert the update into mongo', function () { - return this.db.docHistory.save + return this.db.docHistory.insertOne .calledWithMatch({ pack: this.newUpdates, project_id: ObjectId(this.project_id), @@ -403,7 +403,7 @@ describe('PackManager', function () { }) it('should set an expiry time in the future', function () { - return this.db.docHistory.save + return this.db.docHistory.insertOne .calledWithMatch({ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) }) @@ -496,7 +496,7 @@ describe('PackManager', function () { return describe('for a small update that will not expire', function () { it('should insert the update into mongo', function () { - return this.db.docHistory.save + return this.db.docHistory.insertOne .calledWithMatch({ pack: this.newUpdates, project_id: ObjectId(this.project_id), @@ -509,7 +509,7 @@ describe('PackManager', function () { }) it('should not set any expiry time', function () { - return this.db.docHistory.save + return this.db.docHistory.insertOne .neverCalledWithMatch(sinon.match.has('expiresAt')) .should.equal(true) }) @@ -546,7 +546,7 @@ describe('PackManager', function () { return describe('for a small update that will expire', function () { it('should insert the update into mongo', function () { - return this.db.docHistory.save + return this.db.docHistory.insertOne .calledWithMatch({ pack: this.newUpdates, project_id: ObjectId(this.project_id), @@ -559,7 +559,7 @@ describe('PackManager', function () { }) it('should set an expiry time in the future', function () { - return this.db.docHistory.save + return this.db.docHistory.insertOne .calledWithMatch({ expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) }) diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index cb2af00c16..ed7c4b064e 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -17,6 +17,7 @@ const sinon = require('sinon') const chai = require('chai') const should = chai.should() const { expect } = chai +const { ObjectId } = require('mongodb') const modulePath = '../../../../app/js/UpdatesManager.js' const SandboxedModule = require('sandboxed-module') @@ -864,7 +865,6 @@ describe('UpdatesManager', function () { describe('fillUserInfo', function () { describe('with valid users', function () { beforeEach(function (done) { - const { ObjectId } = require('mongojs') this.user_id_1 = ObjectId().toString() this.user_id_2 = ObjectId().toString() this.updates = [ From 9dddf59a9d888240df90a3b2c32e984fa16134d6 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 10 Sep 2020 16:48:09 +0100 Subject: [PATCH 519/549] [misc] migrate acceptance tests to the native mongo driver, drop mongojs --- services/track-changes/app/js/mongojs.js | 13 - services/track-changes/package-lock.json | 805 +++--------------- services/track-changes/package.json | 1 - .../acceptance/js/AppendingUpdatesTests.js | 3 +- .../acceptance/js/ArchivingUpdatesTests.js | 8 +- .../acceptance/js/FlushingUpdatesTests.js | 3 +- .../test/acceptance/js/GettingADiffTests.js | 4 +- .../test/acceptance/js/GettingUpdatesTests.js | 4 +- .../test/acceptance/js/LockManagerTests.js | 2 - .../test/acceptance/js/RestoringVersions.js | 4 +- .../js/helpers/TrackChangesClient.js | 8 +- 11 files changed, 123 insertions(+), 732 deletions(-) delete mode 100644 services/track-changes/app/js/mongojs.js diff --git a/services/track-changes/app/js/mongojs.js b/services/track-changes/app/js/mongojs.js deleted file mode 100644 index 718d873873..0000000000 --- a/services/track-changes/app/js/mongojs.js +++ /dev/null @@ -1,13 +0,0 @@ -// TODO: This file was created by bulk-decaffeinate. -// Sanity-check the conversion and remove this comment. -const Settings = require('settings-sharelatex') -const mongojs = require('mongojs') -const db = mongojs(Settings.mongo.url, [ - 'docHistory', - 'projectHistoryMetaData', - 'docHistoryIndex' -]) -module.exports = { - db, - ObjectId: mongojs.ObjectId -} diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 600951b57b..06294524b8 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -8,6 +8,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, "requires": { "@babel/highlight": "^7.8.3" } @@ -16,6 +17,7 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.0.tgz", "integrity": "sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw==", + "dev": true, "requires": { "@babel/types": "^7.9.0", "jsesc": "^2.5.1", @@ -26,7 +28,8 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true } } }, @@ -34,6 +37,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.8.3", "@babel/template": "^7.8.3", @@ -44,6 +48,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, "requires": { "@babel/types": "^7.8.3" } @@ -52,6 +57,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, "requires": { "@babel/types": "^7.8.3" } @@ -59,12 +65,14 @@ "@babel/helper-validator-identifier": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==" + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "dev": true }, "@babel/highlight": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -74,7 +82,8 @@ "@babel/parser": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg==" + "integrity": "sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg==", + "dev": true }, "@babel/runtime": { "version": "7.8.4", @@ -107,6 +116,7 @@ "version": "7.8.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, "requires": { "@babel/code-frame": "^7.8.3", "@babel/parser": "^7.8.6", @@ -117,6 +127,7 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", + "dev": true, "requires": { "@babel/code-frame": "^7.8.3", "@babel/generator": "^7.9.0", @@ -133,6 +144,7 @@ "version": "7.9.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", @@ -1251,6 +1263,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -1265,23 +1278,11 @@ "picomatch": "^2.0.4" } }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -1619,17 +1620,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1639,7 +1629,8 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "camelcase-keys": { "version": "6.1.2", @@ -1675,6 +1666,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1684,12 +1676,14 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -1758,6 +1752,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "requires": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -1767,17 +1762,20 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -1800,6 +1798,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -1807,7 +1806,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -1823,11 +1823,6 @@ "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", "dev": true }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1866,14 +1861,6 @@ "emitter-listener": "^1.1.1" } }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "requires": { - "safe-buffer": "~5.1.1" - } - }, "cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", @@ -1901,18 +1888,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - } - }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -1968,7 +1943,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true }, "deep-eql": { "version": "3.0.1", @@ -1985,14 +1961,6 @@ "integrity": "sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==", "dev": true }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==", - "requires": { - "strip-bom": "^3.0.0" - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2076,11 +2044,6 @@ "stream-shift": "^1.0.0" } }, - "each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/each-series/-/each-series-1.0.0.tgz", - "integrity": "sha512-4MQloCGGCmT5GJZK5ibgJSvTK1c1QSrNlDvLk6fEyRxjZnXjl+NNFfzhfXpmnWh33Owc9D9klrdzCUi7yc9r4Q==" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -2139,6 +2102,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -2173,11 +2137,6 @@ "is-symbol": "^1.0.2" } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -2199,7 +2158,8 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true }, "eslint": { "version": "6.8.0", @@ -2720,7 +2680,8 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "esquery": { "version": "1.1.0", @@ -2749,7 +2710,8 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true }, "etag": { "version": "1.8.1", @@ -2953,51 +2915,6 @@ } } }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "requires": { - "find-up": "^3.0.0" - } - } - } - }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -3095,40 +3012,6 @@ } } }, - "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha512-3TOY+4TKV0Ml83PXJQY+JFQaHNV38lzQDIzzXYg1kWdBLenGgoZhAs0CKgzI31vi2pWEpQMq/Yi4bpKwCPkw7g==", - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - } - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3157,7 +3040,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "fsevents": { "version": "2.1.2", @@ -3202,7 +3086,8 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-func-name": { "version": "2.0.0", @@ -3257,7 +3142,8 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true }, "google-auth-library": { "version": "6.0.6", @@ -3404,7 +3290,8 @@ "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true }, "growl": { "version": "1.10.5", @@ -3482,21 +3369,6 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, - "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha512-w0Kz8lJFBoyaurBiNrIvxPqr/gJ6fOfSkpAPOepN3oECqGJag37xPbOv57izi/KP8auHgNYxn5fXtAb+1LsJ6w==", - "requires": { - "is-stream": "^1.0.1" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" - } - } - }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3516,12 +3388,8 @@ "hosted-git-info": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" - }, - "html-escaper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", - "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==" + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true }, "http-errors": { "version": "1.7.2", @@ -3604,7 +3472,8 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true }, "indent-string": { "version": "4.0.0", @@ -3763,7 +3632,8 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "is-binary-path": { "version": "2.1.0", @@ -3876,123 +3746,14 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" - }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "requires": { - "html-escaper": "^2.0.0" - } - }, "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", @@ -4001,12 +3762,14 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -4020,7 +3783,8 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true }, "json-bigint": { "version": "0.3.0", @@ -4030,11 +3794,6 @@ "bignumber.js": "^7.0.0" } }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -4184,11 +3943,6 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==" - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -4357,15 +4111,6 @@ "statsd-parser": "~0.0.4" } }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, "make-plural": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", @@ -4428,14 +4173,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "requires": { - "source-map": "^0.6.1" - } - }, "mersenne": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", @@ -4729,33 +4466,6 @@ "saslprep": "^1.0.0" } }, - "mongojs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mongojs/-/mongojs-3.1.0.tgz", - "integrity": "sha512-aXJ4xfXwx9s1cqtKTZ24PypXiWhIgvgENObQzCGbV4QBxEVedy3yuErhx6znk959cF2dOzL2ClgXJvIhfgkpIQ==", - "requires": { - "each-series": "^1.0.0", - "mongodb": "^3.3.2", - "nyc": "^14.1.1", - "once": "^1.4.0", - "parse-mongo-url": "^1.1.1", - "readable-stream": "^3.4.0", - "thunky": "^1.1.0", - "to-mongodb-core": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -4811,11 +4521,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" - }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -4867,6 +4572,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -4880,104 +4586,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -5098,11 +4706,6 @@ "word-wrap": "~1.2.3" } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==" - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -5148,17 +4751,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, - "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5182,11 +4774,6 @@ "error-ex": "^1.2.0" } }, - "parse-mongo-url": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-mongo-url/-/parse-mongo-url-1.1.1.tgz", - "integrity": "sha512-7bZUusQIrFLwvsLHBnCz2WKYQ5LKO/LwKPnvQxbMIh9gDx8H5ZsknRmLjZdn6GVdrgVOwqDrZKsY0qDLNmRgcw==" - }, "parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", @@ -5200,7 +4787,8 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true }, "path-is-absolute": { "version": "1.0.1", @@ -6007,11 +5595,6 @@ "ipaddr.js": "1.9.1" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -6274,14 +5857,6 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "requires": { - "es6-error": "^4.0.1" - } - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -6334,7 +5909,8 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true }, "require-in-the-middle": { "version": "4.0.1", @@ -6365,7 +5941,8 @@ "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "require-relative": { "version": "0.8.7", @@ -6386,6 +5963,7 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -6588,7 +6166,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true }, "setprototypeof": { "version": "1.1.1", @@ -6643,7 +6222,8 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==" + "integrity": "sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==", + "dev": true }, "sinon": { "version": "9.0.1", @@ -6725,67 +6305,11 @@ "memory-pager": "^1.0.2" } }, - "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", - "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "requires": { - "minimist": "^1.2.5" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -6794,12 +6318,14 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -6808,7 +6334,8 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true }, "split": { "version": "1.0.1", @@ -6821,7 +6348,8 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, "sshpk": { "version": "1.16.0", @@ -6956,6 +6484,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" }, @@ -6963,14 +6492,16 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true } } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true }, "strip-json-comments": { "version": "3.0.1", @@ -7080,117 +6611,6 @@ } } }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - } - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7210,11 +6630,6 @@ "readable-stream": "2 || 3" } }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -7238,12 +6653,8 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" - }, - "to-mongodb-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-mongodb-core/-/to-mongodb-core-2.0.0.tgz", - "integrity": "sha512-vfXXcGYFP8+0L5IPOtUzzVIvPE/G3GN0TKa/PRBlzPqYyhm+UxhPmvv634EQgO4Ot8dHbBFihOslMJQclY8Z9A==" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true }, "to-no-case": { "version": "1.0.2", @@ -7425,6 +6836,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -7534,6 +6946,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -7541,7 +6954,8 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true }, "wide-align": { "version": "1.1.3", @@ -7595,6 +7009,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -7604,17 +7019,20 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -7648,16 +7066,6 @@ } } }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", @@ -7684,7 +7092,8 @@ "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true }, "yallist": { "version": "3.1.1", @@ -7695,6 +7104,7 @@ "version": "13.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, "requires": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -7711,12 +7121,14 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "requires": { "locate-path": "^3.0.0" } @@ -7724,12 +7136,14 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -7739,6 +7153,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "requires": { "p-limit": "^2.0.0" } @@ -7747,6 +7162,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -7759,6 +7175,7 @@ "version": "13.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b14882656f..dfb1d93a7a 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -31,7 +31,6 @@ "metrics-sharelatex": "^2.6.2", "mongo-uri": "^0.1.2", "mongodb": "^3.6.0", - "mongojs": "3.1.0", "redis": "~0.10.1", "redis-sharelatex": "^1.0.13", "request": "~2.88.2", diff --git a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js index d6d6b44726..4cf250cfac 100644 --- a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js @@ -14,8 +14,7 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { ObjectId } = mongojs +const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const request = require('request') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index 8ceb1b0ba7..5df0844409 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -18,9 +18,7 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { db } = mongojs -const { ObjectId } = mongojs +const { db, ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const request = require('request') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now @@ -126,7 +124,7 @@ describe('Archiving updates', function () { after(function (done) { MockWebApi.getUserInfo.restore() - return db.docHistory.remove( + return db.docHistory.deleteMany( { project_id: ObjectId(this.project_id) }, () => { return db.docHistoryIndex.remove( @@ -172,7 +170,7 @@ describe('Archiving updates', function () { }) it('should have one remaining pack after cache is expired', function (done) { - return db.docHistory.remove( + return db.docHistory.deleteMany( { doc_id: ObjectId(this.doc_id), expiresAt: { $exists: true } diff --git a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js index 4a06e086e5..a48abcc976 100644 --- a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js @@ -14,8 +14,7 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { ObjectId } = mongojs +const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const request = require('request') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js index 4830f338d4..926d005164 100644 --- a/services/track-changes/test/acceptance/js/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/js/GettingADiffTests.js @@ -13,9 +13,7 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { db } = mongojs -const { ObjectId } = mongojs +const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const TrackChangesApp = require('./helpers/TrackChangesApp') diff --git a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js index 052cb405e7..83bd985241 100644 --- a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js @@ -14,9 +14,7 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { db } = mongojs -const { ObjectId } = mongojs +const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const TrackChangesApp = require('./helpers/TrackChangesApp') diff --git a/services/track-changes/test/acceptance/js/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js index 484d1a9bc6..b6f8908554 100644 --- a/services/track-changes/test/acceptance/js/LockManagerTests.js +++ b/services/track-changes/test/acceptance/js/LockManagerTests.js @@ -13,8 +13,6 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { ObjectId } = mongojs const Settings = require('settings-sharelatex') const LockManager = require('../../../app/js/LockManager') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js index 8afef4b168..d4d5d60304 100644 --- a/services/track-changes/test/acceptance/js/RestoringVersions.js +++ b/services/track-changes/test/acceptance/js/RestoringVersions.js @@ -13,9 +13,7 @@ const sinon = require('sinon') const chai = require('chai') chai.should() const { expect } = chai -const mongojs = require('../../../app/js/mongojs') -const { db } = mongojs -const { ObjectId } = mongojs +const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const TrackChangesApp = require('./helpers/TrackChangesApp') diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index cca55a87aa..4e5612cb6d 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -19,7 +19,7 @@ const request = require('request') const Settings = require('settings-sharelatex') const rclient = require('redis-sharelatex').createClient(Settings.redis.history) // Only works locally for now const Keys = Settings.redis.history.key_schema -const { db, ObjectId } = require('../../../../app/js/mongojs') +const { db, ObjectId } = require('../../../../app/js/mongodb') const aws = require('aws-sdk') const s3 = new aws.S3({ @@ -87,11 +87,11 @@ module.exports = TrackChangesClient = { if (callback == null) { callback = function (error, updates) {} } - return db.projectHistoryMetaData.find( + return db.projectHistoryMetaData.findOne( { project_id: ObjectId(project_id) }, - (error, projects) => callback(error, projects[0]) + callback ) }, @@ -99,7 +99,7 @@ module.exports = TrackChangesClient = { if (callback == null) { callback = function (error) {} } - return db.projectHistoryMetaData.update( + return db.projectHistoryMetaData.updateOne( { project_id: ObjectId(project_id) }, From a292bd7b1ca087d77a924b83e83cf0a3837844e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Sep 2020 15:08:04 +0000 Subject: [PATCH 520/549] Bump node-fetch from 2.6.0 to 2.6.1 Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1. - [Release notes](https://github.com/bitinn/node-fetch/releases) - [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index a3545e5560..7a060eb1ba 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -4861,9 +4861,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-forge": { "version": "0.9.1", From f052625396de35531e7015757eb664ca41a55e6e Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 17 Sep 2020 15:43:32 +0100 Subject: [PATCH 521/549] [misc] bump the dev-env to 3.3.4 and bump the node version to 10.22.1 --- services/track-changes/.github/dependabot.yml | 6 ++++++ services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 2 +- services/track-changes/buildscript.txt | 4 ++-- services/track-changes/docker-compose.yml | 4 ++-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/services/track-changes/.github/dependabot.yml b/services/track-changes/.github/dependabot.yml index c6f98d843d..e2c64a3351 100644 --- a/services/track-changes/.github/dependabot.yml +++ b/services/track-changes/.github/dependabot.yml @@ -15,3 +15,9 @@ updates: # Block informal upgrades -- security upgrades use a separate queue. # https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#open-pull-requests-limit open-pull-requests-limit: 0 + + # currently assign team-magma to all dependabot PRs - this may change in + # future if we reorganise teams + labels: + - "dependencies" + - "Team-Magma" diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index b61c07ffdd..c2f6421352 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -10.21.0 +10.22.1 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 78a715757d..f0e362fca0 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -2,7 +2,7 @@ # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -FROM node:10.21.0 as base +FROM node:10.22.1 as base WORKDIR /app diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 1750deaa0e..24350af64f 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -3,6 +3,6 @@ track-changes --docker-repos=gcr.io/overleaf-ops --env-add=AWS_BUCKET=bucket --env-pass-through= ---node-version=10.21.0 +--node-version=10.22.1 --public-repo=True ---script-version=3.3.2 +--script-version=3.3.4 diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index ba2b13aad6..d2d867a391 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.3" services: test_unit: - image: node:10.21.0 + image: node:10.22.1 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: node:10.21.0 + image: node:10.22.1 volumes: - .:/app working_dir: /app From 7603e328cdfa731cec3f448cb7c9875cab1b6100 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2020 09:56:07 +0000 Subject: [PATCH 522/549] Bump lodash from 4.17.15 to 4.17.20 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.20. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.20) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 36 ++---------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 06294524b8..277b49a0d0 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -2233,12 +2233,6 @@ "type-fest": "^0.8.1" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3557,12 +3551,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "run-async": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", @@ -3919,9 +3907,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.at": { "version": "4.6.0", @@ -5064,12 +5052,6 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -5378,12 +5360,6 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -6565,12 +6541,6 @@ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", From 724e616df925348d6db2b8a61665714543991952 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Sep 2020 09:56:48 +0000 Subject: [PATCH 523/549] Bump yargs-parser from 13.1.1 to 13.1.2 Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 13.1.1 to 13.1.2. - [Release notes](https://github.com/yargs/yargs-parser/releases) - [Changelog](https://github.com/yargs/yargs-parser/blob/master/docs/CHANGELOG-full.md) - [Commits](https://github.com/yargs/yargs-parser/commits) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 06294524b8..0073f56017 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -4424,16 +4424,6 @@ "y18n": "^4.0.0", "yargs-parser": "^13.1.2" } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -7172,9 +7162,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", From 41a65ee41e14af28b8daf5eb85e9698301382572 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 29 Sep 2020 12:22:22 +0100 Subject: [PATCH 524/549] [PackWorker] wait for the db setup before starting to process updates --- services/track-changes/app/js/PackWorker.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index 74b5368368..500556cd79 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -18,7 +18,7 @@ let project_id, doc_id const Settings = require('settings-sharelatex') const async = require('async') const _ = require('underscore') -const { db, ObjectId } = require('./mongodb') +const { db, ObjectId, waitForDb } = require('./mongodb') const fs = require('fs') const Metrics = require('metrics-sharelatex') Metrics.initialize('track-changes') @@ -160,10 +160,21 @@ const ObjectIdFromDate = function (date) { // find packs to be marked as finalised:true, those which have a newer pack present // then only consider finalised:true packs for archiving -if (pending != null) { - logger.log(`got ${pending.length} entries from ${source}`) - processUpdates(pending) -} else { +waitForDb() + .then(() => { + if (pending != null) { + logger.log(`got ${pending.length} entries from ${source}`) + processUpdates(pending) + } else { + processFromOneWeekAgo() + } + }) + .catch((err) => { + logger.fatal({ err }, 'cannot connect to mongo, exiting') + process.exit(1) + }) + +function processFromOneWeekAgo() { const oneWeekAgo = new Date(Date.now() - 7 * DAYS) db.docHistory .find( From 2c8916f2e03d642d39b2df1fb7723c7cd3491e4b Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 29 Sep 2020 12:23:18 +0100 Subject: [PATCH 525/549] [PackWorker] fix the db cleanup process -- we do not expose db.close --- services/track-changes/app/js/PackWorker.js | 5 +++-- services/track-changes/app/js/mongodb.js | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index 500556cd79..232d7b24ed 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -15,10 +15,11 @@ */ let LIMIT, pending let project_id, doc_id +const { callbackify } = require('util') const Settings = require('settings-sharelatex') const async = require('async') const _ = require('underscore') -const { db, ObjectId, waitForDb } = require('./mongodb') +const { db, ObjectId, waitForDb, closeDb } = require('./mongodb') const fs = require('fs') const Metrics = require('metrics-sharelatex') Metrics.initialize('track-changes') @@ -84,7 +85,7 @@ const finish = function () { clearTimeout(shutDownTimer) } logger.log('closing db') - return db.close(function () { + callbackify(closeDb)(function () { logger.log('closing LockManager Redis Connection') return LockManager.close(function () { logger.log( diff --git a/services/track-changes/app/js/mongodb.js b/services/track-changes/app/js/mongodb.js index 38ebe0462a..e0d50a165b 100644 --- a/services/track-changes/app/js/mongodb.js +++ b/services/track-changes/app/js/mongodb.js @@ -23,8 +23,20 @@ async function setupDb() { db.projectHistoryMetaData = internalDb.collection('projectHistoryMetaData') } +async function closeDb() { + let client + try { + client = await clientPromise + } catch (e) { + // there is nothing to close + return + } + return client.close() +} + module.exports = { db, ObjectId, + closeDb, waitForDb } From 68a1b4d4590f348762ad88f35b404061ae415637 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 10 Nov 2020 11:32:05 +0000 Subject: [PATCH 526/549] [misc] bump @overleaf/redis-wrapper to version 2.0.0 --- services/track-changes/app/js/LockManager.js | 2 +- services/track-changes/app/js/RedisManager.js | 2 +- services/track-changes/package-lock.json | 75 ++++--------------- services/track-changes/package.json | 3 +- .../js/helpers/TrackChangesClient.js | 4 +- .../unit/js/LockManager/LockManagerTests.js | 2 +- .../unit/js/RedisManager/RedisManagerTests.js | 2 +- 7 files changed, 24 insertions(+), 66 deletions(-) diff --git a/services/track-changes/app/js/LockManager.js b/services/track-changes/app/js/LockManager.js index 28701f4ac1..6b94a89233 100644 --- a/services/track-changes/app/js/LockManager.js +++ b/services/track-changes/app/js/LockManager.js @@ -11,7 +11,7 @@ */ let LockManager const Settings = require('settings-sharelatex') -const redis = require('redis-sharelatex') +const redis = require('@overleaf/redis-wrapper') const rclient = redis.createClient(Settings.redis.lock) const os = require('os') const crypto = require('crypto') diff --git a/services/track-changes/app/js/RedisManager.js b/services/track-changes/app/js/RedisManager.js index 5e762bf45a..b995a60128 100644 --- a/services/track-changes/app/js/RedisManager.js +++ b/services/track-changes/app/js/RedisManager.js @@ -14,7 +14,7 @@ */ let RedisManager const Settings = require('settings-sharelatex') -const redis = require('redis-sharelatex') +const redis = require('@overleaf/redis-wrapper') const rclient = redis.createClient(Settings.redis.history) const Keys = Settings.redis.history.key_schema const async = require('async') diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 4fff387a60..789c13ac90 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -906,9 +906,17 @@ } }, "@overleaf/o-error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.0.0.tgz", - "integrity": "sha512-LsM2s6Iy9G97ktPo0ys4VxtI/m3ahc1ZHwjo5XnhXtjeIkkkVAehsrcRRoV/yWepPjymB0oZonhcfojpjYR/tg==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.1.0.tgz", + "integrity": "sha512-TWJ80ozJ1LeugGTJyGQSPEuTkZ9LqZD7/ndLE6azKa03SU/mKV/FINcfk8atpVil8iv1hHQwzYZc35klplpMpQ==" + }, + "@overleaf/redis-wrapper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@overleaf/redis-wrapper/-/redis-wrapper-2.0.0.tgz", + "integrity": "sha512-lREuhDPNgmKyOmL1g6onfRzDLWOG/POsE4Vd7ZzLnKDYt9SbOIujtx3CxI2qtQAKBYHf/hfyrbtyX3Ib2yTvYA==", + "requires": { + "ioredis": "~4.17.3" + } }, "@protobufjs/aspromise": { "version": "1.1.2", @@ -3924,12 +3932,12 @@ "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, "lodash.get": { "version": "4.4.2", @@ -4254,11 +4262,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" }, - "mkdirp": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==" - }, "mocha": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", @@ -5613,11 +5616,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, - "q": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/q/-/q-0.9.2.tgz", - "integrity": "sha512-ZOxMuWPMJnsUdYhuQ9glpZwKhB4cm8ubYFy1nNCY8TkSAuZun5fd8jCDTlf2ykWnK8x9HGn1stNtLeG179DebQ==" - }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -5748,59 +5746,16 @@ "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" }, "redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", "requires": { "redis-errors": "^1.0.0" } }, - "redis-sentinel": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/redis-sentinel/-/redis-sentinel-0.1.1.tgz", - "integrity": "sha512-cKtLSUzDsKmsB50J1eIV/SH11DSMiHgsm/gDPRCU5lXz5OyTSuLKWg9oc8d5n74kZwtAyRkfJP0x8vYXvlPjFQ==", - "requires": { - "q": "0.9.2", - "redis": "0.11.x" - }, - "dependencies": { - "redis": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-0.11.0.tgz", - "integrity": "sha512-wkgzIZ9HuxJ6Sul1IW/6FG13Ecv6q8kmdHb5xo09Hu6bgWzz5qsnM06SVMpDxFNbyApaRjy8CwnmVaRMMhAMWg==" - } - } - }, - "redis-sharelatex": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/redis-sharelatex/-/redis-sharelatex-1.0.13.tgz", - "integrity": "sha512-sAQNofqfcMlIxzxNJF1qUspJKDM1VuuIOrGZQX9nb5JtcJ5cusa5sc+Oyb51eymPV5mZGWT3u07tKtv4jdXVIg==", - "requires": { - "async": "^2.5.0", - "coffee-script": "1.8.0", - "ioredis": "~4.17.3", - "redis-sentinel": "0.1.1", - "underscore": "1.7.0" - }, - "dependencies": { - "coffee-script": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", - "integrity": "sha512-EvLTMcu9vR6G1yfnz75yrISvhq1eBPC+pZbQhHzTiC5vXgpYIrArxQc5tB+SYfBi3souVdSZ4AZzYxI72oLXUw==", - "requires": { - "mkdirp": "~0.3.5" - } - }, - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA==" - } - } - }, "regenerator-runtime": { "version": "0.13.3", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index dfb1d93a7a..b4fc076c96 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -18,6 +18,8 @@ "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" }, "dependencies": { + "@overleaf/o-error": "^3.1.0", + "@overleaf/redis-wrapper": "^2.0.0", "JSONStream": "^1.3.5", "async": "^2.6.3", "aws-sdk": "^2.643.0", @@ -32,7 +34,6 @@ "mongo-uri": "^0.1.2", "mongodb": "^3.6.0", "redis": "~0.10.1", - "redis-sharelatex": "^1.0.13", "request": "~2.88.2", "requestretry": "^4.1.0", "s3-streams": "^0.4.0", diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index 4e5612cb6d..9e8e05bbc4 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -17,7 +17,9 @@ const async = require('async') const zlib = require('zlib') const request = require('request') const Settings = require('settings-sharelatex') -const rclient = require('redis-sharelatex').createClient(Settings.redis.history) // Only works locally for now +const rclient = require('@overleaf/redis-wrapper').createClient( + Settings.redis.history +) // Only works locally for now const Keys = Settings.redis.history.key_schema const { db, ObjectId } = require('../../../../app/js/mongodb') diff --git a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js index 991e5ee2c6..42ee6dcd62 100644 --- a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js @@ -31,7 +31,7 @@ describe('LockManager', function () { } this.LockManager = SandboxedModule.require(modulePath, { requires: { - 'redis-sharelatex': { + '@overleaf/redis-wrapper': { createClient: () => { return (this.rclient = { auth: sinon.stub() }) } diff --git a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js index fcfc17b1bb..f3185daf08 100644 --- a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js @@ -22,7 +22,7 @@ describe('RedisManager', function () { beforeEach(function () { this.RedisManager = SandboxedModule.require(modulePath, { requires: { - 'redis-sharelatex': { + '@overleaf/redis-wrapper': { createClient: () => { return (this.rclient = { auth: sinon.stub(), From 76d0865c216c3e4e408e543a46221b15207e8f74 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Mon, 23 Nov 2020 10:56:33 -0500 Subject: [PATCH 527/549] Upgrade build-scripts to 3.4.0 This version fixes docker-compose health checks for dependent services. See https://github.com/overleaf/dev-environment/pull/409 for details. --- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.ci.yml | 17 ++++++++++++++--- services/track-changes/docker-compose.yml | 17 ++++++++++++++--- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 24350af64f..1e7871e90d 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -5,4 +5,4 @@ track-changes --env-pass-through= --node-version=10.22.1 --public-repo=True ---script-version=3.3.4 +--script-version=3.4.0 diff --git a/services/track-changes/docker-compose.ci.yml b/services/track-changes/docker-compose.ci.yml index 6717530161..3797853153 100644 --- a/services/track-changes/docker-compose.ci.yml +++ b/services/track-changes/docker-compose.ci.yml @@ -20,6 +20,7 @@ services: environment: ELASTIC_SEARCH_DSN: es:9200 REDIS_HOST: redis + QUEUES_REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres AWS_S3_ENDPOINT: http://s3:9090 @@ -50,12 +51,22 @@ services: user: root redis: image: redis + healthcheck: + test: ping="$$(redis-cli ping)" && [ "$$ping" = 'PONG' ] + interval: 1s + retries: 20 mongo: image: mongo:4.0 + healthcheck: + test: "mongo --quiet localhost/test --eval 'quit(db.runCommand({ ping: 1 }).ok ? 0 : 1)'" + interval: 1s + retries: 20 s3: - build: - context: test/acceptance/deps - dockerfile: Dockerfile.s3mock + image: adobe/s3mock environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket + healthcheck: + test: wget --quiet --output-document=/dev/null http://localhost:9090 + interval: 1s + retries: 20 diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index d2d867a391..fd76a1672f 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -25,6 +25,7 @@ services: environment: ELASTIC_SEARCH_DSN: es:9200 REDIS_HOST: redis + QUEUES_REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres AWS_S3_ENDPOINT: http://s3:9090 @@ -48,13 +49,23 @@ services: redis: image: redis + healthcheck: + test: ping=$$(redis-cli ping) && [ "$$ping" = 'PONG' ] + interval: 1s + retries: 20 mongo: image: mongo:4.0 + healthcheck: + test: "mongo --quiet localhost/test --eval 'quit(db.runCommand({ ping: 1 }).ok ? 0 : 1)'" + interval: 1s + retries: 20 s3: - build: - context: test/acceptance/deps - dockerfile: Dockerfile.s3mock + image: adobe/s3mock environment: - initialBuckets=fake_user_files,fake_template_files,fake_public_files,bucket + healthcheck: + test: wget --quiet --output-document=/dev/null http://localhost:9090 + interval: 1s + retries: 20 From 8ef3aa1767238e7073ac6a43204c181feac5bca0 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Wed, 25 Nov 2020 11:57:20 +0000 Subject: [PATCH 528/549] [misc] bump metrics module to 3.4.1 - renamed package from `metrics-sharelatex` to `@overleaf/metrics` - drop support for statsd backend - decaffeinate - compress `/metrics` response using gzip - bump debugging agents to latest versions - expose prometheus interfaces for custom metrics (custom tags) - cleanup of open sockets metrics - fix deprecation warnings for header access --- services/track-changes/app.js | 2 +- services/track-changes/app/js/MongoAWS.js | 2 +- services/track-changes/app/js/MongoManager.js | 2 +- services/track-changes/app/js/PackManager.js | 2 +- services/track-changes/app/js/PackWorker.js | 2 +- services/track-changes/package-lock.json | 1608 +++++++++-------- services/track-changes/package.json | 2 +- .../test/unit/js/DocArchive/MongoAWS.js | 2 +- .../unit/js/MongoManager/MongoManagerTests.js | 2 +- .../unit/js/PackManager/PackManagerTests.js | 2 +- 10 files changed, 824 insertions(+), 802 deletions(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 3794e8f655..b9ca5120ce 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -5,7 +5,7 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const Metrics = require('metrics-sharelatex') +const Metrics = require('@overleaf/metrics') Metrics.initialize('track-changes') const Settings = require('settings-sharelatex') const logger = require('logger-sharelatex') diff --git a/services/track-changes/app/js/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js index 713f76c9f1..c0f88e98e3 100644 --- a/services/track-changes/app/js/MongoAWS.js +++ b/services/track-changes/app/js/MongoAWS.js @@ -22,7 +22,7 @@ const { db, ObjectId } = require('./mongodb') const JSONStream = require('JSONStream') const ReadlineStream = require('byline') const zlib = require('zlib') -const Metrics = require('metrics-sharelatex') +const Metrics = require('@overleaf/metrics') const DAYS = 24 * 3600 * 1000 // one day in milliseconds diff --git a/services/track-changes/app/js/MongoManager.js b/services/track-changes/app/js/MongoManager.js index 72a87bdd0a..bfcdcf1ddb 100644 --- a/services/track-changes/app/js/MongoManager.js +++ b/services/track-changes/app/js/MongoManager.js @@ -16,7 +16,7 @@ const { db, ObjectId } = require('./mongodb') const PackManager = require('./PackManager') const async = require('async') const _ = require('underscore') -const metrics = require('metrics-sharelatex') +const metrics = require('@overleaf/metrics') const logger = require('logger-sharelatex') module.exports = MongoManager = { diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index fc7a483729..10938fede6 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -22,7 +22,7 @@ const { db, ObjectId } = require('./mongodb') const logger = require('logger-sharelatex') const LockManager = require('./LockManager') const MongoAWS = require('./MongoAWS') -const Metrics = require('metrics-sharelatex') +const Metrics = require('@overleaf/metrics') const ProjectIterator = require('./ProjectIterator') const Settings = require('settings-sharelatex') const keys = Settings.redis.lock.key_schema diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index 232d7b24ed..dc15ee8f21 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -21,7 +21,7 @@ const async = require('async') const _ = require('underscore') const { db, ObjectId, waitForDb, closeDb } = require('./mongodb') const fs = require('fs') -const Metrics = require('metrics-sharelatex') +const Metrics = require('@overleaf/metrics') Metrics.initialize('track-changes') const logger = require('logger-sharelatex') logger.initialize('track-changes-packworker') diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 789c13ac90..217fcb653f 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -185,200 +185,6 @@ } } }, - "@google-cloud/debug-agent": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-3.2.0.tgz", - "integrity": "sha512-fP87kYbS6aeDna08BivwQ1J260mwJGchRi99XdWCgqbRwuFac8ul0OT5i2wEeDSc5QaDX8ZuWQQ0igZvh1rTyQ==", - "requires": { - "@google-cloud/common": "^0.32.0", - "@sindresorhus/is": "^0.15.0", - "acorn": "^6.0.0", - "coffeescript": "^2.0.0", - "console-log-level": "^1.4.0", - "extend": "^3.0.1", - "findit2": "^2.2.3", - "gcp-metadata": "^1.0.0", - "lodash.pickby": "^4.6.0", - "p-limit": "^2.2.0", - "pify": "^4.0.1", - "semver": "^6.0.0", - "source-map": "^0.6.1", - "split": "^1.0.0" - }, - "dependencies": { - "@google-cloud/common": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", - "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", - "requires": { - "@google-cloud/projectify": "^0.3.3", - "@google-cloud/promisify": "^0.4.0", - "@types/request": "^2.48.1", - "arrify": "^2.0.0", - "duplexify": "^3.6.0", - "ent": "^2.2.0", - "extend": "^3.0.2", - "google-auth-library": "^3.1.1", - "pify": "^4.0.1", - "retry-request": "^4.0.0", - "teeny-request": "^3.11.3" - } - }, - "@google-cloud/projectify": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", - "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" - }, - "@google-cloud/promisify": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", - "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==" - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "coffeescript": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz", - "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==" - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "gaxios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", - "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", - "requires": { - "abort-controller": "^3.0.0", - "extend": "^3.0.2", - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.3.0" - } - }, - "gcp-metadata": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", - "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", - "requires": { - "gaxios": "^1.0.2", - "json-bigint": "^0.3.0" - } - }, - "google-auth-library": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", - "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", - "requires": { - "base64-js": "^1.3.0", - "fast-text-encoding": "^1.0.0", - "gaxios": "^1.2.1", - "gcp-metadata": "^1.0.0", - "gtoken": "^2.3.2", - "https-proxy-agent": "^2.2.1", - "jws": "^3.1.5", - "lru-cache": "^5.0.0", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "google-p12-pem": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", - "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", - "requires": { - "node-forge": "^0.8.0", - "pify": "^4.0.0" - } - }, - "gtoken": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", - "requires": { - "gaxios": "^1.0.4", - "google-p12-pem": "^1.0.0", - "jws": "^3.1.5", - "mime": "^2.2.0", - "pify": "^4.0.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "node-forge": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", - "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "teeny-request": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", - "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", - "requires": { - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.2.0", - "uuid": "^3.3.2" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, "@google-cloud/logging": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-7.3.0.tgz", @@ -446,200 +252,6 @@ "extend": "^3.0.2" } }, - "@google-cloud/profiler": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-0.2.3.tgz", - "integrity": "sha512-rNvtrFtIebIxZEJ/O0t8n7HciZGIXBo8DvHxWqAmsCaeLvkTtsaL6HmPkwxrNQ1IhbYWAxF+E/DwCiHyhKmgTg==", - "requires": { - "@google-cloud/common": "^0.26.0", - "@types/console-log-level": "^1.4.0", - "@types/semver": "^5.5.0", - "bindings": "^1.2.1", - "console-log-level": "^1.4.0", - "delay": "^4.0.1", - "extend": "^3.0.1", - "gcp-metadata": "^0.9.0", - "nan": "^2.11.1", - "parse-duration": "^0.1.1", - "pify": "^4.0.0", - "pretty-ms": "^4.0.0", - "protobufjs": "~6.8.6", - "semver": "^5.5.0", - "teeny-request": "^3.3.0" - }, - "dependencies": { - "@google-cloud/common": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.26.2.tgz", - "integrity": "sha512-xJ2M/q3MrUbnYZuFlpF01caAlEhAUoRn0NXp93Hn3pkFpfSOG8YfbKbpBAHvcKVbBOAKVIwPsleNtuyuabUwLQ==", - "requires": { - "@google-cloud/projectify": "^0.3.2", - "@google-cloud/promisify": "^0.3.0", - "@types/duplexify": "^3.5.0", - "@types/request": "^2.47.0", - "arrify": "^1.0.1", - "duplexify": "^3.6.0", - "ent": "^2.2.0", - "extend": "^3.0.1", - "google-auth-library": "^2.0.0", - "pify": "^4.0.0", - "retry-request": "^4.0.0", - "through2": "^3.0.0" - } - }, - "@google-cloud/projectify": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", - "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" - }, - "@google-cloud/promisify": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.3.1.tgz", - "integrity": "sha512-QzB0/IMvB0eFxFK7Eqh+bfC8NLv3E9ScjWQrPOk6GgfNroxcVITdTlT8NRsRrcp5+QQJVPLkRqKG0PUdaWXmHw==" - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "gaxios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", - "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", - "requires": { - "abort-controller": "^3.0.0", - "extend": "^3.0.2", - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.3.0" - } - }, - "gcp-metadata": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", - "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", - "requires": { - "gaxios": "^1.0.2", - "json-bigint": "^0.3.0" - } - }, - "google-auth-library": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-2.0.2.tgz", - "integrity": "sha512-FURxmo1hBVmcfLauuMRKOPYAPKht3dGuI2wjeJFalDUThO0HoYVjr4yxt5cgYSFm1dgUpmN9G/poa7ceTFAIiA==", - "requires": { - "axios": "^0.18.0", - "gcp-metadata": "^0.7.0", - "gtoken": "^2.3.0", - "https-proxy-agent": "^2.2.1", - "jws": "^3.1.5", - "lru-cache": "^5.0.0", - "semver": "^5.5.0" - }, - "dependencies": { - "gcp-metadata": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.7.0.tgz", - "integrity": "sha512-ffjC09amcDWjh3VZdkDngIo7WoluyC5Ag9PAYxZbmQLOLNI8lvPtoKTSCyU54j2gwy5roZh6sSMTfkY2ct7K3g==", - "requires": { - "axios": "^0.18.0", - "extend": "^3.0.1", - "retry-axios": "0.3.2" - } - } - } - }, - "google-p12-pem": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", - "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", - "requires": { - "node-forge": "^0.8.0", - "pify": "^4.0.0" - } - }, - "gtoken": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", - "requires": { - "gaxios": "^1.0.4", - "google-p12-pem": "^1.0.0", - "jws": "^3.1.5", - "mime": "^2.2.0", - "pify": "^4.0.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "node-forge": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", - "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" - }, - "teeny-request": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", - "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", - "requires": { - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.2.0", - "uuid": "^3.3.2" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, "@google-cloud/projectify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", @@ -650,196 +262,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==" }, - "@google-cloud/trace-agent": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-3.6.1.tgz", - "integrity": "sha512-KDo85aPN4gSxJ7oEIOlKd7aGENZFXAM1kbIn1Ds+61gh/K1CQWSyepgJo3nUpAwH6D1ezDWV7Iaf8ueoITc8Uw==", - "requires": { - "@google-cloud/common": "^0.32.1", - "builtin-modules": "^3.0.0", - "console-log-level": "^1.4.0", - "continuation-local-storage": "^3.2.1", - "extend": "^3.0.0", - "gcp-metadata": "^1.0.0", - "hex2dec": "^1.0.1", - "is": "^3.2.0", - "methods": "^1.1.1", - "require-in-the-middle": "^4.0.0", - "semver": "^6.0.0", - "shimmer": "^1.2.0", - "uuid": "^3.0.1" - }, - "dependencies": { - "@google-cloud/common": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.32.1.tgz", - "integrity": "sha512-bLdPzFvvBMtVkwsoBtygE9oUm3yrNmPa71gvOgucYI/GqvNP2tb6RYsDHPq98kvignhcgHGDI5wyNgxaCo8bKQ==", - "requires": { - "@google-cloud/projectify": "^0.3.3", - "@google-cloud/promisify": "^0.4.0", - "@types/request": "^2.48.1", - "arrify": "^2.0.0", - "duplexify": "^3.6.0", - "ent": "^2.2.0", - "extend": "^3.0.2", - "google-auth-library": "^3.1.1", - "pify": "^4.0.1", - "retry-request": "^4.0.0", - "teeny-request": "^3.11.3" - } - }, - "@google-cloud/projectify": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-0.3.3.tgz", - "integrity": "sha512-7522YHQ4IhaafgSunsFF15nG0TGVmxgXidy9cITMe+256RgqfcrfWphiMufW+Ou4kqagW/u3yxwbzVEW3dk2Uw==" - }, - "@google-cloud/promisify": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-0.4.0.tgz", - "integrity": "sha512-4yAHDC52TEMCNcMzVC8WlqnKKKq+Ssi2lXoUg9zWWkZ6U6tq9ZBRYLHHCRdfU+EU9YJsVmivwGcKYCjRGjnf4Q==" - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "gaxios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", - "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", - "requires": { - "abort-controller": "^3.0.0", - "extend": "^3.0.2", - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.3.0" - } - }, - "gcp-metadata": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", - "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", - "requires": { - "gaxios": "^1.0.2", - "json-bigint": "^0.3.0" - } - }, - "google-auth-library": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.2.tgz", - "integrity": "sha512-cDQMzTotwyWMrg5jRO7q0A4TL/3GWBgO7I7q5xGKNiiFf9SmGY/OJ1YsLMgI2MVHHsEGyrqYnbnmV1AE+Z6DnQ==", - "requires": { - "base64-js": "^1.3.0", - "fast-text-encoding": "^1.0.0", - "gaxios": "^1.2.1", - "gcp-metadata": "^1.0.0", - "gtoken": "^2.3.2", - "https-proxy-agent": "^2.2.1", - "jws": "^3.1.5", - "lru-cache": "^5.0.0", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "google-p12-pem": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.4.tgz", - "integrity": "sha512-SwLAUJqUfTB2iS+wFfSS/G9p7bt4eWcc2LyfvmUXe7cWp6p3mpxDo6LLI29MXdU6wvPcQ/up298X7GMC5ylAlA==", - "requires": { - "node-forge": "^0.8.0", - "pify": "^4.0.0" - } - }, - "gtoken": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", - "requires": { - "gaxios": "^1.0.4", - "google-p12-pem": "^1.0.0", - "jws": "^3.1.5", - "mime": "^2.2.0", - "pify": "^4.0.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "node-forge": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.5.tgz", - "integrity": "sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "teeny-request": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz", - "integrity": "sha512-CKncqSF7sH6p4rzCgkb/z/Pcos5efl0DmolzvlqRQUNcpRIruOhY9+T1FsIlyEbfWd7MsFpodROOwHYh2BaXzw==", - "requires": { - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.2.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - } - } - }, "@grpc/grpc-js": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.5.tgz", @@ -905,6 +327,351 @@ } } }, + "@overleaf/metrics": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@overleaf/metrics/-/metrics-3.4.1.tgz", + "integrity": "sha512-OgjlzuC+2gPdIEDHhmd9LDMu01tk1ln0cJhw1727BZ+Wgf2Z1hjuHRt4JeCkf+PFTHwJutVYT8v6IGPpNEPtbg==", + "requires": { + "@google-cloud/debug-agent": "^5.1.2", + "@google-cloud/profiler": "^4.0.3", + "@google-cloud/trace-agent": "^5.1.1", + "compression": "^1.7.4", + "prom-client": "^11.1.3", + "underscore": "~1.6.0", + "yn": "^3.1.1" + }, + "dependencies": { + "@google-cloud/common": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz", + "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==", + "requires": { + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^6.1.1", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" + } + }, + "@google-cloud/debug-agent": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-5.1.3.tgz", + "integrity": "sha512-WbzeEz4MvPlM7DX2QBsPcWgF62u7LSQv/oMYPl0L+TddTebqjDKiVXwxpzWk61NIfcKiet3dyCbPIt3N5o8XPQ==", + "requires": { + "@google-cloud/common": "^3.0.0", + "acorn": "^8.0.0", + "coffeescript": "^2.0.0", + "console-log-level": "^1.4.0", + "extend": "^3.0.2", + "findit2": "^2.2.3", + "gcp-metadata": "^4.0.0", + "p-limit": "^3.0.1", + "semver": "^7.0.0", + "source-map": "^0.6.1", + "split": "^1.0.0" + } + }, + "@google-cloud/profiler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.0.tgz", + "integrity": "sha512-9e1zXRctLSUHAoAsFGwE4rS28fr0siiG+jXl5OpwTK8ZAUlxb70aosHaZGdsv8YXrYKjuiufjRZ/OXCs0XLI9g==", + "requires": { + "@google-cloud/common": "^3.0.0", + "@types/console-log-level": "^1.4.0", + "@types/semver": "^7.0.0", + "console-log-level": "^1.4.0", + "delay": "^4.0.1", + "extend": "^3.0.2", + "gcp-metadata": "^4.0.0", + "parse-duration": "^0.4.4", + "pprof": "3.0.0", + "pretty-ms": "^7.0.0", + "protobufjs": "~6.10.0", + "semver": "^7.0.0", + "teeny-request": "^7.0.0" + } + }, + "@google-cloud/projectify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", + "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==" + }, + "@google-cloud/promisify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" + }, + "@google-cloud/trace-agent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-5.1.1.tgz", + "integrity": "sha512-YTcK0RLN90pLCprg0XC8uV4oAVd79vsXhkcxmEVwiOOYjUDvSrAhb7y/0SY606zgfhJHmUTNb/fZSWEtZP/slQ==", + "requires": { + "@google-cloud/common": "^3.0.0", + "@opencensus/propagation-stackdriver": "0.0.22", + "builtin-modules": "^3.0.0", + "console-log-level": "^1.4.0", + "continuation-local-storage": "^3.2.1", + "extend": "^3.0.2", + "gcp-metadata": "^4.0.0", + "google-auth-library": "^6.0.0", + "hex2dec": "^1.0.1", + "is": "^3.2.0", + "methods": "^1.1.1", + "require-in-the-middle": "^5.0.0", + "semver": "^7.0.0", + "shimmer": "^1.2.0", + "source-map-support": "^0.5.16", + "uuid": "^8.0.0" + } + }, + "@opencensus/core": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.22.tgz", + "integrity": "sha512-ErazJtivjceNoOZI1bG9giQ6cWS45J4i6iPUtlp7dLNu58OLs/v+CD0FsaPCh47XgPxAI12vbBE8Ec09ViwHNA==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^7.0.0", + "shimmer": "^1.2.0", + "uuid": "^8.0.0" + } + }, + "@opencensus/propagation-stackdriver": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.22.tgz", + "integrity": "sha512-eBvf/ihb1mN8Yz/ASkz8nHzuMKqygu77+VNnUeR0yEh3Nj+ykB8VVR6lK+NAFXo1Rd1cOsTmgvuXAZgDAGleQQ==", + "requires": { + "@opencensus/core": "^0.0.22", + "hex2dec": "^1.0.1", + "uuid": "^8.0.0" + } + }, + "@types/node": { + "version": "13.13.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.33.tgz", + "integrity": "sha512-1B3GM1yuYsFyEvBb+ljBqWBOylsWDYioZ5wpu8AhXdIhq20neXS7eaSC8GkwHE0yQYGiOIV43lMsgRYTgKZefQ==" + }, + "@types/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" + }, + "acorn": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", + "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==" + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "coffeescript": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz", + "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==" + }, + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "gaxios": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.0.1.tgz", + "integrity": "sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "google-auth-library": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz", + "integrity": "sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "gtoken": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.1.0.tgz", + "integrity": "sha512-4d8N6Lk8TEAHl9vVoRVMh9BNOKWVgl2DdNtr3428O75r3QFrF/a5MMu851VmK0AA8+iSvbwRv69k5XnMLURGhg==", + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0", + "mime": "^2.2.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "parse-duration": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.4.4.tgz", + "integrity": "sha512-KbAJuYGUhZkB9gotDiKLnZ7Z3VTacK3fgwmDdB6ZVDtJbMBT6MfLga0WJaYpPDu0mzqT0NgHtHDt5PY4l0nidg==" + }, + "pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "requires": { + "parse-ms": "^2.1.0" + } + }, + "protobufjs": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", + "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "require-in-the-middle": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.0.3.tgz", + "integrity": "sha512-p/ICV8uMlqC4tjOYabLMxAWCIKa0YUQgZZ6KDM0xgXJNgdGQ1WmL2A07TwmrZw+wi6ITUFKzH5v3n+ENEyXVkA==", + "requires": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.12.0" + } + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + } + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + }, + "uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "@overleaf/o-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@overleaf/o-error/-/o-error-3.1.0.tgz", @@ -972,11 +739,6 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "@sindresorhus/is": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.15.0.tgz", - "integrity": "sha512-lu8BpxjAtRCAo5ifytTpCPCj99LF7o/2Myn+NXyNCBqvPYn7Pjd76AMmUB5l7XF1U6t0hcWrlEM5ESufW7wAeA==" - }, "@sinonjs/commons": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", @@ -1027,11 +789,6 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, - "@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" - }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1043,14 +800,6 @@ "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz", "integrity": "sha512-x+OscEQwcx5Biair4enH7ov9W+clcqUWaZRaxn5IkT4yNWWjRr2oiYDkY/x1uXSTVZOQ2xlbFQySaQGB+VdXGQ==" }, - "@types/duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-5zOA53RUlzN74bvrSGwjudssD9F3a797sDZQkiYpUOxW+WHaXTCPz4/d5Dgi6FKnOqZ2CpaTo0DhgIfsXAOE/A==", - "requires": { - "@types/node": "*" - } - }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -1081,39 +830,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz", "integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q==" }, - "@types/request": { - "version": "2.48.4", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.4.tgz", - "integrity": "sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==", - "requires": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" - }, - "dependencies": { - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - } - } - }, - "@types/semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" - }, - "@types/tough-cookie": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.6.tgz", - "integrity": "sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ==" - }, "@typescript-eslint/experimental-utils": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", @@ -1176,6 +892,11 @@ "through": ">=2.2.7 <3" } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -1211,7 +932,8 @@ "acorn": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true }, "acorn-jsx": { "version": "5.1.0", @@ -1286,6 +1008,20 @@ "picomatch": "^2.0.4" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1422,15 +1158,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, - "axios": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - } - }, "axobject-query": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz", @@ -1600,6 +1327,11 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "builtin-modules": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", @@ -1731,6 +1463,11 @@ "readdirp": "~3.2.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "cli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", @@ -1797,10 +1534,10 @@ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" }, - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha512-Tx8itEfCsQp8RbLDFt7qwjqXycAx2g6SI7//4PPUR2j6meLmNifYm6zKrNDcU1+Q/GWRhjhEZk7DaLG1TfIzGA==" + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "color-convert": { "version": "1.9.3", @@ -1831,11 +1568,65 @@ "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", "dev": true }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + }, + "dependencies": { + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + } + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, "console-log-level": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", @@ -1963,6 +1754,11 @@ "type-detect": "^4.0.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1988,6 +1784,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "denque": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", @@ -2003,6 +1804,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -2145,19 +1951,6 @@ "is-symbol": "^1.0.2" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "requires": { - "es6-promise": "^4.0.3" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2991,29 +2784,6 @@ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3039,11 +2809,18 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.1.2", @@ -3055,8 +2832,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -3064,6 +2840,54 @@ "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "gaxios": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz", @@ -3122,7 +2946,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3337,7 +3160,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -3371,6 +3193,11 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3453,6 +3280,14 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -3497,6 +3332,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, "inquirer": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", @@ -3643,7 +3483,8 @@ "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true }, "is-callable": { "version": "1.1.5", @@ -3651,6 +3492,14 @@ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "requires": { + "has": "^1.0.3" + } + }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -3962,11 +3811,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.pickby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", - "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==" - }, "lodash.unescape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", @@ -4098,15 +3942,6 @@ "yallist": "^3.0.2" } }, - "lynx": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", - "integrity": "sha512-JI52N0NwK2b/Md0TFPdPtUBI46kjyJXF7+q08l2yvQ56q6QA8s7ZjZQQRoxFpS2jDXNf/B0p8ID+OIKcTsZwzw==", - "requires": { - "mersenne": "~0.0.3", - "statsd-parser": "~0.0.4" - } - }, "make-plural": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", @@ -4169,11 +4004,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "mersenne": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", - "integrity": "sha512-XoSUL+nF8hMTKGQxUs8r3Btdsf1yuKKBdCCGbh3YXgCXuVKishpZv1CNc385w9s8t4Ynwc5h61BwW/FCVulkbg==" - }, "messageformat": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz", @@ -4202,28 +4032,6 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, - "metrics-sharelatex": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/metrics-sharelatex/-/metrics-sharelatex-2.6.2.tgz", - "integrity": "sha512-bOLfkSCexiPgB96hdXhoOWyvvrwscgjeZPEqdcJ7BTGxY59anzvymNf5hTGJ1RtS4sblDKxITw3L5a+gYKhRYQ==", - "requires": { - "@google-cloud/debug-agent": "^3.0.0", - "@google-cloud/profiler": "^0.2.3", - "@google-cloud/trace-agent": "^3.2.0", - "coffee-script": "1.6.0", - "lynx": "~0.1.1", - "prom-client": "^11.1.3", - "underscore": "~1.6.0", - "yn": "^3.1.1" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ==" - } - } - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -4252,7 +4060,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4262,6 +4069,38 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + } + } + }, "mocha": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", @@ -4483,7 +4322,8 @@ "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "optional": true }, "natural-compare": { "version": "1.4.0", @@ -4497,6 +4337,31 @@ "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "optional": true }, + "needle": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", + "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -4549,6 +4414,42 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz", "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==" }, + "node-pre-gyp": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.16.0.tgz", + "integrity": "sha512-4efGA+X/YXAHLi1hN8KaPrILULaUn2nWecFrn1k2I+99HpoyvcOGEbtcOxpDiUwPF2ZANMJDh32qwOUPenuR1g==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.3", + "needle": "^2.5.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4567,6 +4468,45 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -4575,8 +4515,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.7.0", @@ -4656,6 +4595,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4687,16 +4631,30 @@ "word-wrap": "~1.2.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } }, "p-limit": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, "requires": { "p-try": "^2.0.0" } @@ -4730,7 +4688,8 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true }, "parent-module": { "version": "1.0.1", @@ -4741,11 +4700,6 @@ "callsites": "^3.0.0" } }, - "parse-duration": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.1.2.tgz", - "integrity": "sha512-0qfMZyjOUFBeEIvJ5EayfXJqaEXxQ+Oj2b7tWJM3hvEXvXsYCk05EDVI23oYnEw2NaFYUWdABEVPBvBMh8L/pA==" - }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -4844,11 +4798,6 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -4858,6 +4807,73 @@ "find-up": "^2.1.0" } }, + "pprof": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pprof/-/pprof-3.0.0.tgz", + "integrity": "sha512-uPWbAhoH/zvq1kM3/Fd/wshb4D7sLlGap8t6uCTER4aZRWqqyPYgXzpjWbT0Unn5U25pEy2VREUu27nQ9o9VPA==", + "requires": { + "bindings": "^1.2.1", + "delay": "^4.0.1", + "findit2": "^2.2.3", + "nan": "^2.14.0", + "node-pre-gyp": "^0.16.0", + "p-limit": "^3.0.0", + "pify": "^5.0.0", + "protobufjs": "~6.10.0", + "source-map": "^0.7.3", + "split": "^1.0.1" + }, + "dependencies": { + "@types/node": { + "version": "13.13.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.33.tgz", + "integrity": "sha512-1B3GM1yuYsFyEvBb+ljBqWBOylsWDYioZ5wpu8AhXdIhq20neXS7eaSC8GkwHE0yQYGiOIV43lMsgRYTgKZefQ==" + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" + }, + "protobufjs": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", + "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -5497,14 +5513,6 @@ } } }, - "pretty-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-4.0.0.tgz", - "integrity": "sha512-qG66ahoLCwpLXD09ZPHSCbUWYTqdosB7SMP4OffgTgL2PBKXMuUsrk5Bwg8q4qPkjTXsKBMr+YK3Ltd/6F9s/Q==", - "requires": { - "parse-ms": "^2.0.0" - } - }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -5683,6 +5691,29 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + } + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5833,26 +5864,6 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "require-in-the-middle": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-4.0.1.tgz", - "integrity": "sha512-EfkM2zANyGkrfIExsECMeNn/uzjvHrE9h36yLXSavmrDiH4tgDNvltAmEKnt4PNLbqKPHZz+uszW2wTKrLUX0w==", - "requires": { - "debug": "^4.1.1", - "module-details-from-path": "^1.0.3", - "resolve": "^1.12.0" - }, - "dependencies": { - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, "require-like": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", @@ -5904,11 +5915,6 @@ "signal-exit": "^3.0.2" } }, - "retry-axios": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", - "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==" - }, "retry-request": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz", @@ -6087,8 +6093,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "setprototypeof": { "version": "1.1.1", @@ -6143,8 +6148,7 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==", - "dev": true + "integrity": "sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==" }, "sinon": { "version": "9.0.1", @@ -6217,6 +6221,15 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -6314,11 +6327,6 @@ "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" }, - "statsd-parser": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", - "integrity": "sha512-7XO+ur89EalMXXFQaydsczB8sclr5nDsNIoUu0IzJx1pIbHUhO3LtpSzBwetIuU9DyTLMiVaJBMtWS/Nb2KR4g==" - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -6499,6 +6507,20 @@ } } }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, "tdigest": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", @@ -6876,7 +6898,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, "requires": { "string-width": "^1.0.2 || 2" }, @@ -6884,20 +6905,17 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", - "dev": true + "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==" }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -6907,7 +6925,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -7111,6 +7128,11 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index b4fc076c96..77d65f3c04 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -18,6 +18,7 @@ "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" }, "dependencies": { + "@overleaf/metrics": "^3.4.1", "@overleaf/o-error": "^3.1.0", "@overleaf/redis-wrapper": "^2.0.0", "JSONStream": "^1.3.5", @@ -30,7 +31,6 @@ "heap": "^0.2.6", "line-reader": "^0.4.0", "logger-sharelatex": "^2.2.0", - "metrics-sharelatex": "^2.6.2", "mongo-uri": "^0.1.2", "mongodb": "^3.6.0", "redis": "~0.10.1", diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index 6bd9b903a2..55c53046a4 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -47,7 +47,7 @@ describe('MongoAWS', function () { './mongodb': { db: (this.db = {}), ObjectId }, JSONStream: (this.JSONStream = {}), 'readline-stream': (this.readline = sinon.stub()), - 'metrics-sharelatex': { inc() {} } + '@overleaf/metrics': { inc() {} } } }) diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index 43a51aaf57..70f3d6f4f5 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -26,7 +26,7 @@ describe('MongoManager', function () { requires: { './mongodb': { db: (this.db = {}), ObjectId }, './PackManager': (this.PackManager = {}), - 'metrics-sharelatex': { timeAsyncMethod() {} }, + '@overleaf/metrics': { timeAsyncMethod() {} }, 'logger-sharelatex': { log() {} } } }) diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index e045f72716..b0092f8df7 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -32,7 +32,7 @@ describe('PackManager', function () { './LockManager': {}, './MongoAWS': {}, 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, - 'metrics-sharelatex': { inc() {} }, + '@overleaf/metrics': { inc() {} }, './ProjectIterator': require('../../../../app/js/ProjectIterator.js'), // Cache for speed 'settings-sharelatex': { redis: { lock: { key_schema: {} } } From a25246106e795885106b32473a3c5b9d101f8f4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Dec 2020 07:36:01 +0000 Subject: [PATCH 529/549] build(deps): bump ini from 1.3.5 to 1.3.8 Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 217fcb653f..afec62a80c 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -3333,9 +3333,9 @@ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inquirer": { "version": "7.1.0", From a506b11245bb48a4eb86d67c71e863e033aa818b Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Wed, 6 Jan 2021 09:57:08 +0000 Subject: [PATCH 530/549] Update Node to 10.23.1 --- services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 2 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.yml | 4 ++-- services/track-changes/package-lock.json | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index c2f6421352..2baa2d433a 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -10.22.1 +10.23.1 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index f0e362fca0..2da67d2436 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -2,7 +2,7 @@ # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -FROM node:10.22.1 as base +FROM node:10.23.1 as base WORKDIR /app diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 1e7871e90d..205e7f63be 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -3,6 +3,6 @@ track-changes --docker-repos=gcr.io/overleaf-ops --env-add=AWS_BUCKET=bucket --env-pass-through= ---node-version=10.22.1 +--node-version=10.23.1 --public-repo=True --script-version=3.4.0 diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index fd76a1672f..e0afc49351 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.3" services: test_unit: - image: node:10.22.1 + image: node:10.23.1 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: node:10.22.1 + image: node:10.23.1 volumes: - .:/app working_dir: /app diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 217fcb653f..d912ad7c49 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -3781,12 +3781,12 @@ "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" }, "lodash.get": { "version": "4.4.2", @@ -4634,7 +4634,7 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==" }, "os-tmpdir": { "version": "1.0.2", @@ -5777,12 +5777,12 @@ "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" }, "redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "requires": { "redis-errors": "^1.0.0" } From 6bd5120dcf09d6a6ab49dcd2ba1f7699423e1366 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 16 Feb 2021 15:10:15 +0000 Subject: [PATCH 531/549] [misc] bump the version of the metrics module to 3.5.1 --- services/track-changes/package-lock.json | 1057 ++++++++++++++-------- services/track-changes/package.json | 2 +- 2 files changed, 699 insertions(+), 360 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 6349950fd5..fd8814cb0f 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -185,6 +185,197 @@ } } }, + "@google-cloud/debug-agent": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-5.1.3.tgz", + "integrity": "sha512-WbzeEz4MvPlM7DX2QBsPcWgF62u7LSQv/oMYPl0L+TddTebqjDKiVXwxpzWk61NIfcKiet3dyCbPIt3N5o8XPQ==", + "requires": { + "@google-cloud/common": "^3.0.0", + "acorn": "^8.0.0", + "coffeescript": "^2.0.0", + "console-log-level": "^1.4.0", + "extend": "^3.0.2", + "findit2": "^2.2.3", + "gcp-metadata": "^4.0.0", + "p-limit": "^3.0.1", + "semver": "^7.0.0", + "source-map": "^0.6.1", + "split": "^1.0.0" + }, + "dependencies": { + "@google-cloud/common": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz", + "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==", + "requires": { + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^6.1.1", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" + } + }, + "@google-cloud/projectify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", + "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==" + }, + "@google-cloud/promisify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" + }, + "acorn": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", + "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==" + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "gaxios": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz", + "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "google-auth-library": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz", + "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "gtoken": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", + "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "@google-cloud/logging": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-7.3.0.tgz", @@ -252,6 +443,211 @@ "extend": "^3.0.2" } }, + "@google-cloud/profiler": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.0.tgz", + "integrity": "sha512-9e1zXRctLSUHAoAsFGwE4rS28fr0siiG+jXl5OpwTK8ZAUlxb70aosHaZGdsv8YXrYKjuiufjRZ/OXCs0XLI9g==", + "requires": { + "@google-cloud/common": "^3.0.0", + "@types/console-log-level": "^1.4.0", + "@types/semver": "^7.0.0", + "console-log-level": "^1.4.0", + "delay": "^4.0.1", + "extend": "^3.0.2", + "gcp-metadata": "^4.0.0", + "parse-duration": "^0.4.4", + "pprof": "3.0.0", + "pretty-ms": "^7.0.0", + "protobufjs": "~6.10.0", + "semver": "^7.0.0", + "teeny-request": "^7.0.0" + }, + "dependencies": { + "@google-cloud/common": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz", + "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==", + "requires": { + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^6.1.1", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" + } + }, + "@google-cloud/projectify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", + "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==" + }, + "@google-cloud/promisify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" + }, + "@types/node": { + "version": "13.13.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.42.tgz", + "integrity": "sha512-g+w2QgbW7k2CWLOXzQXbO37a7v5P9ObPvYahKphdBLV5aqpbVZRhTpWCT0SMRqX1i30Aig791ZmIM2fJGL2S8A==" + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "gaxios": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz", + "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "google-auth-library": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz", + "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "gtoken": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", + "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "protobufjs": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", + "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "@google-cloud/projectify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-1.0.4.tgz", @@ -262,6 +658,229 @@ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.4.tgz", "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==" }, + "@google-cloud/trace-agent": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-5.1.3.tgz", + "integrity": "sha512-f+5DX7n6QpDlHA+4kr81z69SLAdrlvd9T8skqCMgnYvtXx14AwzXZyzEDf3jppOYzYoqPPJv8XYiyYHHmYD0BA==", + "requires": { + "@google-cloud/common": "^3.0.0", + "@opencensus/propagation-stackdriver": "0.0.22", + "builtin-modules": "^3.0.0", + "console-log-level": "^1.4.0", + "continuation-local-storage": "^3.2.1", + "extend": "^3.0.2", + "gcp-metadata": "^4.0.0", + "google-auth-library": "^7.0.0", + "hex2dec": "^1.0.1", + "is": "^3.2.0", + "methods": "^1.1.1", + "require-in-the-middle": "^5.0.0", + "semver": "^7.0.0", + "shimmer": "^1.2.0", + "source-map-support": "^0.5.16", + "uuid": "^8.0.0" + }, + "dependencies": { + "@google-cloud/common": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz", + "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==", + "requires": { + "@google-cloud/projectify": "^2.0.0", + "@google-cloud/promisify": "^2.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^6.1.1", + "retry-request": "^4.1.1", + "teeny-request": "^7.0.0" + }, + "dependencies": { + "google-auth-library": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.6.tgz", + "integrity": "sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + } + } + }, + "@google-cloud/projectify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", + "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==" + }, + "@google-cloud/promisify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", + "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" + }, + "@opencensus/core": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.22.tgz", + "integrity": "sha512-ErazJtivjceNoOZI1bG9giQ6cWS45J4i6iPUtlp7dLNu58OLs/v+CD0FsaPCh47XgPxAI12vbBE8Ec09ViwHNA==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^7.0.0", + "shimmer": "^1.2.0", + "uuid": "^8.0.0" + } + }, + "@opencensus/propagation-stackdriver": { + "version": "0.0.22", + "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.22.tgz", + "integrity": "sha512-eBvf/ihb1mN8Yz/ASkz8nHzuMKqygu77+VNnUeR0yEh3Nj+ykB8VVR6lK+NAFXo1Rd1cOsTmgvuXAZgDAGleQQ==", + "requires": { + "@opencensus/core": "^0.0.22", + "hex2dec": "^1.0.1", + "uuid": "^8.0.0" + } + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "duplexify": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", + "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "gaxios": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.1.0.tgz", + "integrity": "sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + } + }, + "gcp-metadata": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", + "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", + "requires": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + } + }, + "google-auth-library": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.0.2.tgz", + "integrity": "sha512-vjyNZR3pDLC0u7GHLfj+Hw9tGprrJwoMwkYGqURCXYITjCrP9HprOyxVV+KekdLgATtWGuDkQG2MTh0qpUPUgg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^4.0.0", + "gcp-metadata": "^4.2.0", + "gtoken": "^5.0.4", + "jws": "^4.0.0", + "lru-cache": "^6.0.0" + } + }, + "google-p12-pem": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", + "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "gtoken": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.2.1.tgz", + "integrity": "sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==", + "requires": { + "gaxios": "^4.0.0", + "google-p12-pem": "^3.0.3", + "jws": "^4.0.0" + } + }, + "json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "requires": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "@grpc/grpc-js": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.5.tgz", @@ -328,9 +947,9 @@ } }, "@overleaf/metrics": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@overleaf/metrics/-/metrics-3.4.1.tgz", - "integrity": "sha512-OgjlzuC+2gPdIEDHhmd9LDMu01tk1ln0cJhw1727BZ+Wgf2Z1hjuHRt4JeCkf+PFTHwJutVYT8v6IGPpNEPtbg==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@overleaf/metrics/-/metrics-3.5.1.tgz", + "integrity": "sha512-RLHxkMF7Y3725L3QwXo9cIn2gGobsMYUGuxKxg7PVMrPTMsomHEMeG7StOxCO7ML1Z/BwB/9nsVYNrsRdAJtKg==", "requires": { "@google-cloud/debug-agent": "^5.1.2", "@google-cloud/profiler": "^4.0.3", @@ -341,334 +960,10 @@ "yn": "^3.1.1" }, "dependencies": { - "@google-cloud/common": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-3.5.0.tgz", - "integrity": "sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ==", - "requires": { - "@google-cloud/projectify": "^2.0.0", - "@google-cloud/promisify": "^2.0.0", - "arrify": "^2.0.1", - "duplexify": "^4.1.1", - "ent": "^2.2.0", - "extend": "^3.0.2", - "google-auth-library": "^6.1.1", - "retry-request": "^4.1.1", - "teeny-request": "^7.0.0" - } - }, - "@google-cloud/debug-agent": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@google-cloud/debug-agent/-/debug-agent-5.1.3.tgz", - "integrity": "sha512-WbzeEz4MvPlM7DX2QBsPcWgF62u7LSQv/oMYPl0L+TddTebqjDKiVXwxpzWk61NIfcKiet3dyCbPIt3N5o8XPQ==", - "requires": { - "@google-cloud/common": "^3.0.0", - "acorn": "^8.0.0", - "coffeescript": "^2.0.0", - "console-log-level": "^1.4.0", - "extend": "^3.0.2", - "findit2": "^2.2.3", - "gcp-metadata": "^4.0.0", - "p-limit": "^3.0.1", - "semver": "^7.0.0", - "source-map": "^0.6.1", - "split": "^1.0.0" - } - }, - "@google-cloud/profiler": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@google-cloud/profiler/-/profiler-4.1.0.tgz", - "integrity": "sha512-9e1zXRctLSUHAoAsFGwE4rS28fr0siiG+jXl5OpwTK8ZAUlxb70aosHaZGdsv8YXrYKjuiufjRZ/OXCs0XLI9g==", - "requires": { - "@google-cloud/common": "^3.0.0", - "@types/console-log-level": "^1.4.0", - "@types/semver": "^7.0.0", - "console-log-level": "^1.4.0", - "delay": "^4.0.1", - "extend": "^3.0.2", - "gcp-metadata": "^4.0.0", - "parse-duration": "^0.4.4", - "pprof": "3.0.0", - "pretty-ms": "^7.0.0", - "protobufjs": "~6.10.0", - "semver": "^7.0.0", - "teeny-request": "^7.0.0" - } - }, - "@google-cloud/projectify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz", - "integrity": "sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ==" - }, - "@google-cloud/promisify": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", - "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" - }, - "@google-cloud/trace-agent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@google-cloud/trace-agent/-/trace-agent-5.1.1.tgz", - "integrity": "sha512-YTcK0RLN90pLCprg0XC8uV4oAVd79vsXhkcxmEVwiOOYjUDvSrAhb7y/0SY606zgfhJHmUTNb/fZSWEtZP/slQ==", - "requires": { - "@google-cloud/common": "^3.0.0", - "@opencensus/propagation-stackdriver": "0.0.22", - "builtin-modules": "^3.0.0", - "console-log-level": "^1.4.0", - "continuation-local-storage": "^3.2.1", - "extend": "^3.0.2", - "gcp-metadata": "^4.0.0", - "google-auth-library": "^6.0.0", - "hex2dec": "^1.0.1", - "is": "^3.2.0", - "methods": "^1.1.1", - "require-in-the-middle": "^5.0.0", - "semver": "^7.0.0", - "shimmer": "^1.2.0", - "source-map-support": "^0.5.16", - "uuid": "^8.0.0" - } - }, - "@opencensus/core": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.22.tgz", - "integrity": "sha512-ErazJtivjceNoOZI1bG9giQ6cWS45J4i6iPUtlp7dLNu58OLs/v+CD0FsaPCh47XgPxAI12vbBE8Ec09ViwHNA==", - "requires": { - "continuation-local-storage": "^3.2.1", - "log-driver": "^1.2.7", - "semver": "^7.0.0", - "shimmer": "^1.2.0", - "uuid": "^8.0.0" - } - }, - "@opencensus/propagation-stackdriver": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.22.tgz", - "integrity": "sha512-eBvf/ihb1mN8Yz/ASkz8nHzuMKqygu77+VNnUeR0yEh3Nj+ykB8VVR6lK+NAFXo1Rd1cOsTmgvuXAZgDAGleQQ==", - "requires": { - "@opencensus/core": "^0.0.22", - "hex2dec": "^1.0.1", - "uuid": "^8.0.0" - } - }, - "@types/node": { - "version": "13.13.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.33.tgz", - "integrity": "sha512-1B3GM1yuYsFyEvBb+ljBqWBOylsWDYioZ5wpu8AhXdIhq20neXS7eaSC8GkwHE0yQYGiOIV43lMsgRYTgKZefQ==" - }, - "@types/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" - }, - "acorn": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", - "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==" - }, - "bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" - }, - "coffeescript": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz", - "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==" - }, - "duplexify": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", - "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", - "requires": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.0" - } - }, - "gaxios": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.0.1.tgz", - "integrity": "sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ==", - "requires": { - "abort-controller": "^3.0.0", - "extend": "^3.0.2", - "https-proxy-agent": "^5.0.0", - "is-stream": "^2.0.0", - "node-fetch": "^2.3.0" - } - }, - "gcp-metadata": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz", - "integrity": "sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==", - "requires": { - "gaxios": "^4.0.0", - "json-bigint": "^1.0.0" - } - }, - "google-auth-library": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-6.1.3.tgz", - "integrity": "sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g==", - "requires": { - "arrify": "^2.0.0", - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "fast-text-encoding": "^1.0.0", - "gaxios": "^4.0.0", - "gcp-metadata": "^4.2.0", - "gtoken": "^5.0.4", - "jws": "^4.0.0", - "lru-cache": "^6.0.0" - } - }, - "google-p12-pem": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.0.3.tgz", - "integrity": "sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==", - "requires": { - "node-forge": "^0.10.0" - } - }, - "gtoken": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.1.0.tgz", - "integrity": "sha512-4d8N6Lk8TEAHl9vVoRVMh9BNOKWVgl2DdNtr3428O75r3QFrF/a5MMu851VmK0AA8+iSvbwRv69k5XnMLURGhg==", - "requires": { - "gaxios": "^4.0.0", - "google-p12-pem": "^3.0.3", - "jws": "^4.0.0", - "mime": "^2.2.0" - } - }, - "json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "requires": { - "bignumber.js": "^9.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "mime": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", - "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" - }, - "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "parse-duration": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.4.4.tgz", - "integrity": "sha512-KbAJuYGUhZkB9gotDiKLnZ7Z3VTacK3fgwmDdB6ZVDtJbMBT6MfLga0WJaYpPDu0mzqT0NgHtHDt5PY4l0nidg==" - }, - "pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", - "requires": { - "parse-ms": "^2.1.0" - } - }, - "protobufjs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", - "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": "^13.7.0", - "long": "^4.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "require-in-the-middle": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.0.3.tgz", - "integrity": "sha512-p/ICV8uMlqC4tjOYabLMxAWCIKa0YUQgZZ6KDM0xgXJNgdGQ1WmL2A07TwmrZw+wi6ITUFKzH5v3n+ENEyXVkA==", - "requires": { - "debug": "^4.1.1", - "module-details-from-path": "^1.0.3", - "resolve": "^1.12.0" - } - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "teeny-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", - "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", - "requires": { - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.1", - "stream-events": "^1.0.5", - "uuid": "^8.0.0" - } - }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" - }, - "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -830,6 +1125,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz", "integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q==" }, + "@types/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" + }, "@typescript-eslint/experimental-utils": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", @@ -1229,7 +1529,7 @@ "bintrees": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", - "integrity": "sha512-tbaUB1QpTIj4cKY8c1rvNAvEQXA+ekzHmbe4jzNfW3QWsF9GnnP/BRWyl6/qqS53heoYJ93naaFcm/jooONH8g==" + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" }, "bl": { "version": "2.2.1", @@ -1333,9 +1633,9 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "builtin-modules": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==" }, "bunyan": { "version": "2.0.2", @@ -1539,6 +1839,11 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "coffeescript": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz", + "integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1577,9 +1882,9 @@ }, "dependencies": { "mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" } } }, @@ -1775,9 +2080,9 @@ } }, "delay": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz", - "integrity": "sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA==" + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/delay/-/delay-4.4.1.tgz", + "integrity": "sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==" }, "delayed-stream": { "version": "1.0.0", @@ -2722,7 +3027,7 @@ "findit2": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", - "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==" + "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" }, "flat": { "version": "4.1.0", @@ -3493,9 +3798,9 @@ "dev": true }, "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "requires": { "has": "^1.0.3" } @@ -3781,12 +4086,12 @@ "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, "lodash.get": { "version": "4.4.2", @@ -4260,7 +4565,7 @@ "module-details-from-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" }, "moment": { "version": "2.24.0", @@ -4338,9 +4643,9 @@ "optional": true }, "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", + "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", "requires": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -4634,7 +4939,7 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==" + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", @@ -4700,6 +5005,11 @@ "callsites": "^3.0.0" } }, + "parse-duration": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-0.4.4.tgz", + "integrity": "sha512-KbAJuYGUhZkB9gotDiKLnZ7Z3VTacK3fgwmDdB6ZVDtJbMBT6MfLga0WJaYpPDu0mzqT0NgHtHDt5PY4l0nidg==" + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -4798,6 +5108,11 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" + }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -4825,9 +5140,9 @@ }, "dependencies": { "@types/node": { - "version": "13.13.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.33.tgz", - "integrity": "sha512-1B3GM1yuYsFyEvBb+ljBqWBOylsWDYioZ5wpu8AhXdIhq20neXS7eaSC8GkwHE0yQYGiOIV43lMsgRYTgKZefQ==" + "version": "13.13.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.42.tgz", + "integrity": "sha512-g+w2QgbW7k2CWLOXzQXbO37a7v5P9ObPvYahKphdBLV5aqpbVZRhTpWCT0SMRqX1i30Aig791ZmIM2fJGL2S8A==" }, "nan": { "version": "2.14.2", @@ -4842,11 +5157,6 @@ "yocto-queue": "^0.1.0" } }, - "pify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", - "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" - }, "protobufjs": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", @@ -5513,6 +5823,14 @@ } } }, + "pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "requires": { + "parse-ms": "^2.1.0" + } + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -5777,12 +6095,12 @@ "redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" }, "redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", "requires": { "redis-errors": "^1.0.0" } @@ -5864,6 +6182,27 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-in-the-middle": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz", + "integrity": "sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ==", + "requires": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.12.0" + }, + "dependencies": { + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, "require-like": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", @@ -6524,7 +6863,7 @@ "tdigest": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", - "integrity": "sha512-CXcDY/NIgIbKZPx5H4JJNpq6JwJhU5Z4+yWj4ZghDc7/9nVajiRlPPyMXRePPPlBfcayUqtoCXjo7/Hm82ecUA==", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", "requires": { "bintrees": "1.0.1" } diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 77d65f3c04..2aa031f310 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -18,7 +18,7 @@ "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" }, "dependencies": { - "@overleaf/metrics": "^3.4.1", + "@overleaf/metrics": "^3.5.1", "@overleaf/o-error": "^3.1.0", "@overleaf/redis-wrapper": "^2.0.0", "JSONStream": "^1.3.5", From f411049b825b7bf377b59d60021797c039cfc9d7 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 23 Feb 2021 13:57:27 +0000 Subject: [PATCH 532/549] [misc] add a new endpoint for exporting all the project history --- services/track-changes/app.js | 1 + .../track-changes/app/js/HttpController.js | 61 +++++++++++++++++++ .../track-changes/app/js/UpdatesManager.js | 36 +++++++++++ .../acceptance/js/ArchivingUpdatesTests.js | 46 +++++++++++++- .../test/acceptance/js/ExportProjectTests.js | 29 +++++++++ .../js/helpers/TrackChangesClient.js | 11 ++++ 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 services/track-changes/test/acceptance/js/ExportProjectTests.js diff --git a/services/track-changes/app.js b/services/track-changes/app.js index b9ca5120ce..9dd8f9440b 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -64,6 +64,7 @@ app.get('/project/:project_id/doc/:doc_id/diff', HttpController.getDiff) app.get('/project/:project_id/doc/:doc_id/check', HttpController.checkDoc) app.get('/project/:project_id/updates', HttpController.getUpdates) +app.get('/project/:project_id/export', HttpController.exportProject) app.post('/project/:project_id/flush', HttpController.flushProject) diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js index c167bae1c9..54191fd909 100644 --- a/services/track-changes/app/js/HttpController.js +++ b/services/track-changes/app/js/HttpController.js @@ -200,6 +200,67 @@ module.exports = HttpController = { ) }, + exportProject(req, res, next) { + // The project history can be huge: + // - updates can weight MBs for insert/delete of full doc + // - multiple updates form a pack + // Flush updates per pack onto the wire. + const { project_id } = req.params + logger.log({ project_id }, 'exporting project history') + UpdatesManager.exportProject(project_id, function ( + err, + updates, + confirmWrite + ) { + const abortStreaming = req.aborted || res.finished || res.destroyed + if (abortStreaming) { + // Tell the producer to stop emitting data + if (confirmWrite) confirmWrite(new Error('stop')) + return + } + const hasStartedStreamingResponse = res.headersSent + if (err) { + logger.error({ project_id, err }, 'export failed') + if (!hasStartedStreamingResponse) { + // Generate a nice 500 + return next(err) + } else { + // Stop streaming + return res.destroy() + } + } + // Compose the response incrementally + const isFirstWrite = !hasStartedStreamingResponse + const isLastWrite = updates.length === 0 + if (isFirstWrite) { + // The first write will emit the 200 status, headers and start of the + // response payload (open array) + res.setHeader('Content-Type', 'application/json') + res.writeHead(200) + res.write('[') + } + if (!isFirstWrite && !isLastWrite) { + // Starting from the 2nd non-empty write, emit a continuing comma. + // write 1: [updates1 + // write 2: ,updates2 + // write 3: ,updates3 + // write N: ] + res.write(',') + } + + // Every write will emit a blob onto the response stream: + // '[update1,update2,...]' + // ^^^^^^^^^^^^^^^^^^^ + res.write(JSON.stringify(updates).slice(1, -1), confirmWrite) + + if (isLastWrite) { + // The last write will have no updates and will finish the response + // payload (close array). + res.end(']') + } + }) + }, + restore(req, res, next) { if (next == null) { next = function (error) {} diff --git a/services/track-changes/app/js/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js index 811e9ba1ca..8e0e2dc569 100644 --- a/services/track-changes/app/js/UpdatesManager.js +++ b/services/track-changes/app/js/UpdatesManager.js @@ -631,6 +631,42 @@ module.exports = UpdatesManager = { ) }, + exportProject(projectId, consumer) { + // Flush anything before collecting updates. + UpdatesManager.processUncompressedUpdatesForProject(projectId, (err) => { + if (err) return consumer(err) + + // Fetch all the packs. + const before = undefined + PackManager.makeProjectIterator(projectId, before, (err, iterator) => { + if (err) return consumer(err) + + async.whilst( + () => !iterator.done(), + + (cb) => + iterator.next((err, updatesFromASinglePack) => { + if (err) return cb(err) + + if (updatesFromASinglePack.length === 0) { + // This should not happen when `iterator.done() == false`. + // Emitting an empty array would signal the consumer the final + // call. + return cb() + } + // Emit updates and wait for the consumer. + consumer(null, updatesFromASinglePack, cb) + }), + + (err) => { + if (err) return consumer(err) + consumer(null, []) + } + ) + }) + }) + }, + fetchUserInfo(users, callback) { if (callback == null) { callback = function (error, fetchedUserInfo) {} diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index 5df0844409..d082634573 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -141,6 +141,48 @@ describe('Archiving updates', function () { ) }) + function testExportFeature() { + describe('exporting the project', function () { + before('fetch export', function (done) { + TrackChangesClient.exportProject(this.project_id, (error, updates) => { + if (error) { + return done(error) + } + this.exportedUpdates = updates + done() + }) + }) + + it('should include all the imported updates, with ids, sorted by timestamp', function () { + // Add a safe guard for an empty array matching an empty export. + expect(this.updates).to.have.length(1024 + 22) + + const expectedExportedUpdates = this.updates + .slice() + .reverse() + .map((update) => { + // clone object, updates are created once in before handler + const exportedUpdate = Object.assign({}, update) + exportedUpdate.meta = Object.assign({}, update.meta) + + exportedUpdate.doc_id = this.doc_id + exportedUpdate.project_id = this.project_id + + // This is for merged updates, which does not apply here. + exportedUpdate.meta.start_ts = exportedUpdate.meta.end_ts = + exportedUpdate.meta.ts + delete exportedUpdate.meta.ts + return exportedUpdate + }) + expect(this.exportedUpdates).to.deep.equal(expectedExportedUpdates) + }) + }) + } + + describe("before archiving a doc's updates", function () { + testExportFeature() + }) + describe("archiving a doc's updates", function () { before(function (done) { TrackChangesClient.pushDocHistory( @@ -219,7 +261,7 @@ describe('Archiving updates', function () { ) }) - return it('should store 1024 doc changes in S3 in one pack', function (done) { + it('should store 1024 doc changes in S3 in one pack', function (done) { return db.docHistoryIndex.findOne( { _id: ObjectId(this.doc_id) }, (error, index) => { @@ -240,6 +282,8 @@ describe('Archiving updates', function () { } ) }) + + testExportFeature() }) return describe("unarchiving a doc's updates", function () { diff --git a/services/track-changes/test/acceptance/js/ExportProjectTests.js b/services/track-changes/test/acceptance/js/ExportProjectTests.js new file mode 100644 index 0000000000..d2958d7fc0 --- /dev/null +++ b/services/track-changes/test/acceptance/js/ExportProjectTests.js @@ -0,0 +1,29 @@ +const { expect } = require('chai') +const { ObjectId } = require('../../../app/js/mongodb') + +const TrackChangesApp = require('./helpers/TrackChangesApp') +const TrackChangesClient = require('./helpers/TrackChangesClient') + +describe('ExportProject', function () { + before('start app', function (done) { + TrackChangesApp.ensureRunning(done) + }) + + describe('when there are no updates', function () { + before('fetch export', function (done) { + TrackChangesClient.exportProject(ObjectId(), (error, updates) => { + if (error) { + return done(error) + } + this.exportedUpdates = updates + done() + }) + }) + + it('should export an empty array', function () { + expect(this.exportedUpdates).to.deep.equal([]) + }) + }) + + // see ArchivingUpdatesTests for tests with data in mongo/s3 +}) diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index 9e8e05bbc4..aa25839341 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -165,6 +165,17 @@ module.exports = TrackChangesClient = { ) }, + exportProject(project_id, callback) { + request.get( + { url: `http://localhost:3015/project/${project_id}/export`, json: true }, + (error, response, updates) => { + if (error) return callback(error) + response.statusCode.should.equal(200) + callback(null, updates) + } + ) + }, + restoreDoc(project_id, doc_id, version, user_id, callback) { if (callback == null) { callback = function (error) {} From ed70b99d8aa0e0863b2b3db71d9c6116a53043ae Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 25 Feb 2021 09:52:54 +0000 Subject: [PATCH 533/549] [misc] exportProject: collect and send user ids of updates in trailer --- .../track-changes/app/js/HttpController.js | 6 +++-- .../track-changes/app/js/UpdatesManager.js | 19 +++++++++++++-- .../acceptance/js/ArchivingUpdatesTests.js | 23 +++++++++++++------ .../test/acceptance/js/ExportProjectTests.js | 17 +++++++++----- .../js/helpers/TrackChangesClient.js | 2 +- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js index 54191fd909..c5db48210a 100644 --- a/services/track-changes/app/js/HttpController.js +++ b/services/track-changes/app/js/HttpController.js @@ -209,7 +209,7 @@ module.exports = HttpController = { logger.log({ project_id }, 'exporting project history') UpdatesManager.exportProject(project_id, function ( err, - updates, + { updates, userIds }, confirmWrite ) { const abortStreaming = req.aborted || res.finished || res.destroyed @@ -236,6 +236,7 @@ module.exports = HttpController = { // The first write will emit the 200 status, headers and start of the // response payload (open array) res.setHeader('Content-Type', 'application/json') + res.setHeader('Trailer', 'X-User-Ids') res.writeHead(200) res.write('[') } @@ -255,7 +256,8 @@ module.exports = HttpController = { if (isLastWrite) { // The last write will have no updates and will finish the response - // payload (close array). + // payload (close array) and emit the userIds as trailer. + res.addTrailers({ 'X-User-Ids': JSON.stringify(userIds) }) res.end(']') } }) diff --git a/services/track-changes/app/js/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js index 8e0e2dc569..7890208788 100644 --- a/services/track-changes/app/js/UpdatesManager.js +++ b/services/track-changes/app/js/UpdatesManager.js @@ -641,6 +641,8 @@ module.exports = UpdatesManager = { PackManager.makeProjectIterator(projectId, before, (err, iterator) => { if (err) return consumer(err) + const accumulatedUserIds = new Set() + async.whilst( () => !iterator.done(), @@ -654,13 +656,26 @@ module.exports = UpdatesManager = { // call. return cb() } + updatesFromASinglePack.forEach((update) => { + accumulatedUserIds.add( + // Super defensive access on update details. + String(update && update.meta && update.meta.user_id) + ) + }) // Emit updates and wait for the consumer. - consumer(null, updatesFromASinglePack, cb) + consumer(null, { updates: updatesFromASinglePack }, cb) }), (err) => { if (err) return consumer(err) - consumer(null, []) + + // Adding undefined can happen for broken updates. + accumulatedUserIds.delete('undefined') + + consumer(null, { + updates: [], + userIds: Array.from(accumulatedUserIds).sort() + }) } ) }) diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index d082634573..bb1024389c 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -50,6 +50,7 @@ describe('Archiving updates', function () { this.now = Date.now() this.to = this.now this.user_id = ObjectId().toString() + this.user_id_2 = ObjectId().toString() this.doc_id = ObjectId().toString() this.project_id = ObjectId().toString() @@ -92,7 +93,7 @@ describe('Archiving updates', function () { op: [{ i: 'b', p: 0 }], meta: { ts: this.now + (i - 2048) * this.hours + 10 * this.minutes, - user_id: this.user_id + user_id: this.user_id_2 }, v: 2 * i + 2 }) @@ -144,13 +145,17 @@ describe('Archiving updates', function () { function testExportFeature() { describe('exporting the project', function () { before('fetch export', function (done) { - TrackChangesClient.exportProject(this.project_id, (error, updates) => { - if (error) { - return done(error) + TrackChangesClient.exportProject( + this.project_id, + (error, updates, userIds) => { + if (error) { + return done(error) + } + this.exportedUpdates = updates + this.exportedUserIds = userIds + done() } - this.exportedUpdates = updates - done() - }) + ) }) it('should include all the imported updates, with ids, sorted by timestamp', function () { @@ -175,6 +180,10 @@ describe('Archiving updates', function () { return exportedUpdate }) expect(this.exportedUpdates).to.deep.equal(expectedExportedUpdates) + expect(this.exportedUserIds).to.deep.equal([ + this.user_id, + this.user_id_2 + ]) }) }) } diff --git a/services/track-changes/test/acceptance/js/ExportProjectTests.js b/services/track-changes/test/acceptance/js/ExportProjectTests.js index d2958d7fc0..b6ca106a60 100644 --- a/services/track-changes/test/acceptance/js/ExportProjectTests.js +++ b/services/track-changes/test/acceptance/js/ExportProjectTests.js @@ -11,17 +11,22 @@ describe('ExportProject', function () { describe('when there are no updates', function () { before('fetch export', function (done) { - TrackChangesClient.exportProject(ObjectId(), (error, updates) => { - if (error) { - return done(error) + TrackChangesClient.exportProject( + ObjectId(), + (error, updates, userIds) => { + if (error) { + return done(error) + } + this.exportedUpdates = updates + this.exportedUserIds = userIds + done() } - this.exportedUpdates = updates - done() - }) + ) }) it('should export an empty array', function () { expect(this.exportedUpdates).to.deep.equal([]) + expect(this.exportedUserIds).to.deep.equal([]) }) }) diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index aa25839341..f20884448d 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -171,7 +171,7 @@ module.exports = TrackChangesClient = { (error, response, updates) => { if (error) return callback(error) response.statusCode.should.equal(200) - callback(null, updates) + callback(null, updates, JSON.parse(response.trailers['x-user-ids'])) } ) }, From a793ecd41b47c139ff1f505e848a6159f397522a Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Tue, 23 Mar 2021 15:08:32 -0400 Subject: [PATCH 534/549] Add global test setup Configure chai and SandboxedModule in a central place. Configure SandboxedModule globals that are required in Node 12. --- services/track-changes/.mocharc.json | 3 +++ .../acceptance/js/AppendingUpdatesTests.js | 4 +--- .../acceptance/js/ArchivingUpdatesTests.js | 4 +--- .../acceptance/js/FlushingUpdatesTests.js | 4 +--- .../test/acceptance/js/GettingADiffTests.js | 4 +--- .../test/acceptance/js/GettingUpdatesTests.js | 4 +--- .../test/acceptance/js/LockManagerTests.js | 4 +--- .../test/acceptance/js/RestoringVersions.js | 4 +--- .../acceptance/js/helpers/TrackChangesApp.js | 1 - services/track-changes/test/setup.js | 21 +++++++++++++++++++ .../js/DiffGenerator/DiffGeneratorTests.js | 10 ++------- .../unit/js/DiffManager/DiffManagerTests.js | 9 +------- .../test/unit/js/DocArchive/MongoAWS.js | 7 ------- .../DocumentUpdaterManagerTests.js | 8 +------ .../js/HttpController/HttpControllerTests.js | 5 +---- .../unit/js/LockManager/LockManagerTests.js | 7 ++----- .../unit/js/MongoManager/MongoManagerTests.js | 7 ++----- .../unit/js/PackManager/PackManagerTests.js | 6 +----- .../unit/js/RedisManager/RedisManagerTests.js | 4 +--- .../js/RestoreManager/RestoreManagerTests.js | 8 +------ .../UpdateCompressor/UpdateCompressorTests.js | 4 +--- .../js/UpdateTrimmer/UpdateTrimmerTests.js | 8 +------ .../js/UpdatesManager/UpdatesManagerTests.js | 5 +---- .../js/WebApiManager/WebApiManagerTests.js | 8 +------ 24 files changed, 47 insertions(+), 102 deletions(-) create mode 100644 services/track-changes/.mocharc.json create mode 100644 services/track-changes/test/setup.js diff --git a/services/track-changes/.mocharc.json b/services/track-changes/.mocharc.json new file mode 100644 index 0000000000..dc3280aa96 --- /dev/null +++ b/services/track-changes/.mocharc.json @@ -0,0 +1,3 @@ +{ + "require": "test/setup.js" +} diff --git a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js index 4cf250cfac..def0bdfa1d 100644 --- a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js @@ -11,9 +11,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const request = require('request') diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index bb1024389c..a17ad6f1de 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -15,9 +15,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const { db, ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const request = require('request') diff --git a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js index a48abcc976..c9adeb5fd8 100644 --- a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js @@ -11,9 +11,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') const request = require('request') diff --git a/services/track-changes/test/acceptance/js/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js index 926d005164..9ccc2cca36 100644 --- a/services/track-changes/test/acceptance/js/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/js/GettingADiffTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') diff --git a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js index 83bd985241..f216d987a4 100644 --- a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js @@ -11,9 +11,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') diff --git a/services/track-changes/test/acceptance/js/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js index b6f8908554..7062a83237 100644 --- a/services/track-changes/test/acceptance/js/LockManagerTests.js +++ b/services/track-changes/test/acceptance/js/LockManagerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const Settings = require('settings-sharelatex') const LockManager = require('../../../app/js/LockManager') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js index d4d5d60304..a947dc09df 100644 --- a/services/track-changes/test/acceptance/js/RestoringVersions.js +++ b/services/track-changes/test/acceptance/js/RestoringVersions.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -chai.should() -const { expect } = chai +const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') const Settings = require('settings-sharelatex') diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js index 6ef1127f06..861dcdde88 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js @@ -14,7 +14,6 @@ */ const app = require('../../../../app') const { waitForDb } = require('../../../../app/js/mongodb') -require('logger-sharelatex') const logger = require('logger-sharelatex') const Settings = require('settings-sharelatex') diff --git a/services/track-changes/test/setup.js b/services/track-changes/test/setup.js new file mode 100644 index 0000000000..9ebe599cea --- /dev/null +++ b/services/track-changes/test/setup.js @@ -0,0 +1,21 @@ +const chai = require('chai') +const SandboxedModule = require('sandboxed-module') + +// Chai configuration +chai.should() + +// SandboxedModule configuration +SandboxedModule.configure({ + requires: { + 'logger-sharelatex': { + debug() {}, + log() {}, + info() {}, + warn() {}, + err() {}, + error() {}, + fatal() {} + } + }, + globals: { Buffer, JSON, console, process } +}) diff --git a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js index 6526c9445d..75a8ab2c0f 100644 --- a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js +++ b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js @@ -11,19 +11,13 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/DiffGenerator.js' const SandboxedModule = require('sandboxed-module') describe('DiffGenerator', function () { beforeEach(function () { - this.DiffGenerator = SandboxedModule.require(modulePath, { - requires: { - 'logger-sharelatex': { warn: sinon.stub() } - } - }) + this.DiffGenerator = SandboxedModule.require(modulePath, {}) this.ts = Date.now() this.user_id = 'mock-user-id' this.user_id_2 = 'mock-user-id-2' diff --git a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js index 7b72f3a002..b2f4ec6a59 100644 --- a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js @@ -11,9 +11,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/DiffManager.js' const SandboxedModule = require('sandboxed-module') @@ -21,11 +19,6 @@ describe('DiffManager', function () { beforeEach(function () { this.DiffManager = SandboxedModule.require(modulePath, { requires: { - 'logger-sharelatex': (this.logger = { - log: sinon.stub(), - error: sinon.stub(), - warn: sinon.stub() - }), './UpdatesManager': (this.UpdatesManager = {}), './DocumentUpdaterManager': (this.DocumentUpdaterManager = {}), './DiffGenerator': (this.DiffGenerator = {}) diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index 55c53046a4..dd4374e965 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -9,8 +9,6 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const chai = require('chai') -chai.should() const sinon = require('sinon') const modulePath = '../../../../app/js/MongoAWS.js' const SandboxedModule = require('sandboxed-module') @@ -36,11 +34,6 @@ describe('MongoAWS', function () { }), child_process: (this.child_process = {}), 'mongo-uri': (this.mongouri = {}), - 'logger-sharelatex': (this.logger = { - log: sinon.stub(), - error: sinon.stub(), - err() {} - }), 'aws-sdk': (this.awssdk = {}), fs: (this.fs = {}), 's3-streams': (this.S3S = {}), diff --git a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index 83f4bf04ad..2825a10fb5 100644 --- a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/DocumentUpdaterManager.js' const SandboxedModule = require('sandboxed-module') @@ -21,10 +19,6 @@ describe('DocumentUpdaterManager', function () { this.DocumentUpdaterManager = SandboxedModule.require(modulePath, { requires: { request: (this.request = {}), - 'logger-sharelatex': (this.logger = { - log: sinon.stub(), - error: sinon.stub() - }), 'settings-sharelatex': (this.settings = { apis: { documentupdater: { url: 'http://example.com' } } }) diff --git a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js index 08509f6b28..9746213922 100644 --- a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/HttpController.js' const SandboxedModule = require('sandboxed-module') @@ -21,7 +19,6 @@ describe('HttpController', function () { this.HttpController = SandboxedModule.require(modulePath, { singleOnly: true, requires: { - 'logger-sharelatex': { log: sinon.stub() }, './UpdatesManager': (this.UpdatesManager = {}), './DiffManager': (this.DiffManager = {}), './RestoreManager': (this.RestoreManager = {}), diff --git a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js index 42ee6dcd62..fc26997574 100644 --- a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js @@ -16,9 +16,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/LockManager.js' const SandboxedModule = require('sandboxed-module') @@ -36,8 +34,7 @@ describe('LockManager', function () { return (this.rclient = { auth: sinon.stub() }) } }, - 'settings-sharelatex': this.Settings, - 'logger-sharelatex': { error() {} } + 'settings-sharelatex': this.Settings } }) diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index 70f3d6f4f5..d96b27d8d0 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/MongoManager.js' const packModulePath = '../../../../app/js/PackManager.js' const SandboxedModule = require('sandboxed-module') @@ -26,8 +24,7 @@ describe('MongoManager', function () { requires: { './mongodb': { db: (this.db = {}), ObjectId }, './PackManager': (this.PackManager = {}), - '@overleaf/metrics': { timeAsyncMethod() {} }, - 'logger-sharelatex': { log() {} } + '@overleaf/metrics': { timeAsyncMethod() {} } } }) this.callback = sinon.stub() diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index b0092f8df7..957c22aac8 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -11,10 +11,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const { assert } = require('chai') -const should = chai.should() -const { expect } = chai +const { assert, expect } = require('chai') const modulePath = '../../../../app/js/PackManager.js' const SandboxedModule = require('sandboxed-module') const { ObjectId } = require('mongodb') @@ -31,7 +28,6 @@ describe('PackManager', function () { './mongodb': { db: (this.db = {}), ObjectId }, './LockManager': {}, './MongoAWS': {}, - 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, '@overleaf/metrics': { inc() {} }, './ProjectIterator': require('../../../../app/js/ProjectIterator.js'), // Cache for speed 'settings-sharelatex': { diff --git a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js index f3185daf08..13fa467570 100644 --- a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js @@ -12,9 +12,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/RedisManager.js' const SandboxedModule = require('sandboxed-module') diff --git a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js index 7e9c6896b0..dd664a6003 100644 --- a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js +++ b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/RestoreManager.js' const SandboxedModule = require('sandboxed-module') @@ -20,10 +18,6 @@ describe('RestoreManager', function () { beforeEach(function () { this.RestoreManager = SandboxedModule.require(modulePath, { requires: { - 'logger-sharelatex': (this.logger = { - log: sinon.stub(), - error: sinon.stub() - }), './DocumentUpdaterManager': (this.DocumentUpdaterManager = {}), './DiffManager': (this.DiffManager = {}) } diff --git a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js index 110b8ed1c8..a703ae8084 100644 --- a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js +++ b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/UpdateCompressor.js' const SandboxedModule = require('sandboxed-module') diff --git a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js index 5623954230..2d89a3520d 100644 --- a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js +++ b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/UpdateTrimmer.js' const SandboxedModule = require('sandboxed-module') const tk = require('timekeeper') @@ -24,10 +22,6 @@ describe('UpdateTrimmer', function () { this.UpdateTrimmer = SandboxedModule.require(modulePath, { requires: { - 'logger-sharelatex': (this.logger = { - log: sinon.stub(), - error: sinon.stub() - }), './WebApiManager': (this.WebApiManager = {}), './MongoManager': (this.MongoManager = {}) } diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index ed7c4b064e..b90d1567c8 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -14,9 +14,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const { ObjectId } = require('mongodb') const modulePath = '../../../../app/js/UpdatesManager.js' const SandboxedModule = require('sandboxed-module') @@ -34,7 +32,6 @@ describe('UpdatesManager', function () { './WebApiManager': (this.WebApiManager = {}), './UpdateTrimmer': (this.UpdateTrimmer = {}), './DocArchiveManager': (this.DocArchiveManager = {}), - 'logger-sharelatex': { log: sinon.stub(), error: sinon.stub() }, 'settings-sharelatex': { redis: { lock: { diff --git a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js index 5aca308be2..7e23e72b0a 100644 --- a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js @@ -10,9 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ const sinon = require('sinon') -const chai = require('chai') -const should = chai.should() -const { expect } = chai +const { expect } = require('chai') const modulePath = '../../../../app/js/WebApiManager.js' const SandboxedModule = require('sandboxed-module') @@ -21,10 +19,6 @@ describe('WebApiManager', function () { this.WebApiManager = SandboxedModule.require(modulePath, { requires: { requestretry: (this.request = {}), - 'logger-sharelatex': (this.logger = { - log: sinon.stub(), - error: sinon.stub() - }), 'settings-sharelatex': (this.settings = { apis: { web: { From 9b009efd4615753430b95140f88197757ecec91c Mon Sep 17 00:00:00 2001 From: Eric Mc Sween Date: Tue, 23 Mar 2021 15:09:12 -0400 Subject: [PATCH 535/549] Upgrade to Node 12 --- services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 2 +- services/track-changes/Makefile | 6 ++++-- services/track-changes/buildscript.txt | 4 ++-- services/track-changes/docker-compose.yml | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index 2baa2d433a..e68b860383 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -10.23.1 +12.21.0 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 2da67d2436..4f417a2a4b 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -2,7 +2,7 @@ # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -FROM node:10.23.1 as base +FROM node:12.21.0 as base WORKDIR /app diff --git a/services/track-changes/Makefile b/services/track-changes/Makefile index eaff3428ca..664e4ca96d 100644 --- a/services/track-changes/Makefile +++ b/services/track-changes/Makefile @@ -21,8 +21,10 @@ DOCKER_COMPOSE_TEST_UNIT = \ COMPOSE_PROJECT_NAME=test_unit_$(BUILD_DIR_NAME) $(DOCKER_COMPOSE) clean: - docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + -docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + -docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + -$(DOCKER_COMPOSE_TEST_UNIT) down --rmi local + -$(DOCKER_COMPOSE_TEST_ACCEPTANCE) down --rmi local format: $(DOCKER_COMPOSE) run --rm test_unit npm run --silent format diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index 205e7f63be..fb68280822 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -3,6 +3,6 @@ track-changes --docker-repos=gcr.io/overleaf-ops --env-add=AWS_BUCKET=bucket --env-pass-through= ---node-version=10.23.1 +--node-version=12.21.0 --public-repo=True ---script-version=3.4.0 +--script-version=3.7.0 diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index e0afc49351..89df8581d7 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.3" services: test_unit: - image: node:10.23.1 + image: node:12.21.0 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: node:10.23.1 + image: node:12.21.0 volumes: - .:/app working_dir: /app From e325bec7f3305ed0b8a3c75e46630259ce6dfd2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Mar 2021 14:25:02 +0000 Subject: [PATCH 536/549] build(deps): bump y18n from 4.0.0 to 4.0.1 Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index fd8814cb0f..ba0a1964de 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -7361,9 +7361,9 @@ } }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, "yallist": { From 666e9f22cda8ab5bfb0407a3202116cfbcd8fb38 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Thu, 29 Apr 2021 15:30:49 +0100 Subject: [PATCH 537/549] [misc] add linting for missing explicit dependencies and fix any errors --- services/track-changes/.eslintrc | 13 +++++++++++-- services/track-changes/buildscript.txt | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/services/track-changes/.eslintrc b/services/track-changes/.eslintrc index 76dad1561d..321353f971 100644 --- a/services/track-changes/.eslintrc +++ b/services/track-changes/.eslintrc @@ -22,7 +22,10 @@ "rules": { // Swap the no-unused-expressions rule with a more chai-friendly one "no-unused-expressions": 0, - "chai-friendly/no-unused-expressions": "error" + "chai-friendly/no-unused-expressions": "error", + + // Do not allow importing of implicit dependencies. + "import/no-extraneous-dependencies": "error" }, "overrides": [ { @@ -57,7 +60,13 @@ "files": ["app/**/*.js", "app.js", "index.js"], "rules": { // don't allow console.log in backend code - "no-console": "error" + "no-console": "error", + + // Do not allow importing of implicit dependencies. + "import/no-extraneous-dependencies": ["error", { + // Do not allow importing of devDependencies. + "devDependencies": false + }] } } ] diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index fb68280822..de6c7db932 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -5,4 +5,4 @@ track-changes --env-pass-through= --node-version=12.21.0 --public-repo=True ---script-version=3.7.0 +--script-version=3.8.0 From c30fe0364a3d258fe1af8f10aeedfd6dbe752559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 May 2021 05:08:44 +0000 Subject: [PATCH 538/549] build(deps): bump underscore from 1.9.2 to 1.13.1 Bumps [underscore](https://github.com/jashkenas/underscore) from 1.9.2 to 1.13.1. - [Release notes](https://github.com/jashkenas/underscore/releases) - [Commits](https://github.com/jashkenas/underscore/compare/1.9.2...1.13.1) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- services/track-changes/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index fd8814cb0f..40e0b654b4 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -7054,9 +7054,9 @@ "dev": true }, "underscore": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", - "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==" + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" }, "unpipe": { "version": "1.0.0", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 2aa031f310..805610ad5e 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -38,7 +38,7 @@ "requestretry": "^4.1.0", "s3-streams": "^0.4.0", "settings-sharelatex": "^1.1.0", - "underscore": "~1.9.2" + "underscore": "~1.13.1" }, "devDependencies": { "babel-eslint": "^10.1.0", From 39bc2b768584b0480a311c359623907ea41a88d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 May 2021 03:21:10 +0000 Subject: [PATCH 539/549] build(deps): bump lodash from 4.17.20 to 4.17.21 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index fd8814cb0f..ebb86421be 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -4069,9 +4069,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.at": { "version": "4.6.0", From 218f0fd4d8e499a032b9efd0b8de132488ece7fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 May 2021 12:33:05 +0000 Subject: [PATCH 540/549] build(deps): bump hosted-git-info from 2.8.5 to 2.8.9 Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.5 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.5...v2.8.9) Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index fd8814cb0f..1dbd92da5d 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -3520,9 +3520,9 @@ "integrity": "sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA==" }, "hosted-git-info": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", - "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "http-errors": { From f716134b53279394c6ff5521017196efd86b4588 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Wed, 26 May 2021 15:56:08 +0100 Subject: [PATCH 541/549] Delete CoffeeScript scripts --- services/track-changes/benchmark.coffee | 56 ----------------------- services/track-changes/fixdangling.coffee | 54 ---------------------- 2 files changed, 110 deletions(-) delete mode 100644 services/track-changes/benchmark.coffee delete mode 100644 services/track-changes/fixdangling.coffee diff --git a/services/track-changes/benchmark.coffee b/services/track-changes/benchmark.coffee deleted file mode 100644 index 7ed9cd81e7..0000000000 --- a/services/track-changes/benchmark.coffee +++ /dev/null @@ -1,56 +0,0 @@ -request = require "request" -rclient = require("redis").createClient() -async = require "async" -{ObjectId} = require("./app/js/mongojs") - -NO_OF_DOCS = 100 -NO_OF_UPDATES = 200 - -user_id = ObjectId().toString() - -updates = for i in [1..NO_OF_UPDATES] - { - op: { i: "a", p: 0 } - v: i - meta: ts: new Date(), user_id: user_id - } -jsonUpdates = (JSON.stringify(u) for u in updates) - -doc_ids = (ObjectId().toString() for i in [1..NO_OF_DOCS]) - -populateRedis = (callback = (error) ->) -> - console.log "Populating Redis queues..." - - jobs = [] - for doc_id in doc_ids - do (doc_id) -> - jobs.push (callback) -> - rclient.rpush "UncompressedHistoryOps:#{doc_id}", jsonUpdates..., callback - async.series jobs, (error) -> - return callback(error) if error? - console.log "Done." - callback() - -flushDocs = (callback = (error) ->) -> - console.log "Flushing docs..." - inProgress = 0 - jobs = [] - for doc_id in doc_ids - do (doc_id) -> - jobs.push (callback) -> - inProgress = inProgress + 1 - request.post "http://localhost:3014/doc/#{doc_id}/flush", (error) -> - inProgress = inProgress - 1 - console.log Date.now(), "In progress: #{inProgress}" - callback(error) - async.parallel jobs, (error) -> - return callback(error) if error? - console.log "Done." - callback() - -populateRedis (error) -> - throw error if error? - flushDocs (error) -> - throw error if error? - process.exit(0) - diff --git a/services/track-changes/fixdangling.coffee b/services/track-changes/fixdangling.coffee deleted file mode 100644 index 1e656b6fe8..0000000000 --- a/services/track-changes/fixdangling.coffee +++ /dev/null @@ -1,54 +0,0 @@ -Settings = require "settings-sharelatex" -logger = require "logger-sharelatex" -TrackChangesLogger = logger.initialize("track-changes").logger -async = require "async" -fs = require "fs" -request = require "request" -cli = require "cli" - -mongojs = require "mongojs" -bson = require "bson" -db = mongojs(Settings.mongo.url, ["docs"]) -ObjectId = mongojs.ObjectId - -options = cli.parse({ - port: ['p', 'port number for track changes', 'number'], - force: ['f', 'actually make the fix'] -}); - -if cli.args.length < 1 - console.log "fixdangling -p PORT file_of_doc_ids" - process.exit() - -file = cli.args.pop() -doc_ids = fs.readFileSync(file).toString().trim().split("\n") - -missing = 0 -errored = 0 -success = 0 - -fixDangling = (doc_id, callback) -> - # look up project id from doc id - db.docs.find {_id:ObjectId(doc_id)}, {project_id:1}, (err, result) -> - #console.log "doc_id", doc_id, "err", err, "result", result - if err? - errored++ - return callback() - if !result? or result.length == 0 - missing++ - return callback() - project_id = result[0].project_id - console.log "found project_id", project_id, "for doc_id", doc_id - url = "http://localhost:#{options.port}/project/#{project_id}/doc/#{doc_id}/flush" - if options.force - request.post url, (err, response, body) -> - if err? then errored++ else success++ - callback() - else - console.log "URL:", url - success++ - callback() - -async.eachSeries doc_ids, fixDangling, (err) -> - console.log "final result", err, "missing", missing, "errored", errored, "success", success - db.close() From a03f553ea7861de0c5ebe5ab78aa9a95c91522dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 02:55:45 +0000 Subject: [PATCH 542/549] build(deps): bump glob-parent from 5.1.0 to 5.1.2 Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.0 to 5.1.2. - [Release notes](https://github.com/gulpjs/glob-parent/releases) - [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md) - [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.0...v5.1.2) --- updated-dependencies: - dependency-name: glob-parent dependency-type: indirect ... Signed-off-by: dependabot[bot] --- services/track-changes/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 73b894e8e2..4655adaf65 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -3261,9 +3261,9 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" From 7495d2114ae72710508c45c283cfac4a8fe06a0e Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 12 Jul 2021 17:35:46 +0100 Subject: [PATCH 543/549] [misc] install bunyan as production dependency ``` Error: Cannot find module 'bunyan' Require stack: - .../node_modules/@google-cloud/logging-bunyan/build/src/middleware/express.js - .../node_modules/@google-cloud/logging-bunyan/build/src/index.js - .../node_modules/logger-sharelatex/logging-manager.js - .../node_modules/logger-sharelatex/index.js - .../app.js ``` --- services/track-changes/package-lock.json | 16 ++++------------ services/track-changes/package.json | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 73b894e8e2..9574423f5d 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1638,14 +1638,12 @@ "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==" }, "bunyan": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.2.tgz", - "integrity": "sha512-9/tdl5YpJ2FR1ldac8y+hAC3rLaVzXnrd1ZATS+ehg5VCpD+5vltW9VVWbMF8o5qiw4uQO0nEFK/wL7B2SDvpg==", - "dev": true, + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", "requires": { "dtrace-provider": "~0.8", - "exeunt": "1.1.0", - "moment": "^2.10.6", + "moment": "^2.19.3", "mv": "~2", "safe-json-stringify": "~1" } @@ -2837,12 +2835,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" }, - "exeunt": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", - "integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==", - "dev": true - }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 805610ad5e..3ddb284bb0 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -26,6 +26,7 @@ "aws-sdk": "^2.643.0", "body-parser": "^1.19.0", "bson": "^1.1.5", + "bunyan": "^1.8.15", "byline": "^5.0.0", "express": "4.17.1", "heap": "^0.2.6", @@ -42,7 +43,6 @@ }, "devDependencies": { "babel-eslint": "^10.1.0", - "bunyan": "~2.0.2", "chai": "~4.2.0", "cli": "^1.0.1", "eslint": "^6.8.0", From b5bb7c3b04ce52e96fe16dd71b20c4e6194ec142 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 12 Jul 2021 17:47:16 +0100 Subject: [PATCH 544/549] [misc] switch from settings-sharelatex to @overleaf/settings --- services/track-changes/app.js | 2 +- .../app/js/DocumentUpdaterManager.js | 2 +- .../track-changes/app/js/HealthChecker.js | 2 +- services/track-changes/app/js/LockManager.js | 2 +- services/track-changes/app/js/MongoAWS.js | 2 +- services/track-changes/app/js/PackManager.js | 2 +- services/track-changes/app/js/PackWorker.js | 2 +- services/track-changes/app/js/RedisManager.js | 2 +- .../track-changes/app/js/UpdatesManager.js | 2 +- .../track-changes/app/js/WebApiManager.js | 2 +- services/track-changes/app/js/mongodb.js | 2 +- services/track-changes/package-lock.json | 20 +++++-------------- services/track-changes/package.json | 2 +- .../acceptance/js/AppendingUpdatesTests.js | 2 +- .../acceptance/js/ArchivingUpdatesTests.js | 2 +- .../acceptance/js/FlushingUpdatesTests.js | 2 +- .../test/acceptance/js/GettingADiffTests.js | 2 +- .../test/acceptance/js/GettingUpdatesTests.js | 2 +- .../test/acceptance/js/LockManagerTests.js | 2 +- .../test/acceptance/js/RestoringVersions.js | 2 +- .../acceptance/js/helpers/TrackChangesApp.js | 2 +- .../js/helpers/TrackChangesClient.js | 2 +- .../test/unit/js/DocArchive/MongoAWS.js | 2 +- .../DocumentUpdaterManagerTests.js | 2 +- .../unit/js/LockManager/LockManagerTests.js | 2 +- .../unit/js/PackManager/PackManagerTests.js | 2 +- .../unit/js/RedisManager/RedisManagerTests.js | 2 +- .../js/UpdatesManager/UpdatesManagerTests.js | 2 +- .../js/WebApiManager/WebApiManagerTests.js | 2 +- 29 files changed, 33 insertions(+), 43 deletions(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 9dd8f9440b..4245231b8d 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -7,7 +7,7 @@ */ const Metrics = require('@overleaf/metrics') Metrics.initialize('track-changes') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const logger = require('logger-sharelatex') const TrackChangesLogger = logger.initialize('track-changes').logger diff --git a/services/track-changes/app/js/DocumentUpdaterManager.js b/services/track-changes/app/js/DocumentUpdaterManager.js index 29247b1664..e3a7c2ce10 100644 --- a/services/track-changes/app/js/DocumentUpdaterManager.js +++ b/services/track-changes/app/js/DocumentUpdaterManager.js @@ -14,7 +14,7 @@ let DocumentUpdaterManager const request = require('request') const logger = require('logger-sharelatex') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') module.exports = DocumentUpdaterManager = { getDocument(project_id, doc_id, callback) { diff --git a/services/track-changes/app/js/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js index afc7ff7fc8..7a645b254d 100644 --- a/services/track-changes/app/js/HealthChecker.js +++ b/services/track-changes/app/js/HealthChecker.js @@ -13,7 +13,7 @@ const { ObjectId } = require('./mongodb') const request = require('request') const async = require('async') -const settings = require('settings-sharelatex') +const settings = require('@overleaf/settings') const { port } = settings.internal.trackchanges const logger = require('logger-sharelatex') const LockManager = require('./LockManager') diff --git a/services/track-changes/app/js/LockManager.js b/services/track-changes/app/js/LockManager.js index 6b94a89233..6cdb03219a 100644 --- a/services/track-changes/app/js/LockManager.js +++ b/services/track-changes/app/js/LockManager.js @@ -10,7 +10,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let LockManager -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const redis = require('@overleaf/redis-wrapper') const rclient = redis.createClient(Settings.redis.lock) const os = require('os') diff --git a/services/track-changes/app/js/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js index c0f88e98e3..cc4001e9ed 100644 --- a/services/track-changes/app/js/MongoAWS.js +++ b/services/track-changes/app/js/MongoAWS.js @@ -14,7 +14,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let MongoAWS -const settings = require('settings-sharelatex') +const settings = require('@overleaf/settings') const logger = require('logger-sharelatex') const AWS = require('aws-sdk') const S3S = require('s3-streams') diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 10938fede6..509ba3e1c7 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -24,7 +24,7 @@ const LockManager = require('./LockManager') const MongoAWS = require('./MongoAWS') const Metrics = require('@overleaf/metrics') const ProjectIterator = require('./ProjectIterator') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const keys = Settings.redis.lock.key_schema // Sharejs operations are stored in a 'pack' object diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index dc15ee8f21..fe507fe815 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -16,7 +16,7 @@ let LIMIT, pending let project_id, doc_id const { callbackify } = require('util') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const async = require('async') const _ = require('underscore') const { db, ObjectId, waitForDb, closeDb } = require('./mongodb') diff --git a/services/track-changes/app/js/RedisManager.js b/services/track-changes/app/js/RedisManager.js index b995a60128..6a88c31303 100644 --- a/services/track-changes/app/js/RedisManager.js +++ b/services/track-changes/app/js/RedisManager.js @@ -13,7 +13,7 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let RedisManager -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const redis = require('@overleaf/redis-wrapper') const rclient = redis.createClient(Settings.redis.history) const Keys = Settings.redis.history.key_schema diff --git a/services/track-changes/app/js/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js index 7890208788..2acdf8039d 100644 --- a/services/track-changes/app/js/UpdatesManager.js +++ b/services/track-changes/app/js/UpdatesManager.js @@ -25,7 +25,7 @@ const UpdateTrimmer = require('./UpdateTrimmer') const logger = require('logger-sharelatex') const async = require('async') const _ = require('underscore') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const keys = Settings.redis.lock.key_schema module.exports = UpdatesManager = { diff --git a/services/track-changes/app/js/WebApiManager.js b/services/track-changes/app/js/WebApiManager.js index 2ab3bdfda1..b1bd6de063 100644 --- a/services/track-changes/app/js/WebApiManager.js +++ b/services/track-changes/app/js/WebApiManager.js @@ -13,7 +13,7 @@ let WebApiManager const request = require('requestretry') // allow retry on error https://github.com/FGRibreau/node-request-retry const logger = require('logger-sharelatex') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') // Don't let HTTP calls hang for a long time const MAX_HTTP_REQUEST_LENGTH = 15000 // 15 seconds diff --git a/services/track-changes/app/js/mongodb.js b/services/track-changes/app/js/mongodb.js index e0d50a165b..5b4f56dce8 100644 --- a/services/track-changes/app/js/mongodb.js +++ b/services/track-changes/app/js/mongodb.js @@ -1,4 +1,4 @@ -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const { MongoClient, ObjectId } = require('mongodb') const clientPromise = MongoClient.connect( diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 9574423f5d..0a671ec1a6 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -980,6 +980,11 @@ "ioredis": "~4.17.3" } }, + "@overleaf/settings": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@overleaf/settings/-/settings-2.1.1.tgz", + "integrity": "sha512-vcJwqCGFKmQxTP/syUqCeMaSRjHmBcQgKOACR9He2uJcErg2GZPa1go+nGvszMbkElM4HfRKm/MfxvqHhoN4TQ==" + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -6431,21 +6436,6 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, - "settings-sharelatex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/settings-sharelatex/-/settings-sharelatex-1.1.0.tgz", - "integrity": "sha512-f7D+0lnlohoteSn6IKTH72NE+JnAdMWTKwQglAuimZWTID2FRRItZSGeYMTRpvEnaQApkoVwRp//WRMsiddnqw==", - "requires": { - "coffee-script": "1.6.0" - }, - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha512-Tx8itEfCsQp8RbLDFt7qwjqXycAx2g6SI7//4PPUR2j6meLmNifYm6zKrNDcU1+Q/GWRhjhEZk7DaLG1TfIzGA==" - } - } - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", diff --git a/services/track-changes/package.json b/services/track-changes/package.json index 3ddb284bb0..c458f35ab7 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -21,6 +21,7 @@ "@overleaf/metrics": "^3.5.1", "@overleaf/o-error": "^3.1.0", "@overleaf/redis-wrapper": "^2.0.0", + "@overleaf/settings": "^2.1.1", "JSONStream": "^1.3.5", "async": "^2.6.3", "aws-sdk": "^2.643.0", @@ -38,7 +39,6 @@ "request": "~2.88.2", "requestretry": "^4.1.0", "s3-streams": "^0.4.0", - "settings-sharelatex": "^1.1.0", "underscore": "~1.13.1" }, "devDependencies": { diff --git a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js index def0bdfa1d..c0bb0b07ae 100644 --- a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js @@ -13,7 +13,7 @@ const sinon = require('sinon') const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const request = require('request') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index a17ad6f1de..4f693e70a1 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -17,7 +17,7 @@ const sinon = require('sinon') const { expect } = require('chai') const { db, ObjectId } = require('../../../app/js/mongodb') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const request = require('request') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js index c9adeb5fd8..99851d9515 100644 --- a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js @@ -13,7 +13,7 @@ const sinon = require('sinon') const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const request = require('request') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now diff --git a/services/track-changes/test/acceptance/js/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js index 9ccc2cca36..b0390db9e4 100644 --- a/services/track-changes/test/acceptance/js/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/js/GettingADiffTests.js @@ -12,7 +12,7 @@ const sinon = require('sinon') const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const TrackChangesApp = require('./helpers/TrackChangesApp') const TrackChangesClient = require('./helpers/TrackChangesClient') diff --git a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js index f216d987a4..f58c9b729f 100644 --- a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js @@ -13,7 +13,7 @@ const sinon = require('sinon') const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const TrackChangesApp = require('./helpers/TrackChangesApp') const TrackChangesClient = require('./helpers/TrackChangesClient') diff --git a/services/track-changes/test/acceptance/js/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js index 7062a83237..47341f7891 100644 --- a/services/track-changes/test/acceptance/js/LockManagerTests.js +++ b/services/track-changes/test/acceptance/js/LockManagerTests.js @@ -11,7 +11,7 @@ */ const sinon = require('sinon') const { expect } = require('chai') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const LockManager = require('../../../app/js/LockManager') const rclient = require('redis').createClient(Settings.redis.history) // Only works locally for now const TrackChangesApp = require('./helpers/TrackChangesApp') diff --git a/services/track-changes/test/acceptance/js/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js index a947dc09df..1e9f0d43a5 100644 --- a/services/track-changes/test/acceptance/js/RestoringVersions.js +++ b/services/track-changes/test/acceptance/js/RestoringVersions.js @@ -12,7 +12,7 @@ const sinon = require('sinon') const { expect } = require('chai') const { ObjectId } = require('../../../app/js/mongodb') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const TrackChangesApp = require('./helpers/TrackChangesApp') const TrackChangesClient = require('./helpers/TrackChangesClient') diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js index 861dcdde88..7840b1f569 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js @@ -15,7 +15,7 @@ const app = require('../../../../app') const { waitForDb } = require('../../../../app/js/mongodb') const logger = require('logger-sharelatex') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') module.exports = { running: false, diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index f20884448d..a48ccc7a78 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -16,7 +16,7 @@ let TrackChangesClient const async = require('async') const zlib = require('zlib') const request = require('request') -const Settings = require('settings-sharelatex') +const Settings = require('@overleaf/settings') const rclient = require('@overleaf/redis-wrapper').createClient( Settings.redis.history ) // Only works locally for now diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index dd4374e965..cc539c8393 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -21,7 +21,7 @@ describe('MongoAWS', function () { this.MongoAWS = SandboxedModule.require(modulePath, { singleOnly: true, requires: { - 'settings-sharelatex': (this.settings = { + '@overleaf/settings': (this.settings = { trackchanges: { s3: { secret: 's3-secret', diff --git a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index 2825a10fb5..c8a218d2a3 100644 --- a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -19,7 +19,7 @@ describe('DocumentUpdaterManager', function () { this.DocumentUpdaterManager = SandboxedModule.require(modulePath, { requires: { request: (this.request = {}), - 'settings-sharelatex': (this.settings = { + '@overleaf/settings': (this.settings = { apis: { documentupdater: { url: 'http://example.com' } } }) } diff --git a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js index fc26997574..1c933c3eea 100644 --- a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js @@ -34,7 +34,7 @@ describe('LockManager', function () { return (this.rclient = { auth: sinon.stub() }) } }, - 'settings-sharelatex': this.Settings + '@overleaf/settings': this.Settings } }) diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index 957c22aac8..1f85dcf38c 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -30,7 +30,7 @@ describe('PackManager', function () { './MongoAWS': {}, '@overleaf/metrics': { inc() {} }, './ProjectIterator': require('../../../../app/js/ProjectIterator.js'), // Cache for speed - 'settings-sharelatex': { + '@overleaf/settings': { redis: { lock: { key_schema: {} } } } } diff --git a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js index 13fa467570..0196bc0ae2 100644 --- a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js @@ -28,7 +28,7 @@ describe('RedisManager', function () { }) } }, - 'settings-sharelatex': { + '@overleaf/settings': { redis: { history: { key_schema: { diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index b90d1567c8..68904e9471 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -32,7 +32,7 @@ describe('UpdatesManager', function () { './WebApiManager': (this.WebApiManager = {}), './UpdateTrimmer': (this.UpdateTrimmer = {}), './DocArchiveManager': (this.DocArchiveManager = {}), - 'settings-sharelatex': { + '@overleaf/settings': { redis: { lock: { key_schema: { diff --git a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js index 7e23e72b0a..ce2f6f6890 100644 --- a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js @@ -19,7 +19,7 @@ describe('WebApiManager', function () { this.WebApiManager = SandboxedModule.require(modulePath, { requires: { requestretry: (this.request = {}), - 'settings-sharelatex': (this.settings = { + '@overleaf/settings': (this.settings = { apis: { web: { url: 'http://example.com', From 9af44259153b4b86a8db89453eb39da979f536c0 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Mon, 12 Jul 2021 17:51:02 +0100 Subject: [PATCH 545/549] [misc] run npm dedupe --- services/track-changes/package-lock.json | 256 ++--------------------- 1 file changed, 16 insertions(+), 240 deletions(-) diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 0a671ec1a6..68637eada6 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -1344,14 +1344,6 @@ "requires": { "ast-types-flow": "0.0.7", "commander": "^2.11.0" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } } }, "array-flatten": { @@ -1385,6 +1377,14 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", @@ -1716,12 +1716,6 @@ "supports-color": "^5.3.0" }, "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1870,6 +1864,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "common-tags": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", @@ -2030,13 +2030,6 @@ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "requires": { "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - } } }, "debug": { @@ -2342,29 +2335,9 @@ "type-fest": "^0.8.1" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "semver": { "version": "6.3.0", @@ -2545,15 +2518,6 @@ "isarray": "^1.0.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -2646,15 +2610,6 @@ "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", "dev": true }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -3046,29 +3001,6 @@ "write": "1.0.3" }, "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -3235,13 +3167,6 @@ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "requires": { "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - } } }, "glob": { @@ -3976,13 +3901,6 @@ "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - } } }, "jsx-ast-utils": { @@ -4146,17 +4064,6 @@ "yn": "^4.0.0" }, "dependencies": { - "bunyan": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz", - "integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==", - "requires": { - "dtrace-provider": "~0.8", - "moment": "^2.19.3", - "mv": "~2", - "safe-json-stringify": "~1" - } - }, "yn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz", @@ -4366,11 +4273,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" - }, "minipass": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", @@ -4489,15 +4391,6 @@ "path-exists": "^3.0.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", @@ -4608,17 +4501,6 @@ "mkdirp": "~0.5.1", "ncp": "~2.0.0", "rimraf": "~2.4.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "optional": true, - "requires": { - "minimist": "0.0.8" - } - } } }, "nan": { @@ -5327,20 +5209,6 @@ "escape-string-regexp": "^1.0.5" } }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "inquirer": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", @@ -5391,24 +5259,6 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -5629,20 +5479,6 @@ "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", "dev": true }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "ignore": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", @@ -5708,24 +5544,6 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -6281,15 +6099,6 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } } } }, @@ -6620,21 +6429,6 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" - }, - "dependencies": { - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - } } }, "stack-trace": { @@ -7113,13 +6907,6 @@ "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - } } }, "vue-eslint-parser": { @@ -7306,17 +7093,6 @@ "dev": true, "requires": { "mkdirp": "^0.5.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - } } }, "xml2js": { From 1e577e24b1f12fb4ba137804c86e8d6cdf1ea4c9 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 13 Jul 2021 11:55:14 +0100 Subject: [PATCH 546/549] [misc] upgrade build scripts to version 3.11.0 and cleanup packages ``` npm uninstall prettier-eslint-cli eslint-plugin-standard eslint-plugin-jsx-a11y eslint-plugin-react eslint-config-standard-jsx eslint-config-standard-react babel-eslint npm dedupe ``` --- services/track-changes/.eslintrc | 2 +- services/track-changes/.github/dependabot.yml | 2 +- services/track-changes/.prettierrc | 6 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/package-lock.json | 3421 +++++------------ services/track-changes/package.json | 37 +- 6 files changed, 1014 insertions(+), 2456 deletions(-) diff --git a/services/track-changes/.eslintrc b/services/track-changes/.eslintrc index 321353f971..1c14f50efe 100644 --- a/services/track-changes/.eslintrc +++ b/services/track-changes/.eslintrc @@ -3,9 +3,9 @@ // https://github.com/sharelatex/sharelatex-dev-environment { "extends": [ + "eslint:recommended", "standard", "prettier", - "prettier/standard" ], "parserOptions": { "ecmaVersion": 2018 diff --git a/services/track-changes/.github/dependabot.yml b/services/track-changes/.github/dependabot.yml index e2c64a3351..c856753655 100644 --- a/services/track-changes/.github/dependabot.yml +++ b/services/track-changes/.github/dependabot.yml @@ -20,4 +20,4 @@ updates: # future if we reorganise teams labels: - "dependencies" - - "Team-Magma" + - "type:maintenance" diff --git a/services/track-changes/.prettierrc b/services/track-changes/.prettierrc index 24f9ec526f..c92c3526e7 100644 --- a/services/track-changes/.prettierrc +++ b/services/track-changes/.prettierrc @@ -2,6 +2,10 @@ # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment { + "arrowParens": "avoid", "semi": false, - "singleQuote": true + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "useTabs": false } diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index de6c7db932..a1522ab57e 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -5,4 +5,4 @@ track-changes --env-pass-through= --node-version=12.21.0 --public-repo=True ---script-version=3.8.0 +--script-version=3.11.0 diff --git a/services/track-changes/package-lock.json b/services/track-changes/package-lock.json index 68637eada6..0cb04e7652 100644 --- a/services/track-changes/package-lock.json +++ b/services/track-changes/package-lock.json @@ -5,152 +5,68 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/generator": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.0.tgz", - "integrity": "sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw==", - "dev": true, - "requires": { - "@babel/types": "^7.9.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", "dev": true }, "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, - "@babel/parser": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg==", - "dev": true - }, - "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "@babel/runtime-corejs3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.0.tgz", - "integrity": "sha512-Fe3z3yVZNCUTaOFBAofwkEtFiYi7a7Gg2F5S1QX+mqP403i2iKJtyHJYEp/PV2ijUheT0PiKWbmXcqtwLhmBzg==", - "dev": true, - "requires": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" }, "dependencies": { - "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true } } }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", - "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.0", - "@babel/types": "^7.9.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", - "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, "@google-cloud/common": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.4.0.tgz", @@ -229,11 +145,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz", "integrity": "sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw==" }, - "acorn": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", - "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==" - }, "bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -326,14 +237,6 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -905,6 +808,23 @@ "protobufjs": "^6.8.6" } }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@opencensus/core": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.20.tgz", @@ -1089,23 +1009,11 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, "@types/console-log-level": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@types/console-log-level/-/console-log-level-1.4.0.tgz", "integrity": "sha512-x+OscEQwcx5Biair4enH7ov9W+clcqUWaZRaxn5IkT4yNWWjRr2oiYDkY/x1uXSTVZOQ2xlbFQySaQGB+VdXGQ==" }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true - }, "@types/fs-extra": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", @@ -1114,12 +1022,6 @@ "@types/node": "*" } }, - "@types/json-schema": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", - "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", - "dev": true - }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", @@ -1135,58 +1037,11 @@ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" }, - "@typescript-eslint/experimental-utils": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", - "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-scope": "^4.0.0" - }, - "dependencies": { - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", - "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", - "dev": true, - "requires": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "1.13.0", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "@typescript-eslint/typescript-estree": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", - "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", - "dev": true, - "requires": { - "lodash.unescape": "4.0.1", - "semver": "5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - } - } + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true }, "JSONStream": { "version": "1.3.5", @@ -1235,15 +1090,14 @@ } }, "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.5.tgz", + "integrity": "sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg==" }, "acorn-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true }, "agent-base": { @@ -1266,28 +1120,11 @@ } }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -1304,9 +1141,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -1336,40 +1173,50 @@ "sprintf-js": "~1.0.2" } }, - "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", "is-string": "^1.0.5" } }, "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==" + } } }, "arrify": { @@ -1396,16 +1243,10 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", - "dev": true - }, "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "async": { @@ -1463,37 +1304,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, - "axobject-query": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz", - "integrity": "sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==", - "dev": true - }, - "babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "dependencies": { - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1518,9 +1328,9 @@ "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" }, "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "bindings": { @@ -1582,12 +1392,6 @@ } } }, - "boolify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz", - "integrity": "sha512-ma2q0Tc760dW54CdOyJjhrg/a54317o1zYADQJFgperNGKIKgAUGIcKnuMiff8z57+yGlrGNEt4lPgZfCgTJgA==", - "dev": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1663,6 +1467,16 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1670,22 +1484,11 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "camelcase-keys": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.1.2.tgz", - "integrity": "sha512-QfFrU0CIw2oltVvpndW32kuJ/9YOJwUnmWrjlXt1nnJZHCaS9i6bfOpg9R4Lw8aZjStkJWM+jc0cdXjWBgVJSw==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1705,6 +1508,15 @@ "type-detect": "^4.0.5" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1714,25 +1526,8 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -1745,19 +1540,18 @@ "dev": true }, "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" + "readdirp": "~3.5.0" } }, "chownr": { @@ -1775,55 +1569,15 @@ "glob": "^7.1.1" } }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha512-EJLbKSuvHTrVRynOXCYFTbQKZOFXWNe3/6DN1yrEH3TuuZT1x4dMQnCHnfCrBUUiGjO63enEIfaB17VaRl2d4A==", - "dev": true - }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "cluster-key-slot": { @@ -1864,18 +1618,6 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "common-tags": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", - "dev": true - }, "compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -1940,12 +1682,6 @@ "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==" }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==", - "dev": true - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1978,34 +1714,20 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", - "dev": true - }, - "core-js-pure": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz", - "integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "crypt": { @@ -2018,12 +1740,6 @@ "resolved": "https://registry.npmjs.org/d64/-/d64-1.0.0.tgz", "integrity": "sha1-QAKofoUMv8n52XBrYPymE6MzbpA=" }, - "damerau-levenshtein": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", - "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", - "dev": true - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -2041,9 +1757,9 @@ } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "deep-eql": { @@ -2111,15 +1827,9 @@ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "doctrine": { @@ -2208,6 +1918,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "ent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", @@ -2223,22 +1942,24 @@ } }, "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "object-inspect": "^1.10.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" } }, "es-to-primitive": { @@ -2252,6 +1973,12 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2260,58 +1987,61 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz", + "integrity": "sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -2320,67 +2050,130 @@ "uri-js": "^4.2.2" } }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } } }, "eslint-config-prettier": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz", - "integrity": "sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ==", - "dev": true, - "requires": { - "get-stdin": "^6.0.0" - } + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true }, "eslint-config-standard": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", - "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", "dev": true }, - "eslint-config-standard-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", - "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", - "dev": true - }, - "eslint-config-standard-react": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-react/-/eslint-config-standard-react-9.2.0.tgz", - "integrity": "sha512-u+KRP2uCtthZ/W4DlLWCC59GZNV/y9k9yicWWammgTs/Omh8ZUUPF3EnYm81MAcbkYQq2Wg0oxutAhi/FQ8mIw==", - "dev": true, - "requires": { - "eslint-config-standard-jsx": "^8.0.0" - } - }, "eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { "debug": "^2.6.9", @@ -2399,104 +2192,75 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } } } }, "eslint-module-utils": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", - "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", "dev": true, "requires": { - "debug": "^2.6.9", + "debug": "^3.2.7", "pkg-dir": "^2.0.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true } } }, "eslint-plugin-chai-expect": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-chai-expect/-/eslint-plugin-chai-expect-2.1.0.tgz", - "integrity": "sha512-rd0/4mjMV6c3i0o4DKkWI4uaFN9DK707kW+/fDphaDI6HVgxXnhML9Xgt5vHnTXmSSnDhupuCFBgsEAEpchXmQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-chai-expect/-/eslint-plugin-chai-expect-2.2.0.tgz", + "integrity": "sha512-ExTJKhgeYMfY8wDj3UiZmgpMKJOUHGNHmWMlxT49JUDB1vTnw0sSNfXJSxnX+LcebyBD/gudXzjzD136WqPJrQ==", "dev": true }, "eslint-plugin-chai-friendly": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.5.0.tgz", - "integrity": "sha512-Pxe6z8C9fP0pn2X2nGFU/b3GBOCM/5FVus1hsMwJsXP3R7RiXFl7g0ksJbsc0GxiLyidTW4mEFk77qsNn7Tk7g==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.6.0.tgz", + "integrity": "sha512-Uvvv1gkbRGp/qfN15B0kQyQWg+oFA8buDSqrwmW3egNSk/FpqH2MjQqKOuKwmEL6w4QIQrIjDp+gg6kGGmD3oQ==", "dev": true }, "eslint-plugin-es": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.0.tgz", - "integrity": "sha512-6/Jb/J/ZvSebydwbBJO1R9E5ky7YeElfK56Veh7e4QGFHCXoIXGH9HhVz+ibJLM3XJ1XjP+T7rKBLUa/Y7eIng==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", "dev": true, "requires": { "eslint-utils": "^2.0.0", "regexpp": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "regexpp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", - "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", - "dev": true - } } }, "eslint-plugin-import": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", - "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", - "contains-path": "^0.1.0", + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", "has": "^1.0.3", + "is-core-module": "^2.4.0", "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" }, "dependencies": { "debug": { @@ -2509,82 +2273,55 @@ } }, "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "esutils": "^2.0.2" + } + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + }, + "is-core-module": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", + "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", + "dev": true, + "requires": { + "has": "^1.0.3" } }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", - "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.4.5", - "aria-query": "^3.0.0", - "array-includes": "^3.0.3", - "ast-types-flow": "^0.0.7", - "axobject-query": "^2.0.2", - "damerau-levenshtein": "^1.0.4", - "emoji-regex": "^7.0.2", - "has": "^1.0.3", - "jsx-ast-utils": "^2.2.1" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } }, "eslint-plugin-mocha": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz", - "integrity": "sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-8.2.0.tgz", + "integrity": "sha512-8oOR47Ejt+YJPNQzedbiklDqS1zurEaNrxXpRs+Uk4DMDPVmKNagShFeUaYsfvWP55AhI+P1non5QZAHV6K78A==", "dev": true, "requires": { - "eslint-utils": "^2.0.0", - "ramda": "^0.27.0" - }, - "dependencies": { - "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - } + "eslint-utils": "^2.1.0", + "ramda": "^0.27.1" } }, "eslint-plugin-node": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.0.0.tgz", - "integrity": "sha512-chUs/NVID+sknFiJzxoN9lM7uKSOEta8GC8365hw1nDfwIPIjjpRSwwPvQanWv8dt/pDe9EV4anmVSwdiSndNg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", "dev": true, "requires": { "eslint-plugin-es": "^3.0.0", @@ -2595,19 +2332,10 @@ "semver": "^6.1.0" }, "dependencies": { - "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "semver": { @@ -2633,72 +2361,20 @@ "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", "dev": true }, - "eslint-plugin-react": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz", - "integrity": "sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.2.3", - "object.entries": "^1.1.1", - "object.fromentries": "^2.0.2", - "object.values": "^1.1.1", - "prop-types": "^15.7.2", - "resolve": "^1.15.1", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.2", - "xregexp": "^4.3.0" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-standard": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", - "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", - "dev": true - }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "esrecurse": "^4.1.0", + "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" @@ -2711,26 +2387,26 @@ "dev": true }, "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" }, "dependencies": { "acorn": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true } } @@ -2742,21 +2418,26 @@ "dev": true }, "esquery": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", - "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, "estraverse": { @@ -2863,17 +2544,6 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -2906,22 +2576,13 @@ "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.1.tgz", "integrity": "sha512-x4FEgaz3zNRtJfLFqJmHWxkMDDvXVtaznj2V9jiP8ACUJrUgist4bP9FmDL2Vew2Y9mEQI/tG4GqabaitYp9CQ==" }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, "file-uri-to-path": { @@ -2970,7 +2631,7 @@ "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { "locate-path": "^2.0.0" @@ -2982,40 +2643,24 @@ "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "flatted": "^3.1.0" } }, "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", + "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", "dev": true }, "forever-agent": { @@ -3056,13 +2701,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3155,11 +2793,16 @@ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } }, "getpass": { "version": "0.1.7", @@ -3183,19 +2826,22 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } }, "google-auth-library": { "version": "6.0.6", @@ -3340,9 +2986,9 @@ } }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "growl": { @@ -3391,22 +3037,11 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - } - } + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true }, "has-flag": { "version": "3.0.0", @@ -3539,12 +3174,6 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3564,108 +3193,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "internal-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", - "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", - "dev": true, - "requires": { - "es-abstract": "^1.17.0-next.1", - "has": "^1.0.3", - "side-channel": "^1.0.2" - } - }, "ioredis": { "version": "4.17.3", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.17.3.tgz", @@ -3695,7 +3222,13 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", "dev": true }, "is-binary-path": { @@ -3707,11 +3240,19 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.1.5", @@ -3736,7 +3277,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-fullwidth-code-point": { @@ -3754,30 +3295,42 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, - "is-promise": { + "is-plain-obj": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha512-NECAi6wp6CgMesHuVUEK8JwjCvm/tvnn5pCbB42JOHp3mgUizN0nagXu4HEqQZBkieGEQ+jVcMKWqoVd6CDbLQ==", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", "dev": true, "requires": { - "has": "^1.0.3" + "call-bind": "^1.0.2" } }, "is-stream": { @@ -3852,12 +3405,6 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, "json-bigint": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", @@ -3866,6 +3413,12 @@ "bignumber.js": "^7.0.0" } }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -3887,6 +3440,15 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -3903,16 +3465,6 @@ "verror": "1.10.0" } }, - "jsx-ast-utils": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", - "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "object.assign": "^4.1.0" - } - }, "just-extend": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", @@ -3939,13 +3491,13 @@ } }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "line-reader": { @@ -3954,21 +3506,21 @@ "integrity": "sha512-AYJ8g+eE7v+Ba4s/cuYqzuNulH/WbjdKQ55fvx8fNVn8WQzTpioY6vI1MoxTuMgcHYX3VlmZWbVvnkIqkyJbCA==" }, "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", "dev": true, "requires": { "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", + "parse-json": "^4.0.0", + "pify": "^3.0.0", "strip-bom": "^3.0.0" }, "dependencies": { "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -3976,7 +3528,7 @@ "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { "p-locate": "^2.0.0", @@ -3998,6 +3550,12 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -4019,22 +3577,16 @@ "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=" }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg==", + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, "log-driver": { @@ -4043,12 +3595,63 @@ "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { - "chalk": "^2.4.2" + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "logger-sharelatex": { @@ -4071,78 +3674,11 @@ } } }, - "loglevel": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", - "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==", - "dev": true - }, - "loglevel-colored-level-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "loglevel": "^1.4.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - } - } - }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4151,24 +3687,6 @@ "yallist": "^3.0.2" } }, - "make-plural": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.3.0.tgz", - "integrity": "sha512-xTYd4JVHpSCW+aqDof6w/MebaMVNTVYBZhbB/vi513xXdiPT92JMVCo0Jq8W2UZnzYRFeVbQiQ+I25l13JuKvA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true, - "optional": true - } - } - }, "map-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", @@ -4182,13 +3700,6 @@ "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - } } }, "media-typer": { @@ -4213,29 +3724,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "messageformat": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-2.3.0.tgz", - "integrity": "sha512-uTzvsv0lTeQxYI2y1NPa1lItL5VRI8Gb93Y2K2ue5gBPyrbJxfDi/EYWxh2PKv5yO42AJeeqblS9MJSh/IEk4w==", - "dev": true, - "requires": { - "make-plural": "^4.3.0", - "messageformat-formatters": "^2.0.1", - "messageformat-parser": "^4.1.2" - } - }, - "messageformat-formatters": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz", - "integrity": "sha512-E/lQRXhtHwGuiQjI7qxkLp8AHbMD5r2217XNe/SREbBlSawe0lOqsFb7rflZJmlQFSULNLIqlcjjsCPlB3m3Mg==", - "dev": true - }, - "messageformat-parser": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.2.tgz", - "integrity": "sha512-7dWuifeyldz7vhEuL96Kwq1fhZXBW+TUfbnHN4UCrCxoXQTYjHnR78eI66Gk9LaLLsAvzPNVJBaa66DRfFNaiA==", - "dev": true - }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -4259,12 +3747,6 @@ "mime-db": "~1.37.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4273,6 +3755,11 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, "minipass": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", @@ -4296,158 +3783,153 @@ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - } } }, "mocha": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", - "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { - "ansi-colors": "3.2.3", + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.3", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "ms": "^2.1.1" + "color-convert": "^2.0.1" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "has-flag": "^4.0.0" } } } @@ -4486,12 +3968,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", @@ -4509,6 +3985,12 @@ "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", "optional": true }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4551,12 +4033,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "nise": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz", @@ -4570,24 +4046,6 @@ "path-to-regexp": "^1.7.0" } }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", @@ -4702,9 +4160,9 @@ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", "dev": true }, "object-keys": { @@ -4714,61 +4172,26 @@ "dev": true }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", - "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "object.fromentries": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", - "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "es-abstract": "^1.18.2" } }, "on-finished": { @@ -4792,27 +4215,18 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, "os-homedir": { @@ -4835,18 +4249,17 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { "p-limit": "^1.1.0" @@ -4860,19 +4273,13 @@ "requires": { "p-try": "^1.0.0" } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true } } }, "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "parent-module": { @@ -4890,12 +4297,13 @@ "integrity": "sha512-KbAJuYGUhZkB9gotDiKLnZ7Z3VTacK3fgwmDdB6ZVDtJbMBT6MfLga0WJaYpPDu0mzqT0NgHtHDt5PY4l0nidg==" }, "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, "parse-ms": { @@ -4919,16 +4327,10 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { @@ -4954,18 +4356,18 @@ } }, "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^2.0.0" + "pify": "^3.0.0" }, "dependencies": { "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -4982,9 +4384,9 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, "pify": { @@ -4995,7 +4397,16 @@ "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", "dev": true, "requires": { "find-up": "^2.1.0" @@ -5028,14 +4439,6 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, "protobufjs": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", @@ -5064,553 +4467,17 @@ } }, "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "prettier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.1.tgz", - "integrity": "sha512-piXGBcY1zoFOG0MvHpNE5reAGseLmaCRifQ/fmfF49BcYkInEs/naD/unxGNAeOKFA5+JxVrPyMvMlpzcd20UA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", "dev": true }, - "prettier-eslint": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-9.0.1.tgz", - "integrity": "sha512-KZT65QTosSAqBBqmrC+RpXbsMRe7Os2YSR9cAfFbDlyPAopzA/S5bioiZ3rpziNQNSJaOxmtXSx07EQ+o2Dlug==", - "dev": true, - "requires": { - "@typescript-eslint/parser": "^1.10.2", - "common-tags": "^1.4.0", - "core-js": "^3.1.4", - "dlv": "^1.1.0", - "eslint": "^5.0.0", - "indent-string": "^4.0.0", - "lodash.merge": "^4.6.0", - "loglevel-colored-level-prefix": "^1.0.0", - "prettier": "^1.7.0", - "pretty-format": "^23.0.1", - "require-relative": "^0.8.7", - "typescript": "^3.2.1", - "vue-eslint-parser": "^2.0.2" - }, - "dependencies": { - "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } - } - }, - "prettier-eslint-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/prettier-eslint-cli/-/prettier-eslint-cli-5.0.0.tgz", - "integrity": "sha512-cei9UbN1aTrz3sQs88CWpvY/10PYTevzd76zoG1tdJ164OhmNTFRKPTOZrutVvscoQWzbnLKkviS3gu5JXwvZg==", - "dev": true, - "requires": { - "arrify": "^2.0.1", - "boolify": "^1.0.0", - "camelcase-keys": "^6.0.0", - "chalk": "^2.4.2", - "common-tags": "^1.8.0", - "core-js": "^3.1.4", - "eslint": "^5.0.0", - "find-up": "^4.1.0", - "get-stdin": "^7.0.0", - "glob": "^7.1.4", - "ignore": "^5.1.2", - "lodash.memoize": "^4.1.2", - "loglevel-colored-level-prefix": "^1.0.0", - "messageformat": "^2.2.1", - "prettier-eslint": "^9.0.0", - "rxjs": "^6.5.2", - "yargs": "^13.2.4" - }, - "dependencies": { - "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "get-stdin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", - "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", - "dev": true - }, - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true - }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } - } - }, "prettier-linter-helpers": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", @@ -5620,24 +4487,6 @@ "fast-diff": "^1.1.2" } }, - "pretty-format": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", - "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha512-wFUFA5bg5dviipbQQ32yOQhl6gcJaJXiHE7dvR8VYPG97+J/GNC5FKGepKdEDUFeXRzDxPF1X/Btc8L+v7oqIQ==", - "dev": true - } - } - }, "pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -5665,17 +4514,6 @@ "tdigest": "^0.1.1" } }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, "protobufjs": { "version": "6.8.9", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.9.tgz", @@ -5767,17 +4605,20 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", "dev": true }, - "ramda": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz", - "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==", - "dev": true + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } }, "range-parser": { "version": "1.2.1", @@ -5835,11 +4676,6 @@ "strip-json-comments": "~2.0.1" }, "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -5847,31 +4683,25 @@ } } }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "load-json-file": "^2.0.0", + "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "path-type": "^3.0.0" } }, "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "read-pkg": "^3.0.0" } }, "readable-stream": { @@ -5889,12 +4719,12 @@ } }, "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { - "picomatch": "^2.0.4" + "picomatch": "^2.2.1" } }, "redis": { @@ -5920,26 +4750,10 @@ "redis-errors": "^1.0.0" } }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "request": { @@ -5997,6 +4811,12 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require-in-the-middle": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz", @@ -6005,17 +4825,6 @@ "debug": "^4.1.1", "module-details-from-path": "^1.0.3", "resolve": "^1.12.0" - }, - "dependencies": { - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - } } }, "require-like": { @@ -6024,18 +4833,6 @@ "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", "dev": true }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", - "dev": true - }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -6046,11 +4843,11 @@ } }, "resolve": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", - "dev": true, + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "requires": { + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -6059,16 +4856,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==" }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "retry-request": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz", @@ -6102,24 +4889,6 @@ } } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha512-Fx+QT3fGtS0jk8OvKyKgAB2YHPsrmqBRcMeTC5AZ+lp4vzXKPPrFSY3iLdgvjA3HVBkIvJeM6J80LRjx8bQwhA==", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "s3-streams": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/s3-streams/-/s3-streams-0.4.0.tgz", @@ -6224,6 +4993,15 @@ } } }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -6246,18 +5024,18 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "shimmer": { @@ -6265,16 +5043,6 @@ "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, - "side-channel": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", - "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", - "dev": true, - "requires": { - "es-abstract": "^1.17.0-next.1", - "object-inspect": "^1.7.0" - } - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -6319,20 +5087,20 @@ } }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true } } @@ -6370,9 +5138,9 @@ } }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -6380,15 +5148,15 @@ } }, "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -6396,9 +5164,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", "dev": true }, "split": { @@ -6461,59 +5229,34 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, - "string.prototype.matchall": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz", - "integrity": "sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==", + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "has-symbols": "^1.0.1", - "internal-slot": "^1.0.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "string_decoder": { @@ -6525,32 +5268,24 @@ } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "ansi-regex": "^5.0.0" } }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "stubs": { @@ -6559,66 +5294,44 @@ "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz", + "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } } } }, @@ -6693,21 +5406,6 @@ "integrity": "sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==", "dev": true }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, "to-no-case": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", @@ -6759,11 +5457,16 @@ } } }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true + "tsconfig-paths": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", + "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==", + "dev": true, + "requires": { + "json5": "^2.2.0", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } }, "tunnel-agent": { "version": "0.6.0", @@ -6779,12 +5482,12 @@ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -6794,9 +5497,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, "type-is": { @@ -6823,11 +5526,25 @@ } } }, - "typescript": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", - "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", - "dev": true + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } + } }, "underscore": { "version": "1.13.1", @@ -6879,9 +5596,9 @@ "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "validate-npm-package-license": { @@ -6909,74 +5626,6 @@ "extsprintf": "^1.2.0" } }, - "vue-eslint-parser": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", - "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.2", - "esquery": "^1.0.0", - "lodash": "^4.17.4" - }, - "dependencies": { - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ==", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==", - "dev": true - } - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - } - } - }, "walkdir": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", @@ -6988,19 +5637,26 @@ "integrity": "sha512-5cZ7mecD3eYcMiCH4wtRPA5iFJZ50BJYDfckI5RRpQiktMiYTcn0ccLTZOvcbBume+1304fQztxeNzNS9Gvrnw==" }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", - "dev": true + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } }, "wide-align": { "version": "1.1.3", @@ -7045,40 +5701,20 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { @@ -7086,15 +5722,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", @@ -7109,19 +5736,10 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, - "xregexp": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz", - "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==", - "dev": true, - "requires": { - "@babel/runtime-corejs3": "^7.8.3" - } - }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { @@ -7130,95 +5748,36 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" } }, "yn": { diff --git a/services/track-changes/package.json b/services/track-changes/package.json index c458f35ab7..d3a2d990db 100644 --- a/services/track-changes/package.json +++ b/services/track-changes/package.json @@ -13,9 +13,10 @@ "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", "test:unit": "npm run test:unit:_run -- --grep=$MOCHA_GREP", "nodemon": "nodemon --config nodemon.json", - "lint": "node_modules/.bin/eslint --max-warnings 0 .", - "format": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --list-different", - "format:fix": "node_modules/.bin/prettier-eslint $PWD'/**/*.js' --write" + "lint": "eslint --max-warnings 0 --format unix .", + "format": "prettier --list-different $PWD/'**/*.js'", + "format:fix": "prettier --write $PWD/'**/*.js'", + "lint:fix": "eslint --fix ." }, "dependencies": { "@overleaf/metrics": "^3.5.1", @@ -42,28 +43,22 @@ "underscore": "~1.13.1" }, "devDependencies": { - "babel-eslint": "^10.1.0", - "chai": "~4.2.0", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "cli": "^1.0.1", - "eslint": "^6.8.0", - "eslint-config-prettier": "^6.10.0", - "eslint-config-standard": "^14.1.0", - "eslint-config-standard-jsx": "^8.1.0", - "eslint-config-standard-react": "^9.2.0", - "eslint-plugin-chai-expect": "^2.1.0", - "eslint-plugin-chai-friendly": "^0.5.0", - "eslint-plugin-import": "^2.20.1", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-mocha": "^6.3.0", - "eslint-plugin-node": "^11.0.0", + "eslint": "^7.21.0", + "eslint-config-prettier": "^8.1.0", + "eslint-config-standard": "^16.0.2", + "eslint-plugin-chai-expect": "^2.2.0", + "eslint-plugin-chai-friendly": "^0.6.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-mocha": "^8.0.0", + "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-react": "^7.19.0", - "eslint-plugin-standard": "^4.0.1", "memorystream": "0.3.1", - "mocha": "^7.1.1", - "prettier": "^2.0.0", - "prettier-eslint-cli": "^5.0.0", + "mocha": "^8.3.2", + "prettier": "^2.2.1", "sandboxed-module": "~2.0.3", "sinon": "~9.0.1", "timekeeper": "2.2.0" From 9178394dd6d2f0ca4d727445e608d058ed160dac Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 13 Jul 2021 12:04:43 +0100 Subject: [PATCH 547/549] [misc] run format_fix and lint:fix --- services/track-changes/app.js | 12 +- .../track-changes/app/js/DiffGenerator.js | 31 +- services/track-changes/app/js/DiffManager.js | 50 +- .../app/js/DocumentUpdaterManager.js | 6 +- .../track-changes/app/js/HealthChecker.js | 64 +- .../track-changes/app/js/HttpController.js | 126 ++-- services/track-changes/app/js/LockManager.js | 69 +- services/track-changes/app/js/MongoAWS.js | 54 +- services/track-changes/app/js/MongoManager.js | 108 +-- services/track-changes/app/js/PackManager.js | 423 ++++++----- services/track-changes/app/js/PackWorker.js | 16 +- .../track-changes/app/js/ProjectIterator.js | 180 ++--- services/track-changes/app/js/RedisManager.js | 46 +- .../track-changes/app/js/RestoreManager.js | 2 +- .../track-changes/app/js/UpdateCompressor.js | 64 +- .../track-changes/app/js/UpdateTrimmer.js | 93 +-- .../track-changes/app/js/UpdatesManager.js | 713 +++++++++--------- .../track-changes/app/js/WebApiManager.js | 8 +- services/track-changes/app/js/mongodb.js | 2 +- .../track-changes/app/lib/diff_match_patch.js | 27 +- .../track-changes/config/settings.defaults.js | 38 +- .../acceptance/js/AppendingUpdatesTests.js | 114 +-- .../acceptance/js/ArchivingUpdatesTests.js | 56 +- .../acceptance/js/FlushingUpdatesTests.js | 58 +- .../test/acceptance/js/GettingADiffTests.js | 22 +- .../test/acceptance/js/GettingUpdatesTests.js | 40 +- .../test/acceptance/js/LockManagerTests.js | 8 +- .../test/acceptance/js/RestoringVersions.js | 18 +- .../acceptance/js/helpers/MockDocStoreApi.js | 6 +- .../js/helpers/MockDocUpdaterApi.js | 6 +- .../test/acceptance/js/helpers/MockWebApi.js | 6 +- .../acceptance/js/helpers/TrackChangesApp.js | 6 +- .../js/helpers/TrackChangesClient.js | 42 +- services/track-changes/test/setup.js | 6 +- .../js/DiffGenerator/DiffGeneratorTests.js | 94 +-- .../unit/js/DiffManager/DiffManagerTests.js | 26 +- .../test/unit/js/DocArchive/MongoAWS.js | 12 +- .../DocumentUpdaterManagerTests.js | 12 +- .../js/HttpController/HttpControllerTests.js | 34 +- .../unit/js/LockManager/LockManagerTests.js | 12 +- .../unit/js/MongoManager/MongoManagerTests.js | 18 +- .../unit/js/PackManager/PackManagerTests.js | 60 +- .../unit/js/RedisManager/RedisManagerTests.js | 20 +- .../js/RestoreManager/RestoreManagerTests.js | 4 +- .../UpdateCompressor/UpdateCompressorTests.js | 356 ++++----- .../js/UpdateTrimmer/UpdateTrimmerTests.js | 4 +- .../js/UpdatesManager/UpdatesManagerTests.js | 266 +++---- .../js/WebApiManager/WebApiManagerTests.js | 22 +- 48 files changed, 1742 insertions(+), 1718 deletions(-) diff --git a/services/track-changes/app.js b/services/track-changes/app.js index 4245231b8d..a009431de0 100644 --- a/services/track-changes/app.js +++ b/services/track-changes/app.js @@ -16,7 +16,7 @@ if ((Settings.sentry != null ? Settings.sentry.dsn : undefined) != null) { } // log updates as truncated strings -const truncateFn = (updates) => +const truncateFn = updates => JSON.parse( JSON.stringify(updates, function (key, value) { let len @@ -35,7 +35,7 @@ TrackChangesLogger.addSerializers({ rawUpdate: truncateFn, rawUpdates: truncateFn, newUpdates: truncateFn, - lastUpdate: truncateFn + lastUpdate: truncateFn, }) const Path = require('path') @@ -91,7 +91,7 @@ app.post('/pack', function (req, res, next) { [ req.query.limit || 1000, req.query.delay || 1000, - req.query.timeout || 30 * 60 * 1000 + req.query.timeout || 30 * 60 * 1000, ] ) packWorker.on('exit', function (code, signal) { @@ -120,12 +120,12 @@ app.use(function (error, req, res, next) { const port = __guard__( Settings.internal != null ? Settings.internal.trackchanges : undefined, - (x) => x.port + x => x.port ) || 3015 const host = __guard__( Settings.internal != null ? Settings.internal.trackchanges : undefined, - (x1) => x1.host + x1 => x1.host ) || 'localhost' if (!module.parent) { @@ -146,7 +146,7 @@ if (!module.parent) { } }) }) - .catch((err) => { + .catch(err => { logger.fatal({ err }, 'Cannot connect to mongo. Exiting.') process.exit(1) }) diff --git a/services/track-changes/app/js/DiffGenerator.js b/services/track-changes/app/js/DiffGenerator.js index ebc6a9fcb1..0d8ef0be94 100644 --- a/services/track-changes/app/js/DiffGenerator.js +++ b/services/track-changes/app/js/DiffGenerator.js @@ -162,13 +162,11 @@ module.exports = DiffGenerator = { if (op.i != null) { newDiff.push({ i: op.i, - meta + meta, }) } else if (op.d != null) { - ;({ - consumedDiff, - remainingDiff - } = DiffGenerator._consumeDiffAffectedByDeleteOp(remainingDiff, op, meta)) + ;({ consumedDiff, remainingDiff } = + DiffGenerator._consumeDiffAffectedByDeleteOp(remainingDiff, op, meta)) newDiff.push(...Array.from(consumedDiff || [])) } @@ -211,7 +209,7 @@ module.exports = DiffGenerator = { return { consumedDiff, - remainingDiff + remainingDiff, } }, @@ -220,18 +218,15 @@ module.exports = DiffGenerator = { let remainingOp = deleteOp while (remainingOp && remainingDiff.length > 0) { let newPart - ;({ - newPart, - remainingDiff, - remainingOp - } = DiffGenerator._consumeDeletedPart(remainingDiff, remainingOp, meta)) + ;({ newPart, remainingDiff, remainingOp } = + DiffGenerator._consumeDeletedPart(remainingDiff, remainingOp, meta)) if (newPart != null) { consumedDiff.push(newPart) } } return { consumedDiff, - remainingDiff + remainingDiff, } }, @@ -262,7 +257,7 @@ module.exports = DiffGenerator = { if (part.u != null) { newPart = { d: op.d, - meta + meta, } } else if (part.i != null) { newPart = null @@ -282,7 +277,7 @@ module.exports = DiffGenerator = { if (part.u != null) { newPart = { d: op.d, - meta + meta, } } else if (part.i != null) { newPart = null @@ -303,7 +298,7 @@ module.exports = DiffGenerator = { if (part.u) { newPart = { d: part.u, - meta + meta, } } else if (part.i != null) { newPart = null @@ -311,14 +306,14 @@ module.exports = DiffGenerator = { remainingOp = { p: op.p, - d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)) + d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part)), } } return { newPart, remainingDiff, - remainingOp + remainingOp, } }, @@ -341,5 +336,5 @@ module.exports = DiffGenerator = { _getContentOfPart(part) { return part.u || part.d || part.i || '' - } + }, } diff --git a/services/track-changes/app/js/DiffManager.js b/services/track-changes/app/js/DiffManager.js index a33ed07c2d..7c8a9d4ceb 100644 --- a/services/track-changes/app/js/DiffManager.js +++ b/services/track-changes/app/js/DiffManager.js @@ -24,30 +24,30 @@ module.exports = DiffManager = { if (callback == null) { callback = function (error, content, version, updates) {} } - return DocumentUpdaterManager.getDocument(project_id, doc_id, function ( - error, - content, - version - ) { - if (error != null) { - return callback(error) - } - if (fromVersion == null) { - // If we haven't been given a version, just return lastest doc and no updates - return callback(null, content, version, []) - } - return UpdatesManager.getDocUpdatesWithUserInfo( - project_id, - doc_id, - { from: fromVersion }, - function (error, updates) { - if (error != null) { - return callback(error) - } - return callback(null, content, version, updates) + return DocumentUpdaterManager.getDocument( + project_id, + doc_id, + function (error, content, version) { + if (error != null) { + return callback(error) } - ) - }) + if (fromVersion == null) { + // If we haven't been given a version, just return lastest doc and no updates + return callback(null, content, version, []) + } + return UpdatesManager.getDocUpdatesWithUserInfo( + project_id, + doc_id, + { from: fromVersion }, + function (error, updates) { + if (error != null) { + return callback(error) + } + return callback(null, content, version, updates) + } + ) + } + ) }, getDiff(project_id, doc_id, fromVersion, toVersion, callback) { @@ -167,7 +167,7 @@ module.exports = DiffManager = { { docVersion: version, lastUpdateVersion: lastUpdate != null ? lastUpdate.v : undefined, - updateCount: updates.length + updateCount: updates.length, }, 'rewinding updates' ) @@ -184,5 +184,5 @@ module.exports = DiffManager = { return callback(null, startingContent, tryUpdates) } ) - } + }, } diff --git a/services/track-changes/app/js/DocumentUpdaterManager.js b/services/track-changes/app/js/DocumentUpdaterManager.js index e3a7c2ce10..01cdf9e07c 100644 --- a/services/track-changes/app/js/DocumentUpdaterManager.js +++ b/services/track-changes/app/js/DocumentUpdaterManager.js @@ -65,8 +65,8 @@ module.exports = DocumentUpdaterManager = { lines: content.split('\n'), source: 'restore', user_id, - undoing: true - } + undoing: true, + }, }, function (error, res, body) { if (error != null) { @@ -86,5 +86,5 @@ module.exports = DocumentUpdaterManager = { } } ) - } + }, } diff --git a/services/track-changes/app/js/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js index 7a645b254d..94ba2371a8 100644 --- a/services/track-changes/app/js/HealthChecker.js +++ b/services/track-changes/app/js/HealthChecker.js @@ -24,7 +24,7 @@ module.exports = { const url = `http://localhost:${port}/project/${project_id}` logger.log({ project_id }, 'running health check') const jobs = [ - (cb) => + cb => request.get( { url: `http://localhost:${port}/check_lock`, timeout: 3000 }, function (err, res, body) { @@ -41,44 +41,42 @@ module.exports = { } } ), - (cb) => - request.post({ url: `${url}/flush`, timeout: 10000 }, function ( - err, - res, - body - ) { - if (err != null) { - logger.err({ err, project_id }, 'error flushing for health check') - return cb(err) - } else if ((res != null ? res.statusCode : undefined) !== 204) { - return cb(`status code not 204, it's ${res.statusCode}`) - } else { - return cb() + cb => + request.post( + { url: `${url}/flush`, timeout: 10000 }, + function (err, res, body) { + if (err != null) { + logger.err({ err, project_id }, 'error flushing for health check') + return cb(err) + } else if ((res != null ? res.statusCode : undefined) !== 204) { + return cb(`status code not 204, it's ${res.statusCode}`) + } else { + return cb() + } } - }), - (cb) => - request.get({ url: `${url}/updates`, timeout: 10000 }, function ( - err, - res, - body - ) { - if (err != null) { - logger.err( - { err, project_id }, - 'error getting updates for health check' - ) - return cb(err) - } else if ((res != null ? res.statusCode : undefined) !== 200) { - return cb(`status code not 200, it's ${res.statusCode}`) - } else { - return cb() + ), + cb => + request.get( + { url: `${url}/updates`, timeout: 10000 }, + function (err, res, body) { + if (err != null) { + logger.err( + { err, project_id }, + 'error getting updates for health check' + ) + return cb(err) + } else if ((res != null ? res.statusCode : undefined) !== 200) { + return cb(`status code not 200, it's ${res.statusCode}`) + } else { + return cb() + } } - }) + ), ] return async.series(jobs, callback) }, checkLock(callback) { return LockManager.healthCheck(callback) - } + }, } diff --git a/services/track-changes/app/js/HttpController.js b/services/track-changes/app/js/HttpController.js index c5db48210a..8f665682eb 100644 --- a/services/track-changes/app/js/HttpController.js +++ b/services/track-changes/app/js/HttpController.js @@ -160,15 +160,18 @@ module.exports = HttpController = { } logger.log({ project_id, doc_id, from, to }, 'getting diff') - return DiffManager.getDiff(project_id, doc_id, from, to, function ( - error, - diff - ) { - if (error != null) { - return next(error) + return DiffManager.getDiff( + project_id, + doc_id, + from, + to, + function (error, diff) { + if (error != null) { + return next(error) + } + return res.json({ diff }) } - return res.json({ diff }) - }) + ) }, getUpdates(req, res, next) { @@ -194,7 +197,7 @@ module.exports = HttpController = { } return res.json({ updates, - nextBeforeTimestamp + nextBeforeTimestamp, }) } ) @@ -207,60 +210,59 @@ module.exports = HttpController = { // Flush updates per pack onto the wire. const { project_id } = req.params logger.log({ project_id }, 'exporting project history') - UpdatesManager.exportProject(project_id, function ( - err, - { updates, userIds }, - confirmWrite - ) { - const abortStreaming = req.aborted || res.finished || res.destroyed - if (abortStreaming) { - // Tell the producer to stop emitting data - if (confirmWrite) confirmWrite(new Error('stop')) - return - } - const hasStartedStreamingResponse = res.headersSent - if (err) { - logger.error({ project_id, err }, 'export failed') - if (!hasStartedStreamingResponse) { - // Generate a nice 500 - return next(err) - } else { - // Stop streaming - return res.destroy() + UpdatesManager.exportProject( + project_id, + function (err, { updates, userIds }, confirmWrite) { + const abortStreaming = req.aborted || res.finished || res.destroyed + if (abortStreaming) { + // Tell the producer to stop emitting data + if (confirmWrite) confirmWrite(new Error('stop')) + return + } + const hasStartedStreamingResponse = res.headersSent + if (err) { + logger.error({ project_id, err }, 'export failed') + if (!hasStartedStreamingResponse) { + // Generate a nice 500 + return next(err) + } else { + // Stop streaming + return res.destroy() + } + } + // Compose the response incrementally + const isFirstWrite = !hasStartedStreamingResponse + const isLastWrite = updates.length === 0 + if (isFirstWrite) { + // The first write will emit the 200 status, headers and start of the + // response payload (open array) + res.setHeader('Content-Type', 'application/json') + res.setHeader('Trailer', 'X-User-Ids') + res.writeHead(200) + res.write('[') + } + if (!isFirstWrite && !isLastWrite) { + // Starting from the 2nd non-empty write, emit a continuing comma. + // write 1: [updates1 + // write 2: ,updates2 + // write 3: ,updates3 + // write N: ] + res.write(',') + } + + // Every write will emit a blob onto the response stream: + // '[update1,update2,...]' + // ^^^^^^^^^^^^^^^^^^^ + res.write(JSON.stringify(updates).slice(1, -1), confirmWrite) + + if (isLastWrite) { + // The last write will have no updates and will finish the response + // payload (close array) and emit the userIds as trailer. + res.addTrailers({ 'X-User-Ids': JSON.stringify(userIds) }) + res.end(']') } } - // Compose the response incrementally - const isFirstWrite = !hasStartedStreamingResponse - const isLastWrite = updates.length === 0 - if (isFirstWrite) { - // The first write will emit the 200 status, headers and start of the - // response payload (open array) - res.setHeader('Content-Type', 'application/json') - res.setHeader('Trailer', 'X-User-Ids') - res.writeHead(200) - res.write('[') - } - if (!isFirstWrite && !isLastWrite) { - // Starting from the 2nd non-empty write, emit a continuing comma. - // write 1: [updates1 - // write 2: ,updates2 - // write 3: ,updates3 - // write N: ] - res.write(',') - } - - // Every write will emit a blob onto the response stream: - // '[update1,update2,...]' - // ^^^^^^^^^^^^^^^^^^^ - res.write(JSON.stringify(updates).slice(1, -1), confirmWrite) - - if (isLastWrite) { - // The last write will have no updates and will finish the response - // payload (close array) and emit the userIds as trailer. - res.addTrailers({ 'X-User-Ids': JSON.stringify(userIds) }) - res.end(']') - } - }) + ) }, restore(req, res, next) { @@ -334,5 +336,5 @@ module.exports = HttpController = { return res.sendStatus(200) } }) - } + }, } diff --git a/services/track-changes/app/js/LockManager.js b/services/track-changes/app/js/LockManager.js index 6cdb03219a..8be04dbaae 100644 --- a/services/track-changes/app/js/LockManager.js +++ b/services/track-changes/app/js/LockManager.js @@ -43,19 +43,23 @@ module.exports = LockManager = { callback = function (err, gotLock) {} } const lockValue = LockManager.randomLock() - return rclient.set(key, lockValue, 'EX', this.LOCK_TTL, 'NX', function ( - err, - gotLock - ) { - if (err != null) { - return callback(err) + return rclient.set( + key, + lockValue, + 'EX', + this.LOCK_TTL, + 'NX', + function (err, gotLock) { + if (err != null) { + return callback(err) + } + if (gotLock === 'OK') { + return callback(err, true, lockValue) + } else { + return callback(err, false) + } } - if (gotLock === 'OK') { - return callback(err, true, lockValue) - } else { - return callback(err, false) - } - }) + ) }, getLock(key, callback) { @@ -102,23 +106,26 @@ module.exports = LockManager = { }, releaseLock(key, lockValue, callback) { - return rclient.eval(LockManager.unlockScript, 1, key, lockValue, function ( - err, - result - ) { - if (err != null) { - return callback(err) + return rclient.eval( + LockManager.unlockScript, + 1, + key, + lockValue, + function (err, result) { + if (err != null) { + return callback(err) + } + if (result != null && result !== 1) { + // successful unlock should release exactly one key + logger.error( + { key, lockValue, redis_err: err, redis_result: result }, + 'unlocking error' + ) + return callback(new Error('tried to release timed out lock')) + } + return callback(err, result) } - if (result != null && result !== 1) { - // successful unlock should release exactly one key - logger.error( - { key, lockValue, redis_err: err, redis_result: result }, - 'unlocking error' - ) - return callback(new Error('tried to release timed out lock')) - } - return callback(err, result) - }) + ) }, runWithLock(key, runner, callback) { @@ -129,7 +136,7 @@ module.exports = LockManager = { if (error != null) { return callback(error) } - return runner((error1) => + return runner(error1 => LockManager.releaseLock(key, lockValue, function (error2) { error = error1 || error2 if (error != null) { @@ -142,7 +149,7 @@ module.exports = LockManager = { }, healthCheck(callback) { - const action = (releaseLock) => releaseLock() + const action = releaseLock => releaseLock() return LockManager.runWithLock( `HistoryLock:HealthCheck:host=${HOST}:pid=${PID}:random=${RND}`, action, @@ -153,5 +160,5 @@ module.exports = LockManager = { close(callback) { rclient.quit() return rclient.once('end', callback) - } + }, } diff --git a/services/track-changes/app/js/MongoAWS.js b/services/track-changes/app/js/MongoAWS.js index cc4001e9ed..f9e69b12c2 100644 --- a/services/track-changes/app/js/MongoAWS.js +++ b/services/track-changes/app/js/MongoAWS.js @@ -31,12 +31,12 @@ const createStream = function (streamConstructor, project_id, doc_id, pack_id) { accessKeyId: settings.trackchanges.s3.key, secretAccessKey: settings.trackchanges.s3.secret, endpoint: settings.trackchanges.s3.endpoint, - s3ForcePathStyle: settings.trackchanges.s3.pathStyle + s3ForcePathStyle: settings.trackchanges.s3.pathStyle, } return streamConstructor(new AWS.S3(AWS_CONFIG), { Bucket: settings.trackchanges.stores.doc_history, - Key: project_id + '/changes-' + doc_id + '/pack-' + pack_id + Key: project_id + '/changes-' + doc_id + '/pack-' + pack_id, }) } @@ -52,7 +52,7 @@ module.exports = MongoAWS = { const query = { _id: ObjectId(pack_id), - doc_id: ObjectId(doc_id) + doc_id: ObjectId(doc_id), } if (project_id == null) { @@ -92,14 +92,14 @@ module.exports = MongoAWS = { doc_id, pack_id, origSize: uncompressedData.length, - newSize: buf.length + newSize: buf.length, }, 'compressed pack' ) if (err != null) { return callback(err) } - upload.on('error', (err) => callback(err)) + upload.on('error', err => callback(err)) upload.on('finish', function () { Metrics.inc('archive-pack') logger.log({ project_id, doc_id, pack_id }, 'upload to s3 completed') @@ -135,8 +135,8 @@ module.exports = MongoAWS = { const download = createStream(S3S.ReadStream, project_id, doc_id, pack_id) const inputStream = download - .on('open', (obj) => 1) - .on('error', (err) => callback(err)) + .on('open', obj => 1) + .on('error', err => callback(err)) const gunzip = zlib.createGunzip() gunzip.setEncoding('utf8') @@ -150,7 +150,7 @@ module.exports = MongoAWS = { const outputStream = inputStream.pipe(gunzip) const parts = [] - outputStream.on('error', (err) => callback(err)) + outputStream.on('error', err => callback(err)) outputStream.on('end', function () { let object logger.log({ project_id, doc_id, pack_id }, 'download from s3 completed') @@ -169,29 +169,31 @@ module.exports = MongoAWS = { } return callback(null, object) }) - return outputStream.on('data', (data) => parts.push(data)) + return outputStream.on('data', data => parts.push(data)) }, unArchivePack(project_id, doc_id, pack_id, callback) { if (callback == null) { callback = function (error) {} } - return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function ( - err, - object - ) { - if (err != null) { - return callback(err) + return MongoAWS.readArchivedPack( + project_id, + doc_id, + pack_id, + function (err, object) { + if (err != null) { + return callback(err) + } + Metrics.inc('unarchive-pack') + // allow the object to expire, we can always retrieve it again + object.expiresAt = new Date(Date.now() + 7 * DAYS) + logger.log({ project_id, doc_id, pack_id }, 'inserting object from s3') + return db.docHistory.insertOne(object, (err, confirmation) => { + if (err) return callback(err) + object._id = confirmation.insertedId + callback(null, object) + }) } - Metrics.inc('unarchive-pack') - // allow the object to expire, we can always retrieve it again - object.expiresAt = new Date(Date.now() + 7 * DAYS) - logger.log({ project_id, doc_id, pack_id }, 'inserting object from s3') - return db.docHistory.insertOne(object, (err, confirmation) => { - if (err) return callback(err) - object._id = confirmation.insertedId - callback(null, object) - }) - }) - } + ) + }, } diff --git a/services/track-changes/app/js/MongoManager.js b/services/track-changes/app/js/MongoManager.js index bfcdcf1ddb..6dfdd46ce3 100644 --- a/services/track-changes/app/js/MongoManager.js +++ b/services/track-changes/app/js/MongoManager.js @@ -50,50 +50,53 @@ module.exports = MongoManager = { if (callback == null) { callback = function (error, update, version) {} } - return MongoManager.getLastCompressedUpdate(doc_id, function ( - error, - update - ) { - if (error != null) { - return callback(error) - } - if (update != null) { - if (update.broken) { - // marked as broken so we will force a new op - return callback(null, null) - } else if (update.pack != null) { - if (update.finalised) { - // no more ops can be appended - return callback( - null, - null, - update.pack[0] != null ? update.pack[0].v : undefined - ) + return MongoManager.getLastCompressedUpdate( + doc_id, + function (error, update) { + if (error != null) { + return callback(error) + } + if (update != null) { + if (update.broken) { + // marked as broken so we will force a new op + return callback(null, null) + } else if (update.pack != null) { + if (update.finalised) { + // no more ops can be appended + return callback( + null, + null, + update.pack[0] != null ? update.pack[0].v : undefined + ) + } else { + return callback( + null, + update, + update.pack[0] != null ? update.pack[0].v : undefined + ) + } } else { - return callback( - null, - update, - update.pack[0] != null ? update.pack[0].v : undefined - ) + return callback(null, update, update.v) } } else { - return callback(null, update, update.v) + return PackManager.getLastPackFromIndex( + doc_id, + function (error, pack) { + if (error != null) { + return callback(error) + } + if ( + (pack != null ? pack.inS3 : undefined) != null && + (pack != null ? pack.v_end : undefined) != null + ) { + return callback(null, null, pack.v_end) + } + return callback(null, null) + } + ) } - } else { - return PackManager.getLastPackFromIndex(doc_id, function (error, pack) { - if (error != null) { - return callback(error) - } - if ( - (pack != null ? pack.inS3 : undefined) != null && - (pack != null ? pack.v_end : undefined) != null - ) { - return callback(null, null, pack.v_end) - } - return callback(null, null) - }) } - }) + ) }, backportProjectId(project_id, doc_id, callback) { @@ -103,10 +106,10 @@ module.exports = MongoManager = { return db.docHistory.updateMany( { doc_id: ObjectId(doc_id.toString()), - project_id: { $exists: false } + project_id: { $exists: false }, }, { - $set: { project_id: ObjectId(project_id.toString()) } + $set: { project_id: ObjectId(project_id.toString()) }, }, callback ) @@ -118,7 +121,7 @@ module.exports = MongoManager = { } return db.projectHistoryMetaData.findOne( { - project_id: ObjectId(project_id.toString()) + project_id: ObjectId(project_id.toString()), }, callback ) @@ -130,13 +133,13 @@ module.exports = MongoManager = { } return db.projectHistoryMetaData.updateOne( { - project_id: ObjectId(project_id) + project_id: ObjectId(project_id), }, { - $set: metadata + $set: metadata, }, { - upsert: true + upsert: true, }, callback ) @@ -151,11 +154,11 @@ module.exports = MongoManager = { { project_id: ObjectId(project_id), temporary: true, - expiresAt: { $exists: true } + expiresAt: { $exists: true }, }, { $set: { temporary: false }, - $unset: { expiresAt: '' } + $unset: { expiresAt: '' }, }, callback ) @@ -191,12 +194,9 @@ module.exports = MongoManager = { { project_id: 1 }, { background: true } ) - } + }, } -;[ - 'getLastCompressedUpdate', - 'getProjectMetaData', - 'setProjectMetaData' -].map((method) => - metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger) +;['getLastCompressedUpdate', 'getProjectMetaData', 'setProjectMetaData'].map( + method => + metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger) ) diff --git a/services/track-changes/app/js/PackManager.js b/services/track-changes/app/js/PackManager.js index 509ba3e1c7..f8efb77f11 100644 --- a/services/track-changes/app/js/PackManager.js +++ b/services/track-changes/app/js/PackManager.js @@ -206,11 +206,11 @@ module.exports = PackManager = { sz, meta: { start_ts: first.meta.start_ts, - end_ts: last.meta.end_ts + end_ts: last.meta.end_ts, }, v: first.v, v_end: last.v, - temporary + temporary, } if (temporary) { newPack.expiresAt = new Date(Date.now() + 7 * DAYS) @@ -252,20 +252,20 @@ module.exports = PackManager = { _id: lastUpdate._id, project_id: ObjectId(project_id.toString()), doc_id: ObjectId(doc_id.toString()), - pack: { $exists: true } + pack: { $exists: true }, } const update = { $push: { - pack: { $each: newUpdates } + pack: { $each: newUpdates }, }, $inc: { n: n, - sz: sz + sz: sz, }, $set: { 'meta.end_ts': last.meta.end_ts, - v_end: last.v - } + v_end: last.v, + }, } if (lastUpdate.expiresAt && temporary) { update.$set.expiresAt = new Date(Date.now() + 7 * DAYS) @@ -396,7 +396,7 @@ module.exports = PackManager = { } return result1 })() - const loadedPackIds = Array.from(loadedPacks).map((pack) => + const loadedPackIds = Array.from(loadedPacks).map(pack => pack._id.toString() ) const packIdsToFetch = _.difference(allPackIds, loadedPackIds) @@ -494,7 +494,7 @@ module.exports = PackManager = { return db.docHistory.updateOne( { _id: pack._id }, { $set: { expiresAt: new Date(Date.now() + 7 * DAYS) } }, - (err) => callback(err, pack) + err => callback(err, pack) ) } else { return callback(null, pack) @@ -552,60 +552,62 @@ module.exports = PackManager = { }, initialiseIndex(project_id, doc_id, callback) { - return PackManager.findCompletedPacks(project_id, doc_id, function ( - err, - packs - ) { - // console.log 'err', err, 'packs', packs, packs?.length - if (err != null) { - return callback(err) + return PackManager.findCompletedPacks( + project_id, + doc_id, + function (err, packs) { + // console.log 'err', err, 'packs', packs, packs?.length + if (err != null) { + return callback(err) + } + if (packs == null) { + return callback() + } + return PackManager.insertPacksIntoIndexWithLock( + project_id, + doc_id, + packs, + callback + ) } - if (packs == null) { - return callback() - } - return PackManager.insertPacksIntoIndexWithLock( - project_id, - doc_id, - packs, - callback - ) - }) + ) }, updateIndex(project_id, doc_id, callback) { // find all packs prior to current pack - return PackManager.findUnindexedPacks(project_id, doc_id, function ( - err, - newPacks - ) { - if (err != null) { - return callback(err) - } - if (newPacks == null || newPacks.length === 0) { - return callback() - } - return PackManager.insertPacksIntoIndexWithLock( - project_id, - doc_id, - newPacks, - function (err) { - if (err != null) { - return callback(err) - } - logger.log( - { project_id, doc_id, newPacks }, - 'added new packs to index' - ) + return PackManager.findUnindexedPacks( + project_id, + doc_id, + function (err, newPacks) { + if (err != null) { + return callback(err) + } + if (newPacks == null || newPacks.length === 0) { return callback() } - ) - }) + return PackManager.insertPacksIntoIndexWithLock( + project_id, + doc_id, + newPacks, + function (err) { + if (err != null) { + return callback(err) + } + logger.log( + { project_id, doc_id, newPacks }, + 'added new packs to index' + ) + return callback() + } + ) + } + ) }, findCompletedPacks(project_id, doc_id, callback) { const query = { doc_id: ObjectId(doc_id.toString()), - expiresAt: { $exists: false } + expiresAt: { $exists: false }, } return db.docHistory .find(query, { projection: { pack: false } }) @@ -631,7 +633,7 @@ module.exports = PackManager = { findPacks(project_id, doc_id, callback) { const query = { doc_id: ObjectId(doc_id.toString()), - expiresAt: { $exists: false } + expiresAt: { $exists: false }, } return db.docHistory .find(query, { projection: { pack: false } }) @@ -655,61 +657,63 @@ module.exports = PackManager = { if (err != null) { return callback(err) } - return PackManager.findCompletedPacks(project_id, doc_id, function ( - err, - historyPacks - ) { - let pack - if (err != null) { - return callback(err) - } - if (historyPacks == null) { - return callback() - } - // select only the new packs not already in the index - let newPacks = (() => { - const result = [] - for (pack of Array.from(historyPacks)) { - if ( - (indexResult != null ? indexResult[pack._id] : undefined) == null - ) { - result.push(pack) - } + return PackManager.findCompletedPacks( + project_id, + doc_id, + function (err, historyPacks) { + let pack + if (err != null) { + return callback(err) } - return result - })() - newPacks = (() => { - const result1 = [] - for (pack of Array.from(newPacks)) { - result1.push( - _.omit( - pack, - 'doc_id', - 'project_id', - 'n', - 'sz', - 'last_checked', - 'finalised' + if (historyPacks == null) { + return callback() + } + // select only the new packs not already in the index + let newPacks = (() => { + const result = [] + for (pack of Array.from(historyPacks)) { + if ( + (indexResult != null ? indexResult[pack._id] : undefined) == + null + ) { + result.push(pack) + } + } + return result + })() + newPacks = (() => { + const result1 = [] + for (pack of Array.from(newPacks)) { + result1.push( + _.omit( + pack, + 'doc_id', + 'project_id', + 'n', + 'sz', + 'last_checked', + 'finalised' + ) ) + } + return result1 + })() + if (newPacks.length) { + logger.log( + { project_id, doc_id, n: newPacks.length }, + 'found new packs' ) } - return result1 - })() - if (newPacks.length) { - logger.log( - { project_id, doc_id, n: newPacks.length }, - 'found new packs' - ) + return callback(null, newPacks) } - return callback(null, newPacks) - }) + ) }) }, insertPacksIntoIndexWithLock(project_id, doc_id, newPacks, callback) { return LockManager.runWithLock( keys.historyIndexLock({ doc_id }), - (releaseLock) => + releaseLock => PackManager._insertPacksIntoIndex( project_id, doc_id, @@ -726,11 +730,11 @@ module.exports = PackManager = { { $setOnInsert: { project_id: ObjectId(project_id.toString()) }, $push: { - packs: { $each: newPacks, $sort: { v: 1 } } - } + packs: { $each: newPacks, $sort: { v: 1 } }, + }, }, { - upsert: true + upsert: true, }, callback ) @@ -759,36 +763,36 @@ module.exports = PackManager = { } return async.series( [ - (cb) => + cb => PackManager.checkArchiveNotInProgress( project_id, doc_id, pack_id, cb ), - (cb) => + cb => PackManager.markPackAsArchiveInProgress( project_id, doc_id, pack_id, cb ), - (cb) => - MongoAWS.archivePack(project_id, doc_id, pack_id, (err) => + cb => + MongoAWS.archivePack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb) ), - (cb) => - PackManager.checkArchivedPack(project_id, doc_id, pack_id, (err) => + cb => + PackManager.checkArchivedPack(project_id, doc_id, pack_id, err => clearFlagOnError(err, cb) ), - (cb) => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), - (cb) => + cb => PackManager.markPackAsArchived(project_id, doc_id, pack_id, cb), + cb => PackManager.setTTLOnArchivedPack( project_id, doc_id, pack_id, callback - ) + ), ], callback ) @@ -802,40 +806,42 @@ module.exports = PackManager = { if (pack == null) { return callback(new Error('pack not found')) } - return MongoAWS.readArchivedPack(project_id, doc_id, pack_id, function ( - err, - result - ) { - delete result.last_checked - delete pack.last_checked - // need to compare ids as ObjectIds with .equals() - for (const key of ['_id', 'project_id', 'doc_id']) { - if (result[key].equals(pack[key])) { - result[key] = pack[key] + return MongoAWS.readArchivedPack( + project_id, + doc_id, + pack_id, + function (err, result) { + delete result.last_checked + delete pack.last_checked + // need to compare ids as ObjectIds with .equals() + for (const key of ['_id', 'project_id', 'doc_id']) { + if (result[key].equals(pack[key])) { + result[key] = pack[key] + } + } + for (let i = 0; i < result.pack.length; i++) { + const op = result.pack[i] + if (op._id != null && op._id.equals(pack.pack[i]._id)) { + op._id = pack.pack[i]._id + } + } + if (_.isEqual(pack, result)) { + return callback() + } else { + logger.err( + { + pack, + result, + jsondiff: JSON.stringify(pack) === JSON.stringify(result), + }, + 'difference when comparing packs' + ) + return callback( + new Error('pack retrieved from s3 does not match pack in mongo') + ) } } - for (let i = 0; i < result.pack.length; i++) { - const op = result.pack[i] - if (op._id != null && op._id.equals(pack.pack[i]._id)) { - op._id = pack.pack[i]._id - } - } - if (_.isEqual(pack, result)) { - return callback() - } else { - logger.err( - { - pack, - result, - jsondiff: JSON.stringify(pack) === JSON.stringify(result) - }, - 'difference when comparing packs' - ) - return callback( - new Error('pack retrieved from s3 does not match pack in mongo') - ) - } - }) + ) }) }, // Extra methods to test archive/unarchive for a doc_id @@ -870,15 +876,18 @@ module.exports = PackManager = { // Processing old packs via worker processOldPack(project_id, doc_id, pack_id, callback) { - const markAsChecked = (err) => - PackManager.markPackAsChecked(project_id, doc_id, pack_id, function ( - err2 - ) { - if (err2 != null) { - return callback(err2) + const markAsChecked = err => + PackManager.markPackAsChecked( + project_id, + doc_id, + pack_id, + function (err2) { + if (err2 != null) { + return callback(err2) + } + return callback(err) } - return callback(err) - }) + ) logger.log({ project_id, doc_id }, 'processing old packs') return db.docHistory.findOne({ _id: pack_id }, function (err, pack) { if (err != null) { @@ -899,42 +908,47 @@ module.exports = PackManager = { if (err != null) { return markAsChecked(err) } - return PackManager.updateIndexIfNeeded(project_id, doc_id, function ( - err - ) { - if (err != null) { - return markAsChecked(err) - } - return PackManager.findUnarchivedPacks( - project_id, - doc_id, - function (err, unarchivedPacks) { - if (err != null) { - return markAsChecked(err) - } - if ( - !(unarchivedPacks != null - ? unarchivedPacks.length - : undefined) - ) { - logger.log({ project_id, doc_id }, 'no packs need archiving') - return markAsChecked() - } - return async.eachSeries( - unarchivedPacks, - (pack, cb) => - PackManager.archivePack(project_id, doc_id, pack._id, cb), - function (err) { - if (err != null) { - return markAsChecked(err) - } - logger.log({ project_id, doc_id }, 'done processing') + return PackManager.updateIndexIfNeeded( + project_id, + doc_id, + function (err) { + if (err != null) { + return markAsChecked(err) + } + return PackManager.findUnarchivedPacks( + project_id, + doc_id, + function (err, unarchivedPacks) { + if (err != null) { + return markAsChecked(err) + } + if ( + !(unarchivedPacks != null + ? unarchivedPacks.length + : undefined) + ) { + logger.log( + { project_id, doc_id }, + 'no packs need archiving' + ) return markAsChecked() } - ) - } - ) - }) + return async.eachSeries( + unarchivedPacks, + (pack, cb) => + PackManager.archivePack(project_id, doc_id, pack._id, cb), + function (err) { + if (err != null) { + return markAsChecked(err) + } + logger.log({ project_id, doc_id }, 'done processing') + return markAsChecked() + } + ) + } + ) + } + ) } ) }) @@ -974,7 +988,7 @@ module.exports = PackManager = { markPackAsFinalisedWithLock(project_id, doc_id, pack_id, callback) { return LockManager.runWithLock( keys.historyLock({ doc_id }), - (releaseLock) => + releaseLock => PackManager._markPackAsFinalised( project_id, doc_id, @@ -1050,24 +1064,25 @@ module.exports = PackManager = { { project_id, doc_id, pack_id }, 'checking if archive in progress' ) - return PackManager.getPackFromIndex(doc_id, pack_id, function ( - err, - result - ) { - if (err != null) { - return callback(err) + return PackManager.getPackFromIndex( + doc_id, + pack_id, + function (err, result) { + if (err != null) { + return callback(err) + } + if (result == null) { + return callback(new Error('pack not found in index')) + } + if (result.inS3) { + return callback(new Error('pack archiving already done')) + } else if (result.inS3 != null) { + return callback(new Error('pack archiving already in progress')) + } else { + return callback() + } } - if (result == null) { - return callback(new Error('pack not found in index')) - } - if (result.inS3) { - return callback(new Error('pack archiving already done')) - } else if (result.inS3 != null) { - return callback(new Error('pack archiving already in progress')) - } else { - return callback() - } - }) + ) }, markPackAsArchiveInProgress(project_id, doc_id, pack_id, callback) { @@ -1078,7 +1093,7 @@ module.exports = PackManager = { return db.docHistoryIndex.findOneAndUpdate( { _id: ObjectId(doc_id.toString()), - packs: { $elemMatch: { _id: pack_id, inS3: { $exists: false } } } + packs: { $elemMatch: { _id: pack_id, inS3: { $exists: false } } }, }, { $set: { 'packs.$.inS3': false } }, { projection: { 'packs.$': 1 } }, @@ -1106,7 +1121,7 @@ module.exports = PackManager = { return db.docHistoryIndex.updateOne( { _id: ObjectId(doc_id.toString()), - packs: { $elemMatch: { _id: pack_id, inS3: false } } + packs: { $elemMatch: { _id: pack_id, inS3: false } }, }, { $unset: { 'packs.$.inS3': true } }, callback @@ -1118,7 +1133,7 @@ module.exports = PackManager = { return db.docHistoryIndex.findOneAndUpdate( { _id: ObjectId(doc_id.toString()), - packs: { $elemMatch: { _id: pack_id, inS3: false } } + packs: { $elemMatch: { _id: pack_id, inS3: false } }, }, { $set: { 'packs.$.inS3': true } }, { projection: { 'packs.$': 1 } }, @@ -1147,7 +1162,7 @@ module.exports = PackManager = { return callback() } ) - } + }, } // _getOneDayInFutureWithRandomDelay: -> diff --git a/services/track-changes/app/js/PackWorker.js b/services/track-changes/app/js/PackWorker.js index fe507fe815..38e99101de 100644 --- a/services/track-changes/app/js/PackWorker.js +++ b/services/track-changes/app/js/PackWorker.js @@ -53,8 +53,8 @@ if (!source.match(/^[0-9]+$/)) { } return result1 })() - pending = _.filter(result, (row) => - __guard__(row != null ? row.doc_id : undefined, (x) => + pending = _.filter(result, row => + __guard__(row != null ? row.doc_id : undefined, x => x.match(/^[a-f0-9]{24}$/) ) ) @@ -101,9 +101,9 @@ const finish = function () { }) } -process.on('exit', (code) => logger.log({ code }, 'pack archive worker exited')) +process.on('exit', code => logger.log({ code }, 'pack archive worker exited')) -const processUpdates = (pending) => +const processUpdates = pending => async.eachSeries( pending, function (result, callback) { @@ -170,7 +170,7 @@ waitForDb() processFromOneWeekAgo() } }) - .catch((err) => { + .catch(err => { logger.fatal({ err }, 'cannot connect to mongo, exiting') process.exit(1) }) @@ -184,12 +184,12 @@ function processFromOneWeekAgo() { project_id: { $exists: true }, v_end: { $exists: true }, _id: { $lt: ObjectIdFromDate(oneWeekAgo) }, - last_checked: { $lt: oneWeekAgo } + last_checked: { $lt: oneWeekAgo }, }, { projection: { _id: 1, doc_id: 1, project_id: 1 } } ) .sort({ - last_checked: 1 + last_checked: 1, }) .limit(LIMIT) .toArray(function (err, results) { @@ -198,7 +198,7 @@ function processFromOneWeekAgo() { finish() return } - pending = _.uniq(results, false, (result) => result.doc_id.toString()) + pending = _.uniq(results, false, result => result.doc_id.toString()) TOTAL = pending.length logger.log(`found ${TOTAL} documents to archive`) return processUpdates(pending) diff --git a/services/track-changes/app/js/ProjectIterator.js b/services/track-changes/app/js/ProjectIterator.js index 43ddbc7b2f..2b9da401b3 100644 --- a/services/track-changes/app/js/ProjectIterator.js +++ b/services/track-changes/app/js/ProjectIterator.js @@ -14,96 +14,100 @@ let ProjectIterator const Heap = require('heap') -module.exports = ProjectIterator = ProjectIterator = class ProjectIterator { - constructor(packs, before, getPackByIdFn) { - this.before = before - this.getPackByIdFn = getPackByIdFn - const byEndTs = (a, b) => - b.meta.end_ts - a.meta.end_ts || a.fromIndex - b.fromIndex - this.packs = packs.slice().sort(byEndTs) - this.queue = new Heap(byEndTs) - } +module.exports = + ProjectIterator = + ProjectIterator = + class ProjectIterator { + constructor(packs, before, getPackByIdFn) { + this.before = before + this.getPackByIdFn = getPackByIdFn + const byEndTs = (a, b) => + b.meta.end_ts - a.meta.end_ts || a.fromIndex - b.fromIndex + this.packs = packs.slice().sort(byEndTs) + this.queue = new Heap(byEndTs) + } - next(callback) { - // what's up next - // console.log ">>> top item", iterator.packs[0] - const iterator = this - const { before } = this - const { queue } = iterator - const opsToReturn = [] - let nextPack = iterator.packs[0] - let lowWaterMark = - (nextPack != null ? nextPack.meta.end_ts : undefined) || 0 - let nextItem = queue.peek() + next(callback) { + // what's up next + // console.log ">>> top item", iterator.packs[0] + const iterator = this + const { before } = this + const { queue } = iterator + const opsToReturn = [] + let nextPack = iterator.packs[0] + let lowWaterMark = + (nextPack != null ? nextPack.meta.end_ts : undefined) || 0 + let nextItem = queue.peek() - // console.log "queue empty?", queue.empty() - // console.log "nextItem", nextItem - // console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts - // console.log "lowWaterMark", lowWaterMark + // console.log "queue empty?", queue.empty() + // console.log "nextItem", nextItem + // console.log "nextItem.meta.end_ts", nextItem?.meta.end_ts + // console.log "lowWaterMark", lowWaterMark - while ( - before != null && - (nextPack != null ? nextPack.meta.start_ts : undefined) > before - ) { - // discard pack that is outside range - iterator.packs.shift() - nextPack = iterator.packs[0] - lowWaterMark = (nextPack != null ? nextPack.meta.end_ts : undefined) || 0 - } - - if ( - (queue.empty() || - (nextItem != null ? nextItem.meta.end_ts : undefined) <= - lowWaterMark) && - nextPack != null - ) { - // retrieve the next pack and populate the queue - return this.getPackByIdFn( - nextPack.project_id, - nextPack.doc_id, - nextPack._id, - function (err, pack) { - if (err != null) { - return callback(err) - } - iterator.packs.shift() // have now retrieved this pack, remove it - // console.log "got pack", pack - for (const op of Array.from(pack.pack)) { - // console.log "adding op", op - if (before == null || op.meta.end_ts < before) { - op.doc_id = nextPack.doc_id - op.project_id = nextPack.project_id - queue.push(op) - } - } - // now try again - return iterator.next(callback) + while ( + before != null && + (nextPack != null ? nextPack.meta.start_ts : undefined) > before + ) { + // discard pack that is outside range + iterator.packs.shift() + nextPack = iterator.packs[0] + lowWaterMark = + (nextPack != null ? nextPack.meta.end_ts : undefined) || 0 } - ) + + if ( + (queue.empty() || + (nextItem != null ? nextItem.meta.end_ts : undefined) <= + lowWaterMark) && + nextPack != null + ) { + // retrieve the next pack and populate the queue + return this.getPackByIdFn( + nextPack.project_id, + nextPack.doc_id, + nextPack._id, + function (err, pack) { + if (err != null) { + return callback(err) + } + iterator.packs.shift() // have now retrieved this pack, remove it + // console.log "got pack", pack + for (const op of Array.from(pack.pack)) { + // console.log "adding op", op + if (before == null || op.meta.end_ts < before) { + op.doc_id = nextPack.doc_id + op.project_id = nextPack.project_id + queue.push(op) + } + } + // now try again + return iterator.next(callback) + } + ) + } + + // console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark + while ( + nextItem != null && + (nextItem != null ? nextItem.meta.end_ts : undefined) > lowWaterMark + ) { + opsToReturn.push(nextItem) + queue.pop() + nextItem = queue.peek() + } + + // console.log "queue empty?", queue.empty() + // console.log "nextPack", nextPack? + + if (queue.empty() && nextPack == null) { + // got everything + iterator._done = true + } + + return callback(null, opsToReturn) + } + + done() { + return this._done + } } - - // console.log "nextItem", nextItem, "lowWaterMark", lowWaterMark - while ( - nextItem != null && - (nextItem != null ? nextItem.meta.end_ts : undefined) > lowWaterMark - ) { - opsToReturn.push(nextItem) - queue.pop() - nextItem = queue.peek() - } - - // console.log "queue empty?", queue.empty() - // console.log "nextPack", nextPack? - - if (queue.empty() && nextPack == null) { - // got everything - iterator._done = true - } - - return callback(null, opsToReturn) - } - - done() { - return this._done - } -} diff --git a/services/track-changes/app/js/RedisManager.js b/services/track-changes/app/js/RedisManager.js index 6a88c31303..4998c4757f 100644 --- a/services/track-changes/app/js/RedisManager.js +++ b/services/track-changes/app/js/RedisManager.js @@ -34,7 +34,7 @@ module.exports = RedisManager = { callback = function (error, rawUpdates) {} } try { - rawUpdates = Array.from(jsonUpdates || []).map((update) => + rawUpdates = Array.from(jsonUpdates || []).map(update => JSON.parse(update) ) } catch (e) { @@ -93,26 +93,30 @@ module.exports = RedisManager = { let cursor = 0 // redis iterator const keySet = {} // use hash to avoid duplicate results // scan over all keys looking for pattern - var doIteration = (cb) => - node.scan(cursor, 'MATCH', pattern, 'COUNT', 1000, function ( - error, - reply - ) { - let keys - if (error != null) { - return callback(error) + var doIteration = cb => + node.scan( + cursor, + 'MATCH', + pattern, + 'COUNT', + 1000, + function (error, reply) { + let keys + if (error != null) { + return callback(error) + } + ;[cursor, keys] = Array.from(reply) + for (const key of Array.from(keys)) { + keySet[key] = true + } + if (cursor === '0') { + // note redis returns string result not numeric + return callback(null, Object.keys(keySet)) + } else { + return doIteration() + } } - ;[cursor, keys] = Array.from(reply) - for (const key of Array.from(keys)) { - keySet[key] = true - } - if (cursor === '0') { - // note redis returns string result not numeric - return callback(null, Object.keys(keySet)) - } else { - return doIteration() - } - }) + ) return doIteration() }, @@ -162,5 +166,5 @@ module.exports = RedisManager = { return callback(error, doc_ids) } ) - } + }, } diff --git a/services/track-changes/app/js/RestoreManager.js b/services/track-changes/app/js/RestoreManager.js index 6a1af1af44..84ddaf5236 100644 --- a/services/track-changes/app/js/RestoreManager.js +++ b/services/track-changes/app/js/RestoreManager.js @@ -44,5 +44,5 @@ module.exports = RestoreManager = { ) } ) - } + }, } diff --git a/services/track-changes/app/js/UpdateCompressor.js b/services/track-changes/app/js/UpdateCompressor.js index 019ad32760..8e447f95c0 100644 --- a/services/track-changes/app/js/UpdateCompressor.js +++ b/services/track-changes/app/js/UpdateCompressor.js @@ -42,16 +42,16 @@ module.exports = UpdateCompressor = { const splitUpdates = [] for (const update of Array.from(updates)) { // Reject any non-insert or delete ops, i.e. comments - const ops = update.op.filter((o) => o.i != null || o.d != null) + const ops = update.op.filter(o => o.i != null || o.d != null) if (ops.length === 0) { splitUpdates.push({ op: UpdateCompressor.NOOP, meta: { start_ts: update.meta.start_ts || update.meta.ts, end_ts: update.meta.end_ts || update.meta.ts, - user_id: update.meta.user_id + user_id: update.meta.user_id, }, - v: update.v + v: update.v, }) } else { for (const op of Array.from(ops)) { @@ -60,9 +60,9 @@ module.exports = UpdateCompressor = { meta: { start_ts: update.meta.start_ts || update.meta.ts, end_ts: update.meta.end_ts || update.meta.ts, - user_id: update.meta.user_id + user_id: update.meta.user_id, }, - v: update.v + v: update.v, }) } } @@ -82,7 +82,7 @@ module.exports = UpdateCompressor = { const nextUpdate = { op: [], meta: update.meta, - v: update.v + v: update.v, } if (update.op !== UpdateCompressor.NOOP) { nextUpdate.op.push(update.op) @@ -97,7 +97,7 @@ module.exports = UpdateCompressor = { if ( __guard__( lastPreviousUpdate != null ? lastPreviousUpdate.op : undefined, - (x) => x.length + x => x.length ) > 1 ) { // if the last previous update was an array op, don't compress onto it. @@ -144,18 +144,18 @@ module.exports = UpdateCompressor = { meta: { user_id: firstUpdate.meta.user_id || null, start_ts: firstUpdate.meta.start_ts || firstUpdate.meta.ts, - end_ts: firstUpdate.meta.end_ts || firstUpdate.meta.ts + end_ts: firstUpdate.meta.end_ts || firstUpdate.meta.ts, }, - v: firstUpdate.v + v: firstUpdate.v, } secondUpdate = { op: secondUpdate.op, meta: { user_id: secondUpdate.meta.user_id || null, start_ts: secondUpdate.meta.start_ts || secondUpdate.meta.ts, - end_ts: secondUpdate.meta.end_ts || secondUpdate.meta.ts + end_ts: secondUpdate.meta.end_ts || secondUpdate.meta.ts, }, - v: secondUpdate.v + v: secondUpdate.v, } if (firstUpdate.meta.user_id !== secondUpdate.meta.user_id) { @@ -192,14 +192,14 @@ module.exports = UpdateCompressor = { meta: { start_ts: firstUpdate.meta.start_ts, end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id + user_id: firstUpdate.meta.user_id, }, op: { p: firstOp.p, - i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i) + i: strInject(firstOp.i, secondOp.p - firstOp.p, secondOp.i), }, - v: secondUpdate.v - } + v: secondUpdate.v, + }, ] // Two deletes } else if ( @@ -214,14 +214,14 @@ module.exports = UpdateCompressor = { meta: { start_ts: firstUpdate.meta.start_ts, end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id + user_id: firstUpdate.meta.user_id, }, op: { p: secondOp.p, - d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d) + d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d), }, - v: secondUpdate.v - } + v: secondUpdate.v, + }, ] // An insert and then a delete } else if ( @@ -240,14 +240,14 @@ module.exports = UpdateCompressor = { meta: { start_ts: firstUpdate.meta.start_ts, end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id + user_id: firstUpdate.meta.user_id, }, op: { p: firstOp.p, - i: insert + i: insert, }, - v: secondUpdate.v - } + v: secondUpdate.v, + }, ] } else { // This will only happen if the delete extends outside the insert @@ -269,14 +269,14 @@ module.exports = UpdateCompressor = { meta: { start_ts: firstUpdate.meta.start_ts, end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id + user_id: firstUpdate.meta.user_id, }, op: { p: firstOp.p, - i: '' + i: '', }, - v: secondUpdate.v - } + v: secondUpdate.v, + }, ] } else { return diff_ops.map(function (op) { @@ -285,10 +285,10 @@ module.exports = UpdateCompressor = { meta: { start_ts: firstUpdate.meta.start_ts, end_ts: secondUpdate.meta.end_ts, - user_id: firstUpdate.meta.user_id + user_id: firstUpdate.meta.user_id, }, op, - v: secondUpdate.v + v: secondUpdate.v, } }) } @@ -315,13 +315,13 @@ module.exports = UpdateCompressor = { if (type === this.ADDED) { ops.push({ i: content, - p: position + p: position, }) position += content.length } else if (type === this.REMOVED) { ops.push({ d: content, - p: position + p: position, }) } else if (type === this.UNCHANGED) { position += content.length @@ -330,7 +330,7 @@ module.exports = UpdateCompressor = { } } return ops - } + }, } function __guard__(value, transform) { diff --git a/services/track-changes/app/js/UpdateTrimmer.js b/services/track-changes/app/js/UpdateTrimmer.js index 5fabe1cb9e..b37bf545f2 100644 --- a/services/track-changes/app/js/UpdateTrimmer.js +++ b/services/track-changes/app/js/UpdateTrimmer.js @@ -22,54 +22,55 @@ module.exports = UpdateTrimmer = { if (callback == null) { callback = function (error, shouldTrim) {} } - return MongoManager.getProjectMetaData(project_id, function ( - error, - metadata - ) { - if (error != null) { - return callback(error) - } - if (metadata != null ? metadata.preserveHistory : undefined) { - return callback(null, false) - } else { - return WebApiManager.getProjectDetails(project_id, function ( - error, - details - ) { - if (error != null) { - return callback(error) - } - logger.log({ project_id, details }, 'got details') - if ( - __guard__( - details != null ? details.features : undefined, - (x) => x.versioning - ) - ) { - return MongoManager.setProjectMetaData( - project_id, - { preserveHistory: true }, - function (error) { - if (error != null) { - return callback(error) - } - return MongoManager.upgradeHistory(project_id, function ( - error - ) { - if (error != null) { - return callback(error) - } - return callback(null, false) - }) + return MongoManager.getProjectMetaData( + project_id, + function (error, metadata) { + if (error != null) { + return callback(error) + } + if (metadata != null ? metadata.preserveHistory : undefined) { + return callback(null, false) + } else { + return WebApiManager.getProjectDetails( + project_id, + function (error, details) { + if (error != null) { + return callback(error) } - ) - } else { - return callback(null, true) - } - }) + logger.log({ project_id, details }, 'got details') + if ( + __guard__( + details != null ? details.features : undefined, + x => x.versioning + ) + ) { + return MongoManager.setProjectMetaData( + project_id, + { preserveHistory: true }, + function (error) { + if (error != null) { + return callback(error) + } + return MongoManager.upgradeHistory( + project_id, + function (error) { + if (error != null) { + return callback(error) + } + return callback(null, false) + } + ) + } + ) + } else { + return callback(null, true) + } + } + ) + } } - }) - } + ) + }, } function __guard__(value, transform) { diff --git a/services/track-changes/app/js/UpdatesManager.js b/services/track-changes/app/js/UpdatesManager.js index 2acdf8039d..26c1104569 100644 --- a/services/track-changes/app/js/UpdatesManager.js +++ b/services/track-changes/app/js/UpdatesManager.js @@ -50,7 +50,7 @@ module.exports = UpdatesManager = { const op = rawUpdates[i] if (i > 0) { const thisVersion = op != null ? op.v : undefined - const prevVersion = __guard__(rawUpdates[i - 1], (x) => x.v) + const prevVersion = __guard__(rawUpdates[i - 1], x => x.v) if (!(prevVersion < thisVersion)) { logger.error( { @@ -59,7 +59,7 @@ module.exports = UpdatesManager = { rawUpdates, temporary, thisVersion, - prevVersion + prevVersion, }, 'op versions out of order' ) @@ -69,138 +69,137 @@ module.exports = UpdatesManager = { // FIXME: we no longer need the lastCompressedUpdate, so change functions not to need it // CORRECTION: we do use it to log the time in case of error - return MongoManager.peekLastCompressedUpdate(doc_id, function ( - error, - lastCompressedUpdate, - lastVersion - ) { - // lastCompressedUpdate is the most recent update in Mongo, and - // lastVersion is its sharejs version number. - // - // The peekLastCompressedUpdate method may pass the update back - // as 'null' (for example if the previous compressed update has - // been archived). In this case it can still pass back the - // lastVersion from the update to allow us to check consistency. - let op - if (error != null) { - return callback(error) - } - - // Ensure that raw updates start where lastVersion left off - if (lastVersion != null) { - const discardedUpdates = [] - rawUpdates = rawUpdates.slice(0) - while (rawUpdates[0] != null && rawUpdates[0].v <= lastVersion) { - discardedUpdates.push(rawUpdates.shift()) - } - if (discardedUpdates.length) { - logger.error( - { project_id, doc_id, discardedUpdates, temporary, lastVersion }, - 'discarded updates already present' - ) + return MongoManager.peekLastCompressedUpdate( + doc_id, + function (error, lastCompressedUpdate, lastVersion) { + // lastCompressedUpdate is the most recent update in Mongo, and + // lastVersion is its sharejs version number. + // + // The peekLastCompressedUpdate method may pass the update back + // as 'null' (for example if the previous compressed update has + // been archived). In this case it can still pass back the + // lastVersion from the update to allow us to check consistency. + let op + if (error != null) { + return callback(error) } - if (rawUpdates[0] != null && rawUpdates[0].v !== lastVersion + 1) { - const ts = __guard__( - lastCompressedUpdate != null - ? lastCompressedUpdate.meta - : undefined, - (x1) => x1.end_ts - ) - const last_timestamp = ts != null ? new Date(ts) : 'unknown time' - error = new Error( - `Tried to apply raw op at version ${rawUpdates[0].v} to last compressed update with version ${lastVersion} from ${last_timestamp}` - ) - logger.error( - { - err: error, - doc_id, - project_id, - prev_end_ts: ts, - temporary, - lastCompressedUpdate - }, - 'inconsistent doc versions' - ) - if ( - (Settings.trackchanges != null - ? Settings.trackchanges.continueOnError - : undefined) && - rawUpdates[0].v > lastVersion + 1 - ) { - // we have lost some ops - continue to write into the database, we can't recover at this point - lastCompressedUpdate = null - } else { - return callback(error) + // Ensure that raw updates start where lastVersion left off + if (lastVersion != null) { + const discardedUpdates = [] + rawUpdates = rawUpdates.slice(0) + while (rawUpdates[0] != null && rawUpdates[0].v <= lastVersion) { + discardedUpdates.push(rawUpdates.shift()) } - } - } - - if (rawUpdates.length === 0) { - return callback() - } - - // some old large ops in redis need to be rejected, they predate - // the size limit that now prevents them going through the system - const REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024 - for (var rawUpdate of Array.from(rawUpdates)) { - const opSizes = (() => { - const result = [] - for (op of Array.from( - (rawUpdate != null ? rawUpdate.op : undefined) || [] - )) { - result.push( - (op.i != null ? op.i.length : undefined) || - (op.d != null ? op.d.length : undefined) + if (discardedUpdates.length) { + logger.error( + { project_id, doc_id, discardedUpdates, temporary, lastVersion }, + 'discarded updates already present' ) } - return result - })() - const size = _.max(opSizes) - if (size > REJECT_LARGE_OP_SIZE) { - error = new Error( - `dropped op exceeding maximum allowed size of ${REJECT_LARGE_OP_SIZE}` - ) - logger.error( - { err: error, doc_id, project_id, size, rawUpdate }, - 'dropped op - too big' - ) - rawUpdate.op = [] - } - } - const compressedUpdates = UpdateCompressor.compressRawUpdates( - null, - rawUpdates - ) - return PackManager.insertCompressedUpdates( - project_id, - doc_id, - lastCompressedUpdate, - compressedUpdates, - temporary, - function (error, result) { - if (error != null) { - return callback(error) - } - if (result != null) { - logger.log( + if (rawUpdates[0] != null && rawUpdates[0].v !== lastVersion + 1) { + const ts = __guard__( + lastCompressedUpdate != null + ? lastCompressedUpdate.meta + : undefined, + x1 => x1.end_ts + ) + const last_timestamp = ts != null ? new Date(ts) : 'unknown time' + error = new Error( + `Tried to apply raw op at version ${rawUpdates[0].v} to last compressed update with version ${lastVersion} from ${last_timestamp}` + ) + logger.error( { - project_id, + err: error, doc_id, - orig_v: - lastCompressedUpdate != null - ? lastCompressedUpdate.v - : undefined, - new_v: result.v + project_id, + prev_end_ts: ts, + temporary, + lastCompressedUpdate, }, - 'inserted updates into pack' + 'inconsistent doc versions' ) + if ( + (Settings.trackchanges != null + ? Settings.trackchanges.continueOnError + : undefined) && + rawUpdates[0].v > lastVersion + 1 + ) { + // we have lost some ops - continue to write into the database, we can't recover at this point + lastCompressedUpdate = null + } else { + return callback(error) + } } + } + + if (rawUpdates.length === 0) { return callback() } - ) - }) + + // some old large ops in redis need to be rejected, they predate + // the size limit that now prevents them going through the system + const REJECT_LARGE_OP_SIZE = 4 * 1024 * 1024 + for (var rawUpdate of Array.from(rawUpdates)) { + const opSizes = (() => { + const result = [] + for (op of Array.from( + (rawUpdate != null ? rawUpdate.op : undefined) || [] + )) { + result.push( + (op.i != null ? op.i.length : undefined) || + (op.d != null ? op.d.length : undefined) + ) + } + return result + })() + const size = _.max(opSizes) + if (size > REJECT_LARGE_OP_SIZE) { + error = new Error( + `dropped op exceeding maximum allowed size of ${REJECT_LARGE_OP_SIZE}` + ) + logger.error( + { err: error, doc_id, project_id, size, rawUpdate }, + 'dropped op - too big' + ) + rawUpdate.op = [] + } + } + + const compressedUpdates = UpdateCompressor.compressRawUpdates( + null, + rawUpdates + ) + return PackManager.insertCompressedUpdates( + project_id, + doc_id, + lastCompressedUpdate, + compressedUpdates, + temporary, + function (error, result) { + if (error != null) { + return callback(error) + } + if (result != null) { + logger.log( + { + project_id, + doc_id, + orig_v: + lastCompressedUpdate != null + ? lastCompressedUpdate.v + : undefined, + new_v: result.v, + }, + 'inserted updates into pack' + ) + } + return callback() + } + ) + } + ) }, // Check whether the updates are temporary (per-project property) @@ -208,15 +207,15 @@ module.exports = UpdatesManager = { if (callback == null) { callback = function (error, temporary) {} } - return UpdateTrimmer.shouldTrimUpdates(project_id, function ( - error, - temporary - ) { - if (error != null) { - return callback(error) + return UpdateTrimmer.shouldTrimUpdates( + project_id, + function (error, temporary) { + if (error != null) { + return callback(error) + } + return callback(null, temporary) } - return callback(null, temporary) - }) + ) }, // Check for project id on document history (per-document property) @@ -248,71 +247,71 @@ module.exports = UpdatesManager = { } const { length } = docUpdates // parse the redis strings into ShareJs updates - return RedisManager.expandDocUpdates(docUpdates, function ( - error, - rawUpdates - ) { - if (error != null) { - logger.err( - { project_id, doc_id, docUpdates }, - 'failed to parse docUpdates' - ) - return callback(error) - } - logger.log( - { project_id, doc_id, rawUpdates }, - 'retrieved raw updates from redis' - ) - return UpdatesManager.compressAndSaveRawUpdates( - project_id, - doc_id, - rawUpdates, - temporary, - function (error) { - if (error != null) { - return callback(error) - } - logger.log( - { project_id, doc_id }, - 'compressed and saved doc updates' - ) - // delete the applied updates from redis - return RedisManager.deleteAppliedDocUpdates( - project_id, - doc_id, - docUpdates, - function (error) { - if (error != null) { - return callback(error) - } - if (length === UpdatesManager.REDIS_READ_BATCH_SIZE) { - // There might be more updates - logger.log( - { project_id, doc_id }, - 'continuing processing updates' - ) - return setTimeout( - () => - UpdatesManager.processUncompressedUpdates( - project_id, - doc_id, - temporary, - callback - ), - 0 - ) - } else { - logger.log( - { project_id, doc_id }, - 'all raw updates processed' - ) - return callback() - } - } + return RedisManager.expandDocUpdates( + docUpdates, + function (error, rawUpdates) { + if (error != null) { + logger.err( + { project_id, doc_id, docUpdates }, + 'failed to parse docUpdates' ) + return callback(error) } - ) - }) + logger.log( + { project_id, doc_id, rawUpdates }, + 'retrieved raw updates from redis' + ) + return UpdatesManager.compressAndSaveRawUpdates( + project_id, + doc_id, + rawUpdates, + temporary, + function (error) { + if (error != null) { + return callback(error) + } + logger.log( + { project_id, doc_id }, + 'compressed and saved doc updates' + ) + // delete the applied updates from redis + return RedisManager.deleteAppliedDocUpdates( + project_id, + doc_id, + docUpdates, + function (error) { + if (error != null) { + return callback(error) + } + if (length === UpdatesManager.REDIS_READ_BATCH_SIZE) { + // There might be more updates + logger.log( + { project_id, doc_id }, + 'continuing processing updates' + ) + return setTimeout( + () => + UpdatesManager.processUncompressedUpdates( + project_id, + doc_id, + temporary, + callback + ), + 0 + ) + } else { + logger.log( + { project_id, doc_id }, + 'all raw updates processed' + ) + return callback() + } + } + ) + } + ) + } + ) } ) }, @@ -322,20 +321,20 @@ module.exports = UpdatesManager = { if (callback == null) { callback = function (error) {} } - return UpdatesManager._prepareProjectForUpdates(project_id, function ( - error, - temporary - ) { - if (error != null) { - return callback(error) + return UpdatesManager._prepareProjectForUpdates( + project_id, + function (error, temporary) { + if (error != null) { + return callback(error) + } + return UpdatesManager._processUncompressedUpdatesForDocWithLock( + project_id, + doc_id, + temporary, + callback + ) } - return UpdatesManager._processUncompressedUpdatesForDocWithLock( - project_id, - doc_id, - temporary, - callback - ) - }) + ) }, // Process updates for a doc when the whole project is flushed (internal method) @@ -348,24 +347,26 @@ module.exports = UpdatesManager = { if (callback == null) { callback = function (error) {} } - return UpdatesManager._prepareDocForUpdates(project_id, doc_id, function ( - error - ) { - if (error != null) { - return callback(error) + return UpdatesManager._prepareDocForUpdates( + project_id, + doc_id, + function (error) { + if (error != null) { + return callback(error) + } + return LockManager.runWithLock( + keys.historyLock({ doc_id }), + releaseLock => + UpdatesManager.processUncompressedUpdates( + project_id, + doc_id, + temporary, + releaseLock + ), + callback + ) } - return LockManager.runWithLock( - keys.historyLock({ doc_id }), - (releaseLock) => - UpdatesManager.processUncompressedUpdates( - project_id, - doc_id, - temporary, - releaseLock - ), - callback - ) - }) + ) }, // Process all updates for a project, only check project-level information once @@ -373,32 +374,32 @@ module.exports = UpdatesManager = { if (callback == null) { callback = function (error) {} } - return RedisManager.getDocIdsWithHistoryOps(project_id, function ( - error, - doc_ids - ) { - if (error != null) { - return callback(error) - } - return UpdatesManager._prepareProjectForUpdates(project_id, function ( - error, - temporary - ) { - const jobs = [] - for (const doc_id of Array.from(doc_ids)) { - ;((doc_id) => - jobs.push((cb) => - UpdatesManager._processUncompressedUpdatesForDocWithLock( - project_id, - doc_id, - temporary, - cb - ) - ))(doc_id) + return RedisManager.getDocIdsWithHistoryOps( + project_id, + function (error, doc_ids) { + if (error != null) { + return callback(error) } - return async.parallelLimit(jobs, 5, callback) - }) - }) + return UpdatesManager._prepareProjectForUpdates( + project_id, + function (error, temporary) { + const jobs = [] + for (const doc_id of Array.from(doc_ids)) { + ;(doc_id => + jobs.push(cb => + UpdatesManager._processUncompressedUpdatesForDocWithLock( + project_id, + doc_id, + temporary, + cb + ) + ))(doc_id) + } + return async.parallelLimit(jobs, 5, callback) + } + ) + } + ) }, // flush all outstanding changes @@ -417,7 +418,7 @@ module.exports = UpdatesManager = { logger.log( { count: project_ids != null ? project_ids.length : undefined, - project_ids + project_ids, }, 'found projects' ) @@ -426,11 +427,11 @@ module.exports = UpdatesManager = { const selectedProjects = limit < 0 ? project_ids : project_ids.slice(0, limit) for (project_id of Array.from(selectedProjects)) { - ;((project_id) => - jobs.push((cb) => + ;(project_id => + jobs.push(cb => UpdatesManager.processUncompressedUpdatesForProject( project_id, - (err) => cb(null, { failed: err != null, project_id }) + err => cb(null, { failed: err != null, project_id }) ) ))(project_id) } @@ -460,7 +461,7 @@ module.exports = UpdatesManager = { return callback(null, { failed: failedProjects, succeeded: succeededProjects, - all: project_ids + all: project_ids, }) }) }) @@ -485,7 +486,7 @@ module.exports = UpdatesManager = { return callback(error) } // function to get doc_ids for each project - const task = (cb) => + const task = cb => async.concatSeries( all_project_ids, RedisManager.getDocIdsWithHistoryOps, @@ -542,20 +543,22 @@ module.exports = UpdatesManager = { if (callback == null) { callback = function (error, updates) {} } - return UpdatesManager.getDocUpdates(project_id, doc_id, options, function ( - error, - updates - ) { - if (error != null) { - return callback(error) - } - return UpdatesManager.fillUserInfo(updates, function (error, updates) { + return UpdatesManager.getDocUpdates( + project_id, + doc_id, + options, + function (error, updates) { if (error != null) { return callback(error) } - return callback(null, updates) - }) - }) + return UpdatesManager.fillUserInfo(updates, function (error, updates) { + if (error != null) { + return callback(error) + } + return callback(null, updates) + }) + } + ) }, getSummarizedProjectUpdates(project_id, options, callback) { @@ -577,63 +580,65 @@ module.exports = UpdatesManager = { if (error != null) { return callback(error) } - return PackManager.makeProjectIterator(project_id, before, function ( - err, - iterator - ) { - if (err != null) { - return callback(err) - } - // repeatedly get updates and pass them through the summariser to get an final output with user info - return async.whilst( - () => - // console.log "checking iterator.done", iterator.done() - summarizedUpdates.length < options.min_count && !iterator.done(), + return PackManager.makeProjectIterator( + project_id, + before, + function (err, iterator) { + if (err != null) { + return callback(err) + } + // repeatedly get updates and pass them through the summariser to get an final output with user info + return async.whilst( + () => + // console.log "checking iterator.done", iterator.done() + summarizedUpdates.length < options.min_count && + !iterator.done(), - (cb) => - iterator.next(function (err, partialUpdates) { - if (err != null) { - return callback(err) - } - // logger.log {partialUpdates}, 'got partialUpdates' - if (partialUpdates.length === 0) { - return cb() - } // # FIXME should try to avoid this happening - nextBeforeTimestamp = - partialUpdates[partialUpdates.length - 1].meta.end_ts - // add the updates to the summary list - summarizedUpdates = UpdatesManager._summarizeUpdates( - partialUpdates, - summarizedUpdates - ) - return cb() - }), - - () => - // finally done all updates - // console.log 'summarized Updates', summarizedUpdates - UpdatesManager.fillSummarizedUserInfo( - summarizedUpdates, - function (err, results) { + cb => + iterator.next(function (err, partialUpdates) { if (err != null) { return callback(err) } - return callback( - null, - results, - !iterator.done() ? nextBeforeTimestamp : undefined + // logger.log {partialUpdates}, 'got partialUpdates' + if (partialUpdates.length === 0) { + return cb() + } // # FIXME should try to avoid this happening + nextBeforeTimestamp = + partialUpdates[partialUpdates.length - 1].meta.end_ts + // add the updates to the summary list + summarizedUpdates = UpdatesManager._summarizeUpdates( + partialUpdates, + summarizedUpdates ) - } - ) - ) - }) + return cb() + }), + + () => + // finally done all updates + // console.log 'summarized Updates', summarizedUpdates + UpdatesManager.fillSummarizedUserInfo( + summarizedUpdates, + function (err, results) { + if (err != null) { + return callback(err) + } + return callback( + null, + results, + !iterator.done() ? nextBeforeTimestamp : undefined + ) + } + ) + ) + } + ) } ) }, exportProject(projectId, consumer) { // Flush anything before collecting updates. - UpdatesManager.processUncompressedUpdatesForProject(projectId, (err) => { + UpdatesManager.processUncompressedUpdatesForProject(projectId, err => { if (err) return consumer(err) // Fetch all the packs. @@ -646,7 +651,7 @@ module.exports = UpdatesManager = { async.whilst( () => !iterator.done(), - (cb) => + cb => iterator.next((err, updatesFromASinglePack) => { if (err) return cb(err) @@ -656,7 +661,7 @@ module.exports = UpdatesManager = { // call. return cb() } - updatesFromASinglePack.forEach((update) => { + updatesFromASinglePack.forEach(update => { accumulatedUserIds.add( // Super defensive access on update details. String(update && update.meta && update.meta.user_id) @@ -666,7 +671,7 @@ module.exports = UpdatesManager = { consumer(null, { updates: updatesFromASinglePack }, cb) }), - (err) => { + err => { if (err) return consumer(err) // Adding undefined can happen for broken updates. @@ -674,7 +679,7 @@ module.exports = UpdatesManager = { consumer(null, { updates: [], - userIds: Array.from(accumulatedUserIds).sort() + userIds: Array.from(accumulatedUserIds).sort(), }) } ) @@ -689,8 +694,8 @@ module.exports = UpdatesManager = { const jobs = [] const fetchedUserInfo = {} for (const user_id in users) { - ;((user_id) => - jobs.push((callback) => + ;(user_id => + jobs.push(callback => WebApiManager.getUserInfo(user_id, function (error, userInfo) { if (error != null) { return callback(error) @@ -722,22 +727,22 @@ module.exports = UpdatesManager = { } } - return UpdatesManager.fetchUserInfo(users, function ( - error, - fetchedUserInfo - ) { - if (error != null) { - return callback(error) - } - for (update of Array.from(updates)) { - ;({ user_id } = update.meta) - delete update.meta.user_id - if (UpdatesManager._validUserId(user_id)) { - update.meta.user = fetchedUserInfo[user_id] + return UpdatesManager.fetchUserInfo( + users, + function (error, fetchedUserInfo) { + if (error != null) { + return callback(error) } + for (update of Array.from(updates)) { + ;({ user_id } = update.meta) + delete update.meta.user_id + if (UpdatesManager._validUserId(user_id)) { + update.meta.user = fetchedUserInfo[user_id] + } + } + return callback(null, updates) } - return callback(null, updates) - }) + ) }, fillSummarizedUserInfo(updates, callback) { @@ -755,27 +760,27 @@ module.exports = UpdatesManager = { } } - return UpdatesManager.fetchUserInfo(users, function ( - error, - fetchedUserInfo - ) { - if (error != null) { - return callback(error) - } - for (update of Array.from(updates)) { - user_ids = update.meta.user_ids || [] - update.meta.users = [] - delete update.meta.user_ids - for (user_id of Array.from(user_ids)) { - if (UpdatesManager._validUserId(user_id)) { - update.meta.users.push(fetchedUserInfo[user_id]) - } else { - update.meta.users.push(null) + return UpdatesManager.fetchUserInfo( + users, + function (error, fetchedUserInfo) { + if (error != null) { + return callback(error) + } + for (update of Array.from(updates)) { + user_ids = update.meta.user_ids || [] + update.meta.users = [] + delete update.meta.user_ids + for (user_id of Array.from(user_ids)) { + if (UpdatesManager._validUserId(user_id)) { + update.meta.users.push(fetchedUserInfo[user_id]) + } else { + update.meta.users.push(null) + } } } + return callback(null, updates) } - return callback(null, updates) - }) + ) }, _validUserId(user_id) { @@ -830,7 +835,7 @@ module.exports = UpdatesManager = { // check if the user in this update is already present in the earliest update, // if not, add them to the users list of the earliest update earliestUpdate.meta.user_ids = _.union(earliestUpdate.meta.user_ids, [ - update.meta.user_id + update.meta.user_id, ]) doc_id = update.doc_id.toString() @@ -841,7 +846,7 @@ module.exports = UpdatesManager = { } else { earliestUpdate.docs[doc_id] = { fromV: update.v, - toV: update.v + toV: update.v, } } @@ -858,14 +863,14 @@ module.exports = UpdatesManager = { meta: { user_ids: [], start_ts: update.meta.start_ts, - end_ts: update.meta.end_ts + end_ts: update.meta.end_ts, }, - docs: {} + docs: {}, } newUpdate.docs[update.doc_id.toString()] = { fromV: update.v, - toV: update.v + toV: update.v, } newUpdate.meta.user_ids.push(update.meta.user_id) summarizedUpdates.push(newUpdate) @@ -873,7 +878,7 @@ module.exports = UpdatesManager = { } return summarizedUpdates - } + }, } function __guard__(value, transform) { diff --git a/services/track-changes/app/js/WebApiManager.js b/services/track-changes/app/js/WebApiManager.js index b1bd6de063..b0706d8e71 100644 --- a/services/track-changes/app/js/WebApiManager.js +++ b/services/track-changes/app/js/WebApiManager.js @@ -36,8 +36,8 @@ module.exports = WebApiManager = { auth: { user: Settings.apis.web.user, pass: Settings.apis.web.pass, - sendImmediately: true - } + sendImmediately: true, + }, }, function (error, res, body) { if (error != null) { @@ -86,7 +86,7 @@ module.exports = WebApiManager = { id: user.id, email: user.email, first_name: user.first_name, - last_name: user.last_name + last_name: user.last_name, }) }) }, @@ -112,5 +112,5 @@ module.exports = WebApiManager = { } return callback(null, project) }) - } + }, } diff --git a/services/track-changes/app/js/mongodb.js b/services/track-changes/app/js/mongodb.js index 5b4f56dce8..a345d5ce70 100644 --- a/services/track-changes/app/js/mongodb.js +++ b/services/track-changes/app/js/mongodb.js @@ -38,5 +38,5 @@ module.exports = { db, ObjectId, closeDb, - waitForDb + waitForDb, } diff --git a/services/track-changes/app/lib/diff_match_patch.js b/services/track-changes/app/lib/diff_match_patch.js index c4864d7bde..aeb2b1c570 100644 --- a/services/track-changes/app/lib/diff_match_patch.js +++ b/services/track-changes/app/lib/diff_match_patch.js @@ -178,7 +178,7 @@ diff_match_patch.prototype.diff_compute_ = function ( diffs = [ [DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], - [DIFF_INSERT, longtext.substring(i + shorttext.length)] + [DIFF_INSERT, longtext.substring(i + shorttext.length)], ] // Swap insertions for deletions if diff is reversed. if (text1.length > text2.length) { @@ -192,7 +192,7 @@ diff_match_patch.prototype.diff_compute_ = function ( // After the previous speedup, the character can't be an equality. return [ [DIFF_DELETE, text1], - [DIFF_INSERT, text2] + [DIFF_INSERT, text2], ] } @@ -415,7 +415,7 @@ diff_match_patch.prototype.diff_bisect_ = function (text1, text2, deadline) { // number of diffs equals number of characters, no commonality at all. return [ [DIFF_DELETE, text1], - [DIFF_INSERT, text2] + [DIFF_INSERT, text2], ] } @@ -716,7 +716,7 @@ diff_match_patch.prototype.diff_halfMatch_ = function (text1, text2) { best_longtext_b, best_shorttext_a, best_shorttext_b, - best_common + best_common, ] } else { return null @@ -809,7 +809,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function (diffs) { // Duplicate record. diffs.splice(equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, - lastequality + lastequality, ]) // Change second copy to insert. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT @@ -859,7 +859,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function (diffs) { // Overlap found. Insert an equality and trim the surrounding edits. diffs.splice(pointer, 0, [ DIFF_EQUAL, - insertion.substring(0, overlap_length1) + insertion.substring(0, overlap_length1), ]) diffs[pointer - 1][1] = deletion.substring( 0, @@ -877,7 +877,7 @@ diff_match_patch.prototype.diff_cleanupSemantic = function (diffs) { // Insert an equality and swap and trim the surrounding edits. diffs.splice(pointer, 0, [ DIFF_EQUAL, - deletion.substring(0, overlap_length2) + deletion.substring(0, overlap_length2), ]) diffs[pointer - 1][0] = DIFF_INSERT diffs[pointer - 1][1] = insertion.substring( @@ -1093,7 +1093,7 @@ diff_match_patch.prototype.diff_cleanupEfficiency = function (diffs) { // Duplicate record. diffs.splice(equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, - lastequality + lastequality, ]) // Change second copy to insert. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT @@ -1156,13 +1156,12 @@ diff_match_patch.prototype.diff_cleanupMerge = function (diffs) { diffs[pointer - count_delete - count_insert - 1][0] == DIFF_EQUAL ) { - diffs[ - pointer - count_delete - count_insert - 1 - ][1] += text_insert.substring(0, commonlength) + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength) } else { diffs.splice(0, 0, [ DIFF_EQUAL, - text_insert.substring(0, commonlength) + text_insert.substring(0, commonlength), ]) pointer++ } @@ -1189,12 +1188,12 @@ diff_match_patch.prototype.diff_cleanupMerge = function (diffs) { if (count_delete === 0) { diffs.splice(pointer - count_insert, count_delete + count_insert, [ DIFF_INSERT, - text_insert + text_insert, ]) } else if (count_insert === 0) { diffs.splice(pointer - count_delete, count_delete + count_insert, [ DIFF_DELETE, - text_delete + text_delete, ]) } else { diffs.splice( diff --git a/services/track-changes/config/settings.defaults.js b/services/track-changes/config/settings.defaults.js index 38ecce723a..faa8660b70 100755 --- a/services/track-changes/config/settings.defaults.js +++ b/services/track-changes/config/settings.defaults.js @@ -6,18 +6,18 @@ module.exports = { mongo: { options: { useUnifiedTopology: - (process.env.MONGO_USE_UNIFIED_TOPOLOGY || 'true') === 'true' + (process.env.MONGO_USE_UNIFIED_TOPOLOGY || 'true') === 'true', }, url: process.env.MONGO_CONNECTION_STRING || - `mongodb://${process.env.MONGO_HOST || 'localhost'}/sharelatex` + `mongodb://${process.env.MONGO_HOST || 'localhost'}/sharelatex`, }, internal: { trackchanges: { port: 3015, - host: process.env.LISTEN_ADDRESS || 'localhost' - } + host: process.env.LISTEN_ADDRESS || 'localhost', + }, }, apis: { documentupdater: { @@ -25,18 +25,18 @@ module.exports = { process.env.DOCUMENT_UPDATER_HOST || process.env.DOCUPDATER_HOST || 'localhost' - }:3003` + }:3003`, }, docstore: { - url: `http://${process.env.DOCSTORE_HOST || 'localhost'}:3016` + url: `http://${process.env.DOCSTORE_HOST || 'localhost'}:3016`, }, web: { url: `http://${ process.env.WEB_API_HOST || process.env.WEB_HOST || 'localhost' }:${process.env.WEB_API_PORT || process.env.WEB_PORT || 3000}`, user: process.env.WEB_API_USER || 'sharelatex', - pass: process.env.WEB_API_PASSWORD || 'password' - } + pass: process.env.WEB_API_PASSWORD || 'password', + }, }, redis: { lock: { @@ -49,8 +49,8 @@ module.exports = { }, historyIndexLock({ project_id: projectId }) { return `HistoryIndexLock:{${projectId}}` - } - } + }, + }, }, history: { host: process.env.REDIS_HOST || 'localhost', @@ -62,9 +62,9 @@ module.exports = { }, docsWithHistoryOps({ project_id: projectId }) { return `DocsWithHistoryOps:{${projectId}}` - } - } - } + }, + }, + }, }, trackchanges: { @@ -72,19 +72,19 @@ module.exports = { key: process.env.AWS_ACCESS_KEY_ID, secret: process.env.AWS_SECRET_ACCESS_KEY, endpoint: process.env.AWS_S3_ENDPOINT, - pathStyle: process.env.AWS_S3_PATH_STYLE === 'true' + pathStyle: process.env.AWS_S3_PATH_STYLE === 'true', }, stores: { - doc_history: process.env.AWS_BUCKET + doc_history: process.env.AWS_BUCKET, }, - continueOnError: process.env.TRACK_CHANGES_CONTINUE_ON_ERROR || false + continueOnError: process.env.TRACK_CHANGES_CONTINUE_ON_ERROR || false, }, path: { - dumpFolder: Path.join(TMP_DIR, 'dumpFolder') + dumpFolder: Path.join(TMP_DIR, 'dumpFolder'), }, sentry: { - dsn: process.env.SENTRY_DSN - } + dsn: process.env.SENTRY_DSN, + }, } diff --git a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js index c0bb0b07ae..9292005ba7 100644 --- a/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/AppendingUpdatesTests.js @@ -39,20 +39,20 @@ describe('Appending doc ops to the history', function () { { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 + v: 3, }, { op: [{ i: 'o', p: 4 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 4 + v: 4, }, { op: [{ i: 'o', p: 5 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 5 - } + v: 5, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -76,8 +76,8 @@ describe('Appending doc ops to the history', function () { return expect(this.updates[0].pack[0].op).to.deep.equal([ { p: 3, - i: 'foo' - } + i: 'foo', + }, ]) }) @@ -121,20 +121,20 @@ describe('Appending doc ops to the history', function () { { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 + v: 3, }, { op: [{ i: 'o', p: 4 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 4 + v: 4, }, { op: [{ i: 'o', p: 5 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 5 - } + v: 5, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -162,20 +162,20 @@ describe('Appending doc ops to the history', function () { { op: [{ i: 'b', p: 6 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 6 + v: 6, }, { op: [{ i: 'a', p: 7 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 7 + v: 7, }, { op: [{ i: 'r', p: 8 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 8 - } + v: 8, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -199,8 +199,8 @@ describe('Appending doc ops to the history', function () { return expect(this.updates[0].pack[1].op).to.deep.equal([ { p: 6, - i: 'bar' - } + i: 'bar', + }, ]) }) @@ -219,20 +219,20 @@ describe('Appending doc ops to the history', function () { { op: [{ i: 'b', p: 6 }], meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 6 + v: 6, }, { op: [{ i: 'a', p: 7 }], meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 7 + v: 7, }, { op: [{ i: 'r', p: 8 }], meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 8 - } + v: 8, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -256,14 +256,14 @@ describe('Appending doc ops to the history', function () { expect(this.updates[0].pack[0].op).to.deep.equal([ { p: 3, - i: 'foo' - } + i: 'foo', + }, ]) return expect(this.updates[0].pack[1].op).to.deep.equal([ { p: 6, - i: 'bar' - } + i: 'bar', + }, ]) }) }) @@ -281,7 +281,7 @@ describe('Appending doc ops to the history', function () { updates.push({ op: [{ i: 'a', p: 0 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: i + v: i, }) this.expectedOp[0].i = `a${this.expectedOp[0].i}` } @@ -290,7 +290,7 @@ describe('Appending doc ops to the history', function () { this.project_id, this.doc_id, updates, - (error) => { + error => { if (error != null) { throw error } @@ -334,22 +334,22 @@ describe('Appending doc ops to the history', function () { op: [ { i: 'f', p: 3 }, { i: 'o', p: 4 }, - { i: 'o', p: 5 } + { i: 'o', p: 5 }, ], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 + v: 3, }, { op: [ { i: 'b', p: 6 }, { i: 'a', p: 7 }, - { i: 'r', p: 8 } + { i: 'r', p: 8 }, ], meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 4 - } + v: 4, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -373,14 +373,14 @@ describe('Appending doc ops to the history', function () { expect(this.updates[0].pack[0].op).to.deep.equal([ { p: 3, - i: 'foo' - } + i: 'foo', + }, ]) return expect(this.updates[0].pack[1].op).to.deep.equal([ { p: 6, - i: 'bar' - } + i: 'bar', + }, ]) }) @@ -404,15 +404,15 @@ describe('Appending doc ops to the history', function () { { op: [], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 + v: 3, }, { op: [{ i: 'foo', p: 3 }], meta: { ts: Date.now() + oneDay, user_id: this.user_id }, - v: 4 - } + v: 4, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -440,8 +440,8 @@ describe('Appending doc ops to the history', function () { return expect(this.updates[0].pack[1].op).to.deep.equal([ { p: 3, - i: 'foo' - } + i: 'foo', + }, ]) }) @@ -464,13 +464,13 @@ describe('Appending doc ops to the history', function () { { op: [ { c: 'foo', p: 3 }, - { d: 'bar', p: 6 } + { d: 'bar', p: 6 }, ], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -492,7 +492,7 @@ describe('Appending doc ops to the history', function () { it('should ignore the comment op', function () { return expect(this.updates[0].pack[0].op).to.deep.equal([ - { d: 'bar', p: 6 } + { d: 'bar', p: 6 }, ]) }) @@ -515,10 +515,10 @@ describe('Appending doc ops to the history', function () { { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } @@ -557,10 +557,10 @@ describe('Appending doc ops to the history', function () { { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } diff --git a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js index 4f693e70a1..09435bc80c 100644 --- a/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/ArchivingUpdatesTests.js @@ -32,9 +32,9 @@ describe('Archiving updates', function () { __guard__( __guard__( Settings != null ? Settings.trackchanges : undefined, - (x1) => x1.s3 + x1 => x1.s3 ), - (x) => x.key.length + x => x.key.length ) < 1 ) { const message = new Error('s3 keys not setup, this test setup will fail') @@ -57,8 +57,8 @@ describe('Archiving updates', function () { MockWebApi.projects[this.project_id] = { features: { - versioning: true - } + versioning: true, + }, } sinon.spy(MockWebApi, 'getProjectDetails') @@ -66,13 +66,13 @@ describe('Archiving updates', function () { email: 'user@sharelatex.com', first_name: 'Leo', last_name: 'Lion', - id: this.user_id + id: this.user_id, } sinon.spy(MockWebApi, 'getUserInfo') MockDocStoreApi.docs[this.doc_id] = this.doc = { _id: this.doc_id, - project_id: this.project_id + project_id: this.project_id, } sinon.spy(MockDocStoreApi, 'getAllDoc') @@ -85,15 +85,15 @@ describe('Archiving updates', function () { this.updates.push({ op: [{ i: 'a', p: 0 }], meta: { ts: this.now + (i - 2048) * this.hours, user_id: this.user_id }, - v: 2 * i + 1 + v: 2 * i + 1, }) this.updates.push({ op: [{ i: 'b', p: 0 }], meta: { ts: this.now + (i - 2048) * this.hours + 10 * this.minutes, - user_id: this.user_id_2 + user_id: this.user_id_2, }, - v: 2 * i + 2 + v: 2 * i + 2, }) } TrackChangesApp.ensureRunning(() => { @@ -101,14 +101,14 @@ describe('Archiving updates', function () { this.project_id, this.doc_id, this.updates, - (error) => { + error => { if (error != null) { throw error } return TrackChangesClient.flushDoc( this.project_id, this.doc_id, - (error) => { + error => { if (error != null) { throw error } @@ -163,7 +163,7 @@ describe('Archiving updates', function () { const expectedExportedUpdates = this.updates .slice() .reverse() - .map((update) => { + .map(update => { // clone object, updates are created once in before handler const exportedUpdate = Object.assign({}, update) exportedUpdate.meta = Object.assign({}, update.meta) @@ -180,7 +180,7 @@ describe('Archiving updates', function () { expect(this.exportedUpdates).to.deep.equal(expectedExportedUpdates) expect(this.exportedUserIds).to.deep.equal([ this.user_id, - this.user_id_2 + this.user_id_2, ]) }) }) @@ -192,16 +192,12 @@ describe('Archiving updates', function () { describe("archiving a doc's updates", function () { before(function (done) { - TrackChangesClient.pushDocHistory( - this.project_id, - this.doc_id, - (error) => { - if (error != null) { - throw error - } - return done() + TrackChangesClient.pushDocHistory(this.project_id, this.doc_id, error => { + if (error != null) { + throw error } - ) + return done() + }) return null }) @@ -222,7 +218,7 @@ describe('Archiving updates', function () { return db.docHistory.deleteMany( { doc_id: ObjectId(this.doc_id), - expiresAt: { $exists: true } + expiresAt: { $exists: true }, }, (err, result) => { if (typeof error !== 'undefined' && error !== null) { @@ -295,16 +291,12 @@ describe('Archiving updates', function () { return describe("unarchiving a doc's updates", function () { before(function (done) { - TrackChangesClient.pullDocHistory( - this.project_id, - this.doc_id, - (error) => { - if (error != null) { - throw error - } - return done() + TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, error => { + if (error != null) { + throw error } - ) + return done() + }) return null }) diff --git a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js index 99851d9515..65e5ecc468 100644 --- a/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/FlushingUpdatesTests.js @@ -40,17 +40,17 @@ describe('Flushing updates', function () { { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } return TrackChangesClient.flushDoc( this.project_id, this.doc_id, - (error) => { + error => { if (error != null) { throw error } @@ -67,8 +67,8 @@ describe('Flushing updates', function () { expect(updates[0].pack[0].op).to.deep.equal([ { p: 3, - i: 'f' - } + i: 'f', + }, ]) return done() }) @@ -87,8 +87,8 @@ describe('Flushing updates', function () { MockWebApi.projects[this.project_id] = { features: { - versioning: true - } + versioning: true, + }, } TrackChangesClient.pushRawUpdates( @@ -98,19 +98,19 @@ describe('Flushing updates', function () { { op: [{ i: 'g', p: 2 }], meta: { ts: Date.now() - 2 * this.weeks, user_id: this.user_id }, - v: 2 + v: 2, }, { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } - return TrackChangesClient.flushProject(this.project_id, (error) => { + return TrackChangesClient.flushProject(this.project_id, error => { if (error != null) { throw error } @@ -154,8 +154,8 @@ describe('Flushing updates', function () { MockWebApi.projects[this.project_id] = { features: { - versioning: false - } + versioning: false, + }, } TrackChangesClient.pushRawUpdates( @@ -165,19 +165,19 @@ describe('Flushing updates', function () { { op: [{ i: 'g', p: 2 }], meta: { ts: Date.now() - 2 * this.weeks, user_id: this.user_id }, - v: 2 + v: 2, }, { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } - return TrackChangesClient.flushProject(this.project_id, (error) => { + return TrackChangesClient.flushProject(this.project_id, error => { if (error != null) { throw error } @@ -210,13 +210,13 @@ describe('Flushing updates', function () { MockWebApi.projects[this.project_id] = { features: { - versioning: false - } + versioning: false, + }, } TrackChangesClient.setPreserveHistoryForProject( this.project_id, - (error) => { + error => { if (error != null) { throw error } @@ -228,23 +228,23 @@ describe('Flushing updates', function () { op: [{ i: 'g', p: 2 }], meta: { ts: Date.now() - 2 * this.weeks, - user_id: this.user_id + user_id: this.user_id, }, - v: 2 + v: 2, }, { op: [{ i: 'f', p: 3 }], meta: { ts: Date.now(), user_id: this.user_id }, - v: 3 - } + v: 3, + }, ], - (error) => { + error => { if (error != null) { throw error } return TrackChangesClient.flushProject( this.project_id, - (error) => { + error => { if (error != null) { throw error } diff --git a/services/track-changes/test/acceptance/js/GettingADiffTests.js b/services/track-changes/test/acceptance/js/GettingADiffTests.js index b0390db9e4..f2de3d7d7f 100644 --- a/services/track-changes/test/acceptance/js/GettingADiffTests.js +++ b/services/track-changes/test/acceptance/js/GettingADiffTests.js @@ -35,7 +35,7 @@ describe('Getting a diff', function () { email: 'user@sharelatex.com', first_name: 'Leo', last_name: 'Lion', - id: this.user_id + id: this.user_id, } sinon.spy(MockWebApi, 'getUserInfo') @@ -45,23 +45,23 @@ describe('Getting a diff', function () { { op: [{ i: 'one ', p: 0 }], meta: { ts: this.from - twoMinutes, user_id: this.user_id }, - v: 3 + v: 3, }, { op: [{ i: 'two ', p: 4 }], meta: { ts: this.from + twoMinutes, user_id: this.user_id }, - v: (this.fromVersion = 4) + v: (this.fromVersion = 4), }, { op: [{ i: 'three ', p: 8 }], meta: { ts: this.to - twoMinutes, user_id: this.user_id }, - v: (this.toVersion = 5) + v: (this.toVersion = 5), }, { op: [{ i: 'four', p: 14 }], meta: { ts: this.to + twoMinutes, user_id: this.user_id }, - v: 6 - } + v: 6, + }, ] this.lines = ['one two three four'] this.expected_diff = [ @@ -71,21 +71,21 @@ describe('Getting a diff', function () { meta: { start_ts: this.from + twoMinutes, end_ts: this.to - twoMinutes, - user: this.user - } - } + user: this.user, + }, + }, ] MockDocUpdaterApi.docs[this.doc_id] = { lines: this.lines, - version: 7 + version: 7, } TrackChangesApp.ensureRunning(() => { return TrackChangesClient.pushRawUpdates( this.project_id, this.doc_id, this.updates, - (error) => { + error => { if (error != null) { throw error } diff --git a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js index f58c9b729f..d3fce21171 100644 --- a/services/track-changes/test/acceptance/js/GettingUpdatesTests.js +++ b/services/track-changes/test/acceptance/js/GettingUpdatesTests.js @@ -33,15 +33,15 @@ describe('Getting updates', function () { MockWebApi.projects[this.project_id] = { features: { - versioning: true - } + versioning: true, + }, } MockWebApi.users[this.user_id] = this.user = { email: 'user@sharelatex.com', first_name: 'Leo', last_name: 'Lion', - id: this.user_id + id: this.user_id, } sinon.spy(MockWebApi, 'getUserInfo') @@ -51,14 +51,14 @@ describe('Getting updates', function () { op: [{ i: 'a', p: 0 }], meta: { ts: this.now - (9 - i) * this.hours - 2 * this.minutes, - user_id: this.user_id + user_id: this.user_id, }, - v: 2 * i + 1 + v: 2 * i + 1, }) this.updates.push({ op: [{ i: 'b', p: 0 }], meta: { ts: this.now - (9 - i) * this.hours, user_id: this.user_id }, - v: 2 * i + 2 + v: 2 * i + 2, }) } this.updates[0].meta.user_id = this.deleted_user_id @@ -68,7 +68,7 @@ describe('Getting updates', function () { this.project_id, this.doc_id, this.updates, - (error) => { + error => { if (error != null) { throw error } @@ -82,7 +82,7 @@ describe('Getting updates', function () { after() { MockWebApi.getUserInfo.restore() return null - } + }, }) describe('getting updates up to the limit', function () { @@ -118,25 +118,25 @@ describe('Getting updates', function () { meta: { start_ts: this.to - 2 * this.minutes, end_ts: this.to, - users: [this.user] - } + users: [this.user], + }, }, { docs: docs2, meta: { start_ts: this.to - 1 * this.hours - 2 * this.minutes, end_ts: this.to - 1 * this.hours, - users: [this.user] - } + users: [this.user], + }, }, { docs: docs3, meta: { start_ts: this.to - 2 * this.hours - 2 * this.minutes, end_ts: this.to - 2 * this.hours, - users: [this.user] - } - } + users: [this.user], + }, + }, ]) }) }) @@ -168,17 +168,17 @@ describe('Getting updates', function () { meta: { start_ts: this.to - 8 * this.hours - 2 * this.minutes, end_ts: this.to - 8 * this.hours, - users: [this.user] - } + users: [this.user], + }, }, { docs: docs2, meta: { start_ts: this.to - 9 * this.hours - 2 * this.minutes, end_ts: this.to - 9 * this.hours, - users: [this.user, null] - } - } + users: [this.user, null], + }, + }, ]) }) }) diff --git a/services/track-changes/test/acceptance/js/LockManagerTests.js b/services/track-changes/test/acceptance/js/LockManagerTests.js index 47341f7891..104e7c3220 100644 --- a/services/track-changes/test/acceptance/js/LockManagerTests.js +++ b/services/track-changes/test/acceptance/js/LockManagerTests.js @@ -27,24 +27,24 @@ describe('Locking document', function () { LockManager.LOCK_TTL = 1 // second LockManager.runWithLock( 'doc123', - (releaseA) => { + releaseA => { // we create a lock A and allow it to expire in redis return setTimeout( () => // now we create a new lock B and try to release A LockManager.runWithLock( 'doc123', - (releaseB) => { + releaseB => { return releaseA() }, // try to release lock A to see if it wipes out lock B - (error) => {} + error => {} ), // we never release lock B so nothing should happen here 1500 ) }, // enough time to wait until the lock has expired - (error) => + error => // we get here after trying to release lock A done() ) diff --git a/services/track-changes/test/acceptance/js/RestoringVersions.js b/services/track-changes/test/acceptance/js/RestoringVersions.js index 1e9f0d43a5..312d92bef9 100644 --- a/services/track-changes/test/acceptance/js/RestoringVersions.js +++ b/services/track-changes/test/acceptance/js/RestoringVersions.js @@ -35,23 +35,23 @@ describe('Restoring a version', function () { { op: [{ i: 'one ', p: 0 }], meta: { ts: this.now - 6 * minutes, user_id: this.user_id }, - v: 3 + v: 3, }, { op: [{ i: 'two ', p: 4 }], meta: { ts: this.now - 4 * minutes, user_id: this.user_id }, - v: 4 + v: 4, }, { op: [{ i: 'three ', p: 8 }], meta: { ts: this.now - 2 * minutes, user_id: this.user_id }, - v: 5 + v: 5, }, { op: [{ i: 'four', p: 14 }], meta: { ts: this.now, user_id: this.user_id }, - v: 6 - } + v: 6, + }, ] this.lines = ['one two three four'] this.restored_lines = ['one two '] @@ -61,12 +61,12 @@ describe('Restoring a version', function () { email: 'user@sharelatex.com', first_name: 'Leo', last_name: 'Lion', - id: this.user_id + id: this.user_id, } MockDocUpdaterApi.docs[this.doc_id] = { lines: this.lines, - version: 7 + version: 7, } TrackChangesApp.ensureRunning(() => { @@ -74,7 +74,7 @@ describe('Restoring a version', function () { this.project_id, this.doc_id, this.updates, - (error) => { + error => { if (error != null) { throw error } @@ -83,7 +83,7 @@ describe('Restoring a version', function () { this.doc_id, this.beforeVersion, this.user_id, - (error) => { + error => { if (error != null) { throw error } diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js index b1ebcf9cef..12787770a4 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocStoreApi.js @@ -39,16 +39,16 @@ module.exports = MockDocUpdaterApi = { }) return app - .listen(3016, (error) => { + .listen(3016, error => { if (error != null) { throw error } }) - .on('error', (error) => { + .on('error', error => { console.error('error starting MockDocStoreApi:', error.message) return process.exit(1) }) - } + }, } MockDocUpdaterApi.run() diff --git a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js index b3b5e23ba7..1c39ff5dbc 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockDocUpdaterApi.js @@ -74,16 +74,16 @@ module.exports = MockDocUpdaterApi = { }) return app - .listen(3003, (error) => { + .listen(3003, error => { if (error != null) { throw error } }) - .on('error', (error) => { + .on('error', error => { console.error('error starting MockDocUpdaterApi:', error.message) return process.exit(1) }) - } + }, } MockDocUpdaterApi.run() diff --git a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js index b7ebbc876f..db6968dc54 100644 --- a/services/track-changes/test/acceptance/js/helpers/MockWebApi.js +++ b/services/track-changes/test/acceptance/js/helpers/MockWebApi.js @@ -61,16 +61,16 @@ module.exports = MockWebApi = { }) return app - .listen(3000, (error) => { + .listen(3000, error => { if (error != null) { throw error } }) - .on('error', (error) => { + .on('error', error => { console.error('error starting MockWebApiServer:', error.message) return process.exit(1) }) - } + }, } MockWebApi.run() diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js index 7840b1f569..f6ff0cc024 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesApp.js @@ -38,10 +38,10 @@ module.exports = { Settings.internal != null ? Settings.internal.trackchanges : undefined, - (x) => x.port + x => x.port ), 'localhost', - (error) => { + error => { if (error != null) { throw error } @@ -58,7 +58,7 @@ module.exports = { } ) }) - } + }, } function __guard__(value, transform) { return typeof value !== 'undefined' && value !== null diff --git a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js index a48ccc7a78..0216c2c928 100644 --- a/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js +++ b/services/track-changes/test/acceptance/js/helpers/TrackChangesClient.js @@ -28,7 +28,7 @@ const s3 = new aws.S3({ accessKeyId: Settings.trackchanges.s3.key, secretAccessKey: Settings.trackchanges.s3.secret, endpoint: Settings.trackchanges.s3.endpoint, - s3ForcePathStyle: Settings.trackchanges.s3.pathStyle + s3ForcePathStyle: Settings.trackchanges.s3.pathStyle, }) const S3_BUCKET = Settings.trackchanges.stores.doc_history @@ -37,7 +37,7 @@ module.exports = TrackChangesClient = { if (callback == null) { callback = function (error, updates) {} } - return TrackChangesClient.flushDoc(project_id, doc_id, (error) => { + return TrackChangesClient.flushDoc(project_id, doc_id, error => { if (error != null) { return callback(error) } @@ -51,7 +51,7 @@ module.exports = TrackChangesClient = { } return request.post( { - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush` + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush`, }, (error, response, body) => { response.statusCode.should.equal(204) @@ -66,7 +66,7 @@ module.exports = TrackChangesClient = { } return request.post( { - url: `http://localhost:3015/project/${project_id}/flush` + url: `http://localhost:3015/project/${project_id}/flush`, }, (error, response, body) => { response.statusCode.should.equal(204) @@ -91,7 +91,7 @@ module.exports = TrackChangesClient = { } return db.projectHistoryMetaData.findOne( { - project_id: ObjectId(project_id) + project_id: ObjectId(project_id), }, callback ) @@ -103,13 +103,13 @@ module.exports = TrackChangesClient = { } return db.projectHistoryMetaData.updateOne( { - project_id: ObjectId(project_id) + project_id: ObjectId(project_id), }, { - $set: { preserveHistory: true } + $set: { preserveHistory: true }, }, { - upsert: true + upsert: true, }, callback ) @@ -122,13 +122,13 @@ module.exports = TrackChangesClient = { return rclient.sadd( Keys.docsWithHistoryOps({ project_id }), doc_id, - (error) => { + error => { if (error != null) { return callback(error) } return rclient.rpush( Keys.uncompressedHistoryOps({ doc_id }), - ...Array.from(Array.from(updates).map((u) => JSON.stringify(u))), + ...Array.from(Array.from(updates).map(u => JSON.stringify(u))), callback ) } @@ -141,7 +141,7 @@ module.exports = TrackChangesClient = { } return request.get( { - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}` + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}`, }, (error, response, body) => { response.statusCode.should.equal(200) @@ -156,7 +156,7 @@ module.exports = TrackChangesClient = { } return request.get( { - url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}` + url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}`, }, (error, response, body) => { response.statusCode.should.equal(200) @@ -184,8 +184,8 @@ module.exports = TrackChangesClient = { { url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/version/${version}/restore`, headers: { - 'X-User-Id': user_id - } + 'X-User-Id': user_id, + }, }, (error, response, body) => { response.statusCode.should.equal(204) @@ -200,7 +200,7 @@ module.exports = TrackChangesClient = { } return request.post( { - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push` + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push`, }, (error, response, body) => { response.statusCode.should.equal(204) @@ -215,7 +215,7 @@ module.exports = TrackChangesClient = { } return request.post( { - url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull` + url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull`, }, (error, response, body) => { response.statusCode.should.equal(204) @@ -254,7 +254,7 @@ module.exports = TrackChangesClient = { } const params = { Bucket: S3_BUCKET, - Key: `${project_id}/changes-${doc_id}/pack-${pack_id}` + Key: `${project_id}/changes-${doc_id}/pack-${pack_id}`, } return s3.getObject(params, (error, data) => { @@ -280,7 +280,7 @@ module.exports = TrackChangesClient = { } let params = { Bucket: S3_BUCKET, - Prefix: `${project_id}/changes-${doc_id}` + Prefix: `${project_id}/changes-${doc_id}`, } return s3.listObjects(params, (error, data) => { @@ -291,11 +291,11 @@ module.exports = TrackChangesClient = { params = { Bucket: S3_BUCKET, Delete: { - Objects: data.Contents.map((s3object) => ({ Key: s3object.Key })) - } + Objects: data.Contents.map(s3object => ({ Key: s3object.Key })), + }, } return s3.deleteObjects(params, callback) }) - } + }, } diff --git a/services/track-changes/test/setup.js b/services/track-changes/test/setup.js index 9ebe599cea..17e1782172 100644 --- a/services/track-changes/test/setup.js +++ b/services/track-changes/test/setup.js @@ -14,8 +14,8 @@ SandboxedModule.configure({ warn() {}, err() {}, error() {}, - fatal() {} - } + fatal() {}, + }, }, - globals: { Buffer, JSON, console, process } + globals: { Buffer, JSON, console, process }, }) diff --git a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js index 75a8ab2c0f..2b67220de9 100644 --- a/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js +++ b/services/track-changes/test/unit/js/DiffGenerator/DiffGeneratorTests.js @@ -24,7 +24,7 @@ describe('DiffGenerator', function () { return (this.meta = { start_ts: this.ts, end_ts: this.ts, - user_id: this.user_id + user_id: this.user_id, }) }) @@ -34,7 +34,7 @@ describe('DiffGenerator', function () { const content = 'hello world' const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, - i: 'wo' + i: 'wo', }) return rewoundContent.should.equal('hello rld') }) @@ -45,7 +45,7 @@ describe('DiffGenerator', function () { const content = 'hello rld' const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 6, - d: 'wo' + d: 'wo', }) return rewoundContent.should.equal('hello world') }) @@ -65,7 +65,7 @@ describe('DiffGenerator', function () { const content = 'foobar' const rewoundContent = this.DiffGenerator.rewindOp(content, { p: 4, - i: 'bar' + i: 'bar', }) return rewoundContent.should.equal('foo') }) @@ -78,8 +78,8 @@ describe('DiffGenerator', function () { const update = { op: [ { p: 3, i: 'bbb' }, - { p: 6, i: 'ccc' } - ] + { p: 6, i: 'ccc' }, + ], } const rewoundContent = this.DiffGenerator.rewindUpdate(content, update) return rewoundContent.should.equal('aaa') @@ -91,7 +91,7 @@ describe('DiffGenerator', function () { const content = 'aaabbbccc' const updates = [ { op: [{ p: 3, i: 'bbb' }] }, - { op: [{ p: 6, i: 'ccc' }] } + { op: [{ p: 6, i: 'ccc' }] }, ] const rewoundContent = this.DiffGenerator.rewindUpdates(content, updates) return rewoundContent.should.equal('aaa') @@ -105,7 +105,7 @@ describe('DiffGenerator', function () { this.updates = [ { i: 'mock-update-1' }, { i: 'mock-update-2' }, - { i: 'mock-update-3' } + { i: 'mock-update-3' }, ] this.DiffGenerator.applyUpdateToDiff = sinon.stub().returns(this.diff) this.DiffGenerator.compressDiff = sinon.stub().returns(this.diff) @@ -124,8 +124,8 @@ describe('DiffGenerator', function () { .calledWith( [ { - u: this.content - } + u: this.content, + }, ], this.updates[0] ) @@ -133,7 +133,7 @@ describe('DiffGenerator', function () { }) it('should apply each update', function () { - return Array.from(this.updates).map((update) => + return Array.from(this.updates).map(update => this.DiffGenerator.applyUpdateToDiff .calledWith(sinon.match.any, update) .should.equal(true) @@ -153,18 +153,18 @@ describe('DiffGenerator', function () { const diff = this.DiffGenerator.compressDiff([ { i: 'foo', - meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }, }, { i: 'bar', - meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } } - } + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }, + }, ]) return expect(diff).to.deep.equal([ { i: 'foobar', - meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } } - } + meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }, + }, ]) }) }) @@ -174,12 +174,12 @@ describe('DiffGenerator', function () { const input = [ { i: 'foo', - meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }, }, { i: 'bar', - meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } } - } + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }, + }, ] const output = this.DiffGenerator.compressDiff(input) return expect(output).to.deep.equal(input) @@ -191,18 +191,18 @@ describe('DiffGenerator', function () { const diff = this.DiffGenerator.compressDiff([ { d: 'foo', - meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }, }, { d: 'bar', - meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } } - } + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id } }, + }, ]) return expect(diff).to.deep.equal([ { d: 'foobar', - meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } } - } + meta: { start_ts: 5, end_ts: 20, user: { id: this.user_id } }, + }, ]) }) }) @@ -212,12 +212,12 @@ describe('DiffGenerator', function () { const input = [ { d: 'foo', - meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } } + meta: { start_ts: 10, end_ts: 20, user: { id: this.user_id } }, }, { d: 'bar', - meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } } - } + meta: { start_ts: 5, end_ts: 15, user: { id: this.user_id_2 } }, + }, ] const output = this.DiffGenerator.compressDiff(input) return expect(output).to.deep.equal(input) @@ -230,34 +230,34 @@ describe('DiffGenerator', function () { it('should insert into the middle of (u)nchanged text', function () { const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { op: [{ p: 3, i: 'baz' }], - meta: this.meta + meta: this.meta, }) return expect(diff).to.deep.equal([ { u: 'foo' }, { i: 'baz', meta: this.meta }, - { u: 'bar' } + { u: 'bar' }, ]) }) it('should insert into the start of (u)changed text', function () { const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { op: [{ p: 0, i: 'baz' }], - meta: this.meta + meta: this.meta, }) return expect(diff).to.deep.equal([ { i: 'baz', meta: this.meta }, - { u: 'foobar' } + { u: 'foobar' }, ]) }) it('should insert into the end of (u)changed text', function () { const diff = this.DiffGenerator.applyUpdateToDiff([{ u: 'foobar' }], { op: [{ p: 6, i: 'baz' }], - meta: this.meta + meta: this.meta, }) return expect(diff).to.deep.equal([ { u: 'foobar' }, - { i: 'baz', meta: this.meta } + { i: 'baz', meta: this.meta }, ]) }) @@ -269,7 +269,7 @@ describe('DiffGenerator', function () { return expect(diff).to.deep.equal([ { i: 'foo', meta: this.meta }, { i: 'baz', meta: this.meta }, - { i: 'bar', meta: this.meta } + { i: 'bar', meta: this.meta }, ]) }) @@ -282,7 +282,7 @@ describe('DiffGenerator', function () { { d: 'deleted', meta: this.meta }, { u: 'foo' }, { i: 'baz', meta: this.meta }, - { u: 'bar' } + { u: 'bar' }, ]) }) }) @@ -297,7 +297,7 @@ describe('DiffGenerator', function () { return expect(diff).to.deep.equal([ { u: 'foo' }, { d: 'baz', meta: this.meta }, - { u: 'bar' } + { u: 'bar' }, ]) }) @@ -308,7 +308,7 @@ describe('DiffGenerator', function () { ) return expect(diff).to.deep.equal([ { d: 'foo', meta: this.meta }, - { u: 'bazbar' } + { u: 'bazbar' }, ]) }) @@ -319,7 +319,7 @@ describe('DiffGenerator', function () { ) return expect(diff).to.deep.equal([ { u: 'foobaz' }, - { d: 'bar', meta: this.meta } + { d: 'bar', meta: this.meta }, ]) }) @@ -333,7 +333,7 @@ describe('DiffGenerator', function () { { d: 'o', meta: this.meta }, { d: 'baz', meta: this.meta }, { d: 'b', meta: this.meta }, - { u: 'ar' } + { u: 'ar' }, ]) }) }) @@ -346,7 +346,7 @@ describe('DiffGenerator', function () { ) return expect(diff).to.deep.equal([ { i: 'foo', meta: this.meta }, - { i: 'bar', meta: this.meta } + { i: 'bar', meta: this.meta }, ]) }) @@ -375,7 +375,7 @@ describe('DiffGenerator', function () { { u: 'fo' }, { d: 'o', meta: this.meta }, { d: 'b', meta: this.meta }, - { u: 'ar' } + { u: 'ar' }, ]) }) }) @@ -391,7 +391,7 @@ describe('DiffGenerator', function () { { d: 'o', meta: this.meta }, { d: 'baz', meta: this.meta }, { d: 'b', meta: this.meta }, - { u: 'ar' } + { u: 'ar' }, ]) }) }) @@ -401,7 +401,7 @@ describe('DiffGenerator', function () { return expect(() => this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { op: [{ p: 3, d: 'xxx' }], - meta: this.meta + meta: this.meta, }) ).to.throw(this.DiffGenerator.ConsistencyError) }) @@ -410,7 +410,7 @@ describe('DiffGenerator', function () { return expect(() => this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { op: [{ p: 0, d: 'xxx' }], - meta: this.meta + meta: this.meta, }) ).to.throw(this.DiffGenerator.ConsistencyError) }) @@ -419,7 +419,7 @@ describe('DiffGenerator', function () { return expect(() => this.DiffGenerator.applyUpdateToDiff([{ u: 'foobazbar' }], { op: [{ p: 6, d: 'xxx' }], - meta: this.meta + meta: this.meta, }) ).to.throw(this.DiffGenerator.ConsistencyError) }) @@ -434,7 +434,7 @@ describe('DiffGenerator', function () { return expect(diff).to.deep.equal([ { u: 'foo' }, { i: 'baz', meta: this.meta }, - { d: 'bar', meta: this.meta } + { d: 'bar', meta: this.meta }, ]) }) }) @@ -447,7 +447,7 @@ describe('DiffGenerator', function () { ) return expect(diff).to.deep.equal([ { d: 'bar', meta: this.meta }, - { i: 'baz', meta: this.meta } + { i: 'baz', meta: this.meta }, ]) }) }) diff --git a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js index b2f4ec6a59..846ad706c2 100644 --- a/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js +++ b/services/track-changes/test/unit/js/DiffManager/DiffManagerTests.js @@ -21,8 +21,8 @@ describe('DiffManager', function () { requires: { './UpdatesManager': (this.UpdatesManager = {}), './DocumentUpdaterManager': (this.DocumentUpdaterManager = {}), - './DiffGenerator': (this.DiffGenerator = {}) - } + './DiffGenerator': (this.DiffGenerator = {}), + }, }) this.callback = sinon.stub() this.from = new Date() @@ -114,23 +114,23 @@ describe('DiffManager', function () { { op: 'mock-4', v: 42, - meta: { start_ts: new Date(this.to.getTime() + 20) } + meta: { start_ts: new Date(this.to.getTime() + 20) }, }, { op: 'mock-3', v: 41, - meta: { start_ts: new Date(this.to.getTime() + 10) } + meta: { start_ts: new Date(this.to.getTime() + 10) }, }, { op: 'mock-2', v: 40, - meta: { start_ts: new Date(this.to.getTime() - 10) } + meta: { start_ts: new Date(this.to.getTime() - 10) }, }, { op: 'mock-1', v: 39, - meta: { start_ts: new Date(this.to.getTime() - 20) } - } + meta: { start_ts: new Date(this.to.getTime() - 20) }, + }, ] this.fromVersion = 39 this.toVersion = 40 @@ -333,23 +333,23 @@ describe('DiffManager', function () { { op: 'mock-4', v: 42, - meta: { start_ts: new Date(this.to.getTime() + 20) } + meta: { start_ts: new Date(this.to.getTime() + 20) }, }, { op: 'mock-3', v: 41, - meta: { start_ts: new Date(this.to.getTime() + 10) } + meta: { start_ts: new Date(this.to.getTime() + 10) }, }, { op: 'mock-2', v: 40, - meta: { start_ts: new Date(this.to.getTime() - 10) } + meta: { start_ts: new Date(this.to.getTime() - 10) }, }, { op: 'mock-1', v: 39, - meta: { start_ts: new Date(this.to.getTime() - 20) } - } + meta: { start_ts: new Date(this.to.getTime() - 20) }, + }, ] this.fromVersion = 39 this.rewound_content = 'rewound-content' @@ -400,7 +400,7 @@ describe('DiffManager', function () { this.version = 50 this.updates = [ { op: 'mock-1', v: 40 }, - { op: 'mock-1', v: 39 } + { op: 'mock-1', v: 39 }, ] this.DiffManager.getLatestDocAndUpdates = sinon .stub() diff --git a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js index cc539c8393..72bcaccddf 100644 --- a/services/track-changes/test/unit/js/DocArchive/MongoAWS.js +++ b/services/track-changes/test/unit/js/DocArchive/MongoAWS.js @@ -25,12 +25,12 @@ describe('MongoAWS', function () { trackchanges: { s3: { secret: 's3-secret', - key: 's3-key' + key: 's3-key', }, stores: { - doc_history: 's3-bucket' - } - } + doc_history: 's3-bucket', + }, + }, }), child_process: (this.child_process = {}), 'mongo-uri': (this.mongouri = {}), @@ -40,8 +40,8 @@ describe('MongoAWS', function () { './mongodb': { db: (this.db = {}), ObjectId }, JSONStream: (this.JSONStream = {}), 'readline-stream': (this.readline = sinon.stub()), - '@overleaf/metrics': { inc() {} } - } + '@overleaf/metrics': { inc() {} }, + }, }) this.project_id = ObjectId().toString() diff --git a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js index c8a218d2a3..8221f98d06 100644 --- a/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js +++ b/services/track-changes/test/unit/js/DocumentUpdaterManager/DocumentUpdaterManagerTests.js @@ -20,9 +20,9 @@ describe('DocumentUpdaterManager', function () { requires: { request: (this.request = {}), '@overleaf/settings': (this.settings = { - apis: { documentupdater: { url: 'http://example.com' } } - }) - } + apis: { documentupdater: { url: 'http://example.com' } }, + }), + }, }) this.callback = sinon.stub() this.lines = ['one', 'two', 'three'] @@ -35,7 +35,7 @@ describe('DocumentUpdaterManager', function () { this.body = JSON.stringify({ lines: this.lines, version: this.version, - ops: [] + ops: [], }) this.request.get = sinon .stub() @@ -135,8 +135,8 @@ describe('DocumentUpdaterManager', function () { lines: this.content.split('\n'), source: 'restore', user_id: this.user_id, - undoing: true - } + undoing: true, + }, }) .should.equal(true) }) diff --git a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js index 9746213922..6b82dd2b80 100644 --- a/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js +++ b/services/track-changes/test/unit/js/HttpController/HttpControllerTests.js @@ -24,8 +24,8 @@ describe('HttpController', function () { './RestoreManager': (this.RestoreManager = {}), './PackManager': (this.PackManager = {}), './DocArchiveManager': (this.DocArchiveManager = {}), - './HealthChecker': (this.HealthChecker = {}) - } + './HealthChecker': (this.HealthChecker = {}), + }, }) this.doc_id = 'doc-id-123' this.project_id = 'project-id-123' @@ -39,8 +39,8 @@ describe('HttpController', function () { this.req = { params: { doc_id: this.doc_id, - project_id: this.project_id - } + project_id: this.project_id, + }, } this.res = { sendStatus: sinon.stub() } this.UpdatesManager.processUncompressedUpdatesWithLock = sinon @@ -64,8 +64,8 @@ describe('HttpController', function () { beforeEach(function () { this.req = { params: { - project_id: this.project_id - } + project_id: this.project_id, + }, } this.res = { sendStatus: sinon.stub() } this.UpdatesManager.processUncompressedUpdatesForProject = sinon @@ -92,12 +92,12 @@ describe('HttpController', function () { this.req = { params: { doc_id: this.doc_id, - project_id: this.project_id + project_id: this.project_id, }, query: { from: this.from.toString(), - to: this.to.toString() - } + to: this.to.toString(), + }, } this.res = { json: sinon.stub() } this.diff = [{ u: 'mock-diff' }] @@ -128,12 +128,12 @@ describe('HttpController', function () { this.min_count = 10 this.req = { params: { - project_id: this.project_id + project_id: this.project_id, }, query: { before: this.before.toString(), - min_count: this.min_count.toString() - } + min_count: this.min_count.toString(), + }, } this.res = { json: sinon.stub() } this.updates = ['mock-summarized-updates'] @@ -147,7 +147,7 @@ describe('HttpController', function () { return this.UpdatesManager.getSummarizedProjectUpdates .calledWith(this.project_id, { before: this.before, - min_count: this.min_count + min_count: this.min_count, }) .should.equal(true) }) @@ -156,7 +156,7 @@ describe('HttpController', function () { return this.res.json .calledWith({ updates: this.updates, - nextBeforeTimestamp: this.nextBeforeTimestamp + nextBeforeTimestamp: this.nextBeforeTimestamp, }) .should.equal(true) }) @@ -169,11 +169,11 @@ describe('HttpController', function () { params: { doc_id: this.doc_id, project_id: this.project_id, - version: this.version + version: this.version, }, headers: { - 'x-user-id': this.user_id - } + 'x-user-id': this.user_id, + }, } this.res = { sendStatus: sinon.stub() } diff --git a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js index 1c933c3eea..fd76bc0d2d 100644 --- a/services/track-changes/test/unit/js/LockManager/LockManagerTests.js +++ b/services/track-changes/test/unit/js/LockManager/LockManagerTests.js @@ -24,18 +24,18 @@ describe('LockManager', function () { beforeEach(function () { this.Settings = { redis: { - lock: {} - } + lock: {}, + }, } this.LockManager = SandboxedModule.require(modulePath, { requires: { '@overleaf/redis-wrapper': { createClient: () => { return (this.rclient = { auth: sinon.stub() }) - } + }, }, - '@overleaf/settings': this.Settings - } + '@overleaf/settings': this.Settings, + }, }) this.key = 'lock-key' @@ -240,7 +240,7 @@ describe('LockManager', function () { describe('when the runner function returns an error', function () { beforeEach(function () { this.error = new Error('oops') - this.runner = (releaseLock) => { + this.runner = releaseLock => { if (releaseLock == null) { releaseLock = function (error) {} } diff --git a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js index d96b27d8d0..6e560e7ab8 100644 --- a/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js +++ b/services/track-changes/test/unit/js/MongoManager/MongoManagerTests.js @@ -24,8 +24,8 @@ describe('MongoManager', function () { requires: { './mongodb': { db: (this.db = {}), ObjectId }, './PackManager': (this.PackManager = {}), - '@overleaf/metrics': { timeAsyncMethod() {} } - } + '@overleaf/metrics': { timeAsyncMethod() {} }, + }, }) this.callback = sinon.stub() this.doc_id = ObjectId().toString() @@ -166,10 +166,10 @@ describe('MongoManager', function () { .calledWith( { doc_id: ObjectId(this.doc_id), - project_id: { $exists: false } + project_id: { $exists: false }, }, { - $set: { project_id: ObjectId(this.project_id) } + $set: { project_id: ObjectId(this.project_id) }, } ) .should.equal(true) @@ -184,7 +184,7 @@ describe('MongoManager', function () { beforeEach(function () { this.metadata = { mock: 'metadata' } this.db.projectHistoryMetaData = { - findOne: sinon.stub().callsArgWith(1, null, this.metadata) + findOne: sinon.stub().callsArgWith(1, null, this.metadata), } return this.MongoManager.getProjectMetaData( this.project_id, @@ -207,7 +207,7 @@ describe('MongoManager', function () { beforeEach(function () { this.metadata = { mock: 'metadata' } this.db.projectHistoryMetaData = { - updateOne: sinon.stub().yields() + updateOne: sinon.stub().yields(), } return this.MongoManager.setProjectMetaData( this.project_id, @@ -220,13 +220,13 @@ describe('MongoManager', function () { return this.db.projectHistoryMetaData.updateOne .calledWith( { - project_id: ObjectId(this.project_id) + project_id: ObjectId(this.project_id), }, { - $set: this.metadata + $set: this.metadata, }, { - upsert: true + upsert: true, } ) .should.equal(true) diff --git a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js index 1f85dcf38c..4f538f2109 100644 --- a/services/track-changes/test/unit/js/PackManager/PackManagerTests.js +++ b/services/track-changes/test/unit/js/PackManager/PackManagerTests.js @@ -31,9 +31,9 @@ describe('PackManager', function () { '@overleaf/metrics': { inc() {} }, './ProjectIterator': require('../../../../app/js/ProjectIterator.js'), // Cache for speed '@overleaf/settings': { - redis: { lock: { key_schema: {} } } - } - } + redis: { lock: { key_schema: {} } }, + }, + }, }) this.callback = sinon.stub() this.doc_id = ObjectId().toString() @@ -51,20 +51,20 @@ describe('PackManager', function () { _id: '12345', pack: [ { op: 'op-1', meta: 'meta-1', v: 1 }, - { op: 'op-2', meta: 'meta-2', v: 2 } + { op: 'op-2', meta: 'meta-2', v: 2 }, ], n: 2, - sz: 100 + sz: 100, } this.newUpdates = [ { op: 'op-3', meta: 'meta-3', v: 3 }, - { op: 'op-4', meta: 'meta-4', v: 4 } + { op: 'op-4', meta: 'meta-4', v: 4 }, ] return (this.db.docHistory = { insertOne: sinon.stub().yields(), insert: sinon.stub().callsArg(1), updateOne: sinon.stub().yields(), - findAndModify: sinon.stub().callsArg(1) + findAndModify: sinon.stub().callsArg(1), }) }) @@ -95,10 +95,10 @@ describe('PackManager', function () { return describe('for many small updates', function () { beforeEach(function () { - this.newUpdates = __range__(0, 2048, true).map((i) => ({ + this.newUpdates = __range__(0, 2048, true).map(i => ({ op: `op-${i}`, meta: `meta-${i}`, - v: i + v: i, })) return this.PackManager.insertCompressedUpdates( this.project_id, @@ -209,10 +209,10 @@ describe('PackManager', function () { describe('for many small updates', function () { beforeEach(function () { - this.newUpdates = __range__(0, 2048, true).map((i) => ({ + this.newUpdates = __range__(0, 2048, true).map(i => ({ op: `op-${i}`, meta: `meta-${i}`, - v: i + v: i, })) return this.PackManager.insertCompressedUpdates( this.project_id, @@ -292,12 +292,12 @@ describe('PackManager', function () { 0.75 * this.PackManager.MAX_SIZE, true ) - .map((j) => 'a') + .map(j => 'a') .join('') - this.newUpdates = [0, 1, 2, 3, 4].map((i) => ({ + this.newUpdates = [0, 1, 2, 3, 4].map(i => ({ op: `op-${i}-${longString}`, meta: `meta-${i}`, - v: i + v: i, })) return this.PackManager.insertCompressedUpdates( this.project_id, @@ -393,7 +393,7 @@ describe('PackManager', function () { doc_id: ObjectId(this.doc_id), n: this.newUpdates.length, v: this.newUpdates[0].v, - v_end: this.newUpdates[this.newUpdates.length - 1].v + v_end: this.newUpdates[this.newUpdates.length - 1].v, }) .should.equal(true) }) @@ -401,7 +401,7 @@ describe('PackManager', function () { it('should set an expiry time in the future', function () { return this.db.docHistory.insertOne .calledWithMatch({ - expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) + expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000), }) .should.equal(true) }) @@ -419,12 +419,12 @@ describe('PackManager', function () { _id: '12345', pack: [ { op: 'op-1', meta: 'meta-1', v: 1 }, - { op: 'op-2', meta: 'meta-2', v: 2 } + { op: 'op-2', meta: 'meta-2', v: 2 }, ], n: 2, sz: 100, meta: { start_ts: Date.now() - 6 * 3600 * 1000 }, - expiresAt: new Date(Date.now()) + expiresAt: new Date(Date.now()), } return this.PackManager.flushCompressedUpdates( @@ -444,7 +444,7 @@ describe('PackManager', function () { { _id: this.lastUpdate._id }, { $push: { pack: { $each: this.newUpdates } }, - $set: { v_end: this.newUpdates[this.newUpdates.length - 1].v } + $set: { v_end: this.newUpdates[this.newUpdates.length - 1].v }, } ) .should.equal(true) @@ -453,7 +453,7 @@ describe('PackManager', function () { it('should set an expiry time in the future', function () { return this.db.docHistory.updateOne .calledWithMatch(sinon.match.any, { - $set: { expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) } + $set: { expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) }, }) .should.equal(true) }) @@ -472,12 +472,12 @@ describe('PackManager', function () { _id: '12345', pack: [ { op: 'op-1', meta: 'meta-1', v: 1 }, - { op: 'op-2', meta: 'meta-2', v: 2 } + { op: 'op-2', meta: 'meta-2', v: 2 }, ], n: 2, sz: 100, meta: { start_ts: Date.now() - 6 * 3600 * 1000 }, - expiresAt: new Date(Date.now()) + expiresAt: new Date(Date.now()), } return this.PackManager.flushCompressedUpdates( @@ -499,7 +499,7 @@ describe('PackManager', function () { doc_id: ObjectId(this.doc_id), n: this.newUpdates.length, v: this.newUpdates[0].v, - v_end: this.newUpdates[this.newUpdates.length - 1].v + v_end: this.newUpdates[this.newUpdates.length - 1].v, }) .should.equal(true) }) @@ -522,12 +522,12 @@ describe('PackManager', function () { _id: '12345', pack: [ { op: 'op-1', meta: 'meta-1', v: 1 }, - { op: 'op-2', meta: 'meta-2', v: 2 } + { op: 'op-2', meta: 'meta-2', v: 2 }, ], n: 2, sz: 100, meta: { start_ts: Date.now() - 30 * 24 * 3600 * 1000 }, - expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000) + expiresAt: new Date(Date.now() - 30 * 24 * 3600 * 1000), } return this.PackManager.flushCompressedUpdates( @@ -549,7 +549,7 @@ describe('PackManager', function () { doc_id: ObjectId(this.doc_id), n: this.newUpdates.length, v: this.newUpdates[0].v, - v_end: this.newUpdates[this.newUpdates.length - 1].v + v_end: this.newUpdates[this.newUpdates.length - 1].v, }) .should.equal(true) }) @@ -557,7 +557,7 @@ describe('PackManager', function () { it('should set an expiry time in the future', function () { return this.db.docHistory.insertOne .calledWithMatch({ - expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000) + expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000), }) .should.equal(true) }) @@ -602,7 +602,7 @@ describe('PackManager', function () { describe('when an archive is in progress', function () { beforeEach(function () { this.db.docHistoryIndex = { - findOne: sinon.stub().callsArgWith(2, null, { inS3: false }) + findOne: sinon.stub().callsArgWith(2, null, { inS3: false }), } return this.PackManager.checkArchiveNotInProgress( this.project_id, @@ -624,7 +624,7 @@ describe('PackManager', function () { describe('when an archive is completed', function () { beforeEach(function () { this.db.docHistoryIndex = { - findOne: sinon.stub().callsArgWith(2, null, { inS3: true }) + findOne: sinon.stub().callsArgWith(2, null, { inS3: true }), } return this.PackManager.checkArchiveNotInProgress( this.project_id, @@ -646,7 +646,7 @@ describe('PackManager', function () { return describe('when the archive has not started or completed', function () { beforeEach(function () { this.db.docHistoryIndex = { - findOne: sinon.stub().callsArgWith(2, null, {}) + findOne: sinon.stub().callsArgWith(2, null, {}), } return this.PackManager.checkArchiveNotInProgress( this.project_id, diff --git a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js index 0196bc0ae2..f0c9b0f3d5 100644 --- a/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js +++ b/services/track-changes/test/unit/js/RedisManager/RedisManagerTests.js @@ -24,9 +24,9 @@ describe('RedisManager', function () { createClient: () => { return (this.rclient = { auth: sinon.stub(), - multi: () => this.rclient + multi: () => this.rclient, }) - } + }, }, '@overleaf/settings': { redis: { @@ -37,12 +37,12 @@ describe('RedisManager', function () { }, docsWithHistoryOps({ project_id }) { return `DocsWithHistoryOps:${project_id}` - } - } - } - } - } - } + }, + }, + }, + }, + }, + }, }) this.doc_id = 'doc-id-123' this.project_id = 'project-id-123' @@ -54,9 +54,9 @@ describe('RedisManager', function () { beforeEach(function () { this.rawUpdates = [ { v: 42, op: 'mock-op-42' }, - { v: 45, op: 'mock-op-45' } + { v: 45, op: 'mock-op-45' }, ] - this.jsonUpdates = Array.from(this.rawUpdates).map((update) => + this.jsonUpdates = Array.from(this.rawUpdates).map(update => JSON.stringify(update) ) this.rclient.lrange = sinon.stub().callsArgWith(3, null, this.jsonUpdates) diff --git a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js index dd664a6003..9047946405 100644 --- a/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js +++ b/services/track-changes/test/unit/js/RestoreManager/RestoreManagerTests.js @@ -19,8 +19,8 @@ describe('RestoreManager', function () { this.RestoreManager = SandboxedModule.require(modulePath, { requires: { './DocumentUpdaterManager': (this.DocumentUpdaterManager = {}), - './DiffManager': (this.DiffManager = {}) - } + './DiffManager': (this.DiffManager = {}), + }, }) this.callback = sinon.stub() this.project_id = 'mock-project-id' diff --git a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js index a703ae8084..cd7c6c4b76 100644 --- a/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js +++ b/services/track-changes/test/unit/js/UpdateCompressor/UpdateCompressorTests.js @@ -15,18 +15,18 @@ const modulePath = '../../../../app/js/UpdateCompressor.js' const SandboxedModule = require('sandboxed-module') const bigstring = __range__(0, 2 * 1024 * 1024, true) - .map((i) => 'a') + .map(i => 'a') .join('') const mediumstring = __range__(0, 1024 * 1024, true) - .map((j) => 'a') + .map(j => 'a') .join('') describe('UpdateCompressor', function () { beforeEach(function () { this.UpdateCompressor = SandboxedModule.require(modulePath, { requires: { - '../lib/diff_match_patch': require('../../../../app/lib/diff_match_patch') - } + '../lib/diff_match_patch': require('../../../../app/lib/diff_match_patch'), + }, }) this.user_id = 'user-id-1' this.other_user_id = 'user-id-2' @@ -41,37 +41,37 @@ describe('UpdateCompressor', function () { { op: [ (this.op1 = { p: 0, i: 'Foo' }), - (this.op2 = { p: 6, i: 'bar' }) + (this.op2 = { p: 6, i: 'bar' }), ], meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: [(this.op3 = { p: 10, i: 'baz' })], meta: { ts: this.ts2, user_id: this.other_user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { op: this.op1, meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: this.op2, meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: this.op3, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.other_user_id + user_id: this.other_user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -81,15 +81,15 @@ describe('UpdateCompressor', function () { { op: [], meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 - } + v: 42, + }, ]) ).to.deep.equal([ { op: this.UpdateCompressor.NOOP, meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - } + v: 42, + }, ]) }) @@ -100,23 +100,23 @@ describe('UpdateCompressor', function () { op: [ (this.op1 = { p: 0, i: 'Foo' }), (this.op2 = { p: 9, c: 'baz' }), - (this.op3 = { p: 6, i: 'bar' }) + (this.op3 = { p: 6, i: 'bar' }), ], meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 - } + v: 42, + }, ]) ).to.deep.equal([ { op: this.op1, meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: this.op3, meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - } + v: 42, + }, ]) }) }) @@ -130,44 +130,44 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: (this.op2 = { p: 6, i: 'bar' }), meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: (this.op3 = { p: 10, i: 'baz' }), meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.other_user_id + user_id: this.other_user_id, }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { op: [this.op1, this.op2], meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: [this.op3], meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.other_user_id + user_id: this.other_user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -179,17 +179,17 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 - } + v: 42, + }, ]) ).to.deep.equal([ { op: [], meta: { start_ts: this.ts1, end_ts: this.ts1, user_id: this.user_id }, - v: 42 - } + v: 42, + }, ]) }) }) @@ -202,13 +202,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 6, i: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -216,10 +216,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -229,13 +229,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 5, i: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -243,10 +243,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -256,13 +256,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 9, i: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -270,19 +270,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 9, i: 'bar' }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -292,13 +292,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 6, i: bigstring }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -306,19 +306,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 6, i: bigstring }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -328,13 +328,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: bigstring }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 3 + bigstring.length, i: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -342,19 +342,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 3 + bigstring.length, i: 'bar' }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -364,13 +364,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: mediumstring }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 3 + mediumstring.length, i: mediumstring }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -378,19 +378,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 3 + mediumstring.length, i: mediumstring }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) @@ -402,13 +402,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, d: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 3, d: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -416,10 +416,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -429,13 +429,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, d: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 1, d: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -443,10 +443,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -456,13 +456,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, d: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 9, d: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -470,19 +470,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 9, d: 'bar' }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) @@ -494,13 +494,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 5, d: 'o' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -508,10 +508,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -521,13 +521,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'fobaro' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 5, d: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -535,10 +535,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -548,13 +548,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 3, d: 'foo' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -562,10 +562,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -575,13 +575,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foo' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 9, d: 'bar' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -589,19 +589,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 9, d: 'bar' }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -611,13 +611,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, i: 'foobar' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 6, d: 'bardle' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -625,19 +625,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 6, d: 'bardle' }, meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) @@ -649,13 +649,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, d: 'one two three four five six seven eight' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 3, i: 'one 2 three four five six seven eight' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -663,19 +663,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 + v: 43, }, { op: { p: 7, i: '2' }, meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) @@ -685,13 +685,13 @@ describe('UpdateCompressor', function () { { op: { p: 3, d: 'one two three four five six seven eight' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 3, i: 'one two three four five six seven eight' }, meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -699,10 +699,10 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) @@ -714,13 +714,13 @@ describe('UpdateCompressor', function () { { op: this.UpdateCompressor.NOOP, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 6, i: 'bar' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -728,19 +728,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 6, i: 'bar' }, meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) @@ -752,13 +752,13 @@ describe('UpdateCompressor', function () { { op: this.UpdateCompressor.NOOP, meta: { ts: this.ts1, user_id: this.user_id }, - v: 42 + v: 42, }, { op: { p: 6, d: 'bar' }, meta: { ts: this.ts1, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ]) ).to.deep.equal([ { @@ -766,19 +766,19 @@ describe('UpdateCompressor', function () { meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: { p: 6, d: 'bar' }, meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) @@ -792,45 +792,45 @@ describe('UpdateCompressor', function () { { op: [ { p: 1000, d: 'hello' }, - { p: 1000, i: 'HELLO()' } + { p: 1000, i: 'HELLO()' }, ], meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, [ { op: [{ p: 1006, i: 'WORLD' }], meta: { ts: this.ts2, user_id: this.user_id }, - v: 43 - } + v: 43, + }, ] ) ).to.deep.equal([ { op: [ { p: 1000, d: 'hello' }, - { p: 1000, i: 'HELLO()' } + { p: 1000, i: 'HELLO()' }, ], meta: { start_ts: this.ts1, end_ts: this.ts1, - user_id: this.user_id + user_id: this.user_id, }, - v: 42 + v: 42, }, { op: [{ p: 1006, i: 'WORLD' }], meta: { start_ts: this.ts2, end_ts: this.ts2, - user_id: this.user_id + user_id: this.user_id, }, - v: 43 - } + v: 43, + }, ]) }) }) diff --git a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js index 2d89a3520d..a49554d9bb 100644 --- a/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js +++ b/services/track-changes/test/unit/js/UpdateTrimmer/UpdateTrimmerTests.js @@ -23,8 +23,8 @@ describe('UpdateTrimmer', function () { this.UpdateTrimmer = SandboxedModule.require(modulePath, { requires: { './WebApiManager': (this.WebApiManager = {}), - './MongoManager': (this.MongoManager = {}) - } + './MongoManager': (this.MongoManager = {}), + }, }) this.callback = sinon.stub() diff --git a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js index 68904e9471..74233e6ffa 100644 --- a/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js +++ b/services/track-changes/test/unit/js/UpdatesManager/UpdatesManagerTests.js @@ -38,12 +38,12 @@ describe('UpdatesManager', function () { key_schema: { historyLock({ doc_id }) { return `HistoryLock:${doc_id}` - } - } - } - } - } - } + }, + }, + }, + }, + }, + }, }) this.doc_id = 'doc-id-123' this.project_id = 'project-id-123' @@ -79,7 +79,7 @@ describe('UpdatesManager', function () { beforeEach(function () { this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, - { v: 13, op: 'mock-op-13' } + { v: 13, op: 'mock-op-13' }, ] this.compressedUpdates = [{ v: 13, op: 'compressed-op-12' }] @@ -127,7 +127,7 @@ describe('UpdatesManager', function () { this.lastCompressedUpdate = { v: 11, op: 'compressed-op-11' } this.compressedUpdates = [ { v: 12, op: 'compressed-op-11+12' }, - { v: 13, op: 'compressed-op-12' } + { v: 13, op: 'compressed-op-12' }, ] this.MongoManager.peekLastCompressedUpdate = sinon @@ -148,7 +148,7 @@ describe('UpdatesManager', function () { beforeEach(function () { this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, - { v: 13, op: 'mock-op-13' } + { v: 13, op: 'mock-op-13' }, ] return this.UpdatesManager.compressAndSaveRawUpdates( this.project_id, @@ -192,11 +192,11 @@ describe('UpdatesManager', function () { beforeEach(function () { this.lastCompressedUpdate = { pack: [{ v: 11, op: 'compressed-op-11' }], - v: 11 + v: 11, } this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, - { v: 13, op: 'mock-op-13' } + { v: 13, op: 'mock-op-13' }, ] this.MongoManager.peekLastCompressedUpdate = sinon .stub() @@ -250,7 +250,7 @@ describe('UpdatesManager', function () { { v: 10, op: 'mock-op-10' }, { v: 11, op: 'mock-op-11' }, { v: 12, op: 'mock-op-12' }, - { v: 13, op: 'mock-op-13' } + { v: 13, op: 'mock-op-13' }, ] return this.UpdatesManager.compressAndSaveRawUpdates( @@ -303,7 +303,7 @@ describe('UpdatesManager', function () { beforeEach(function () { this.rawUpdates = [ { v: 13, op: 'mock-op-13' }, - { v: 12, op: 'mock-op-12' } + { v: 12, op: 'mock-op-12' }, ] return this.UpdatesManager.compressAndSaveRawUpdates( this.project_id, @@ -347,7 +347,7 @@ describe('UpdatesManager', function () { beforeEach(function () { this.rawUpdates = [ { v: 12, op: 'mock-op-12' }, - { v: 13, op: 'mock-op-13' } + { v: 13, op: 'mock-op-13' }, ] return this.UpdatesManager.compressAndSaveRawUpdates( this.project_id, @@ -454,7 +454,7 @@ describe('UpdatesManager', function () { 'mock-update-1', 'mock-update-2', 'mock-update-3', - 'mock-update-4' + 'mock-update-4', ] this.redisArray = this.updates.slice() this.RedisManager.getOldestDocUpdates = ( @@ -673,7 +673,7 @@ describe('UpdatesManager', function () { }) it('should process the doc ops for the each doc_id', function () { - return Array.from(this.doc_ids).map((doc_id) => + return Array.from(this.doc_ids).map(doc_id => this.UpdatesManager._processUncompressedUpdatesForDocWithLock .calledWith(this.project_id, doc_id, this.temporary) .should.equal(true) @@ -692,26 +692,26 @@ describe('UpdatesManager', function () { doc_id: 123, v: 456, op: 'mock-updates', - meta: { user_id: 123, start_ts: 1233, end_ts: 1234 } - } + meta: { user_id: 123, start_ts: 1233, end_ts: 1234 }, + }, ] this.options = { before: 'mock-before', limit: 'mock-limit' } this.summarizedUpdates = [ { meta: { user_ids: [123], start_ts: 1233, end_ts: 1234 }, - docs: { '123': { fromV: 456, toV: 456 } } - } + docs: { 123: { fromV: 456, toV: 456 } }, + }, ] this.updatesWithUserInfo = ['updates-with-user-info'] this.done_state = false this.iterator = { - next: (cb) => { + next: cb => { this.done_state = true return cb(null, this.updates) }, done: () => { return this.done_state - } + }, } this.PackManager.makeProjectIterator = sinon .stub() @@ -867,22 +867,22 @@ describe('UpdatesManager', function () { this.updates = [ { meta: { - user_id: this.user_id_1 + user_id: this.user_id_1, }, - op: 'mock-op-1' + op: 'mock-op-1', }, { meta: { - user_id: this.user_id_1 + user_id: this.user_id_1, }, - op: 'mock-op-2' + op: 'mock-op-2', }, { meta: { - user_id: this.user_id_2 + user_id: this.user_id_2, }, - op: 'mock-op-3' - } + op: 'mock-op-3', + }, ] this.user_info = {} this.user_info[this.user_id_1] = { email: 'user1@sharelatex.com' } @@ -920,27 +920,27 @@ describe('UpdatesManager', function () { { meta: { user: { - email: 'user1@sharelatex.com' - } + email: 'user1@sharelatex.com', + }, }, - op: 'mock-op-1' + op: 'mock-op-1', }, { meta: { user: { - email: 'user1@sharelatex.com' - } + email: 'user1@sharelatex.com', + }, }, - op: 'mock-op-2' + op: 'mock-op-2', }, { meta: { user: { - email: 'user2@sharelatex.com' - } + email: 'user2@sharelatex.com', + }, }, - op: 'mock-op-3' - } + op: 'mock-op-3', + }, ]) }) }) @@ -950,16 +950,16 @@ describe('UpdatesManager', function () { this.updates = [ { meta: { - user_id: null + user_id: null, }, - op: 'mock-op-1' + op: 'mock-op-1', }, { meta: { - user_id: 'anonymous-user' + user_id: 'anonymous-user', }, - op: 'mock-op-2' - } + op: 'mock-op-2', + }, ] this.WebApiManager.getUserInfo = (user_id, callback) => { if (callback == null) { @@ -986,12 +986,12 @@ describe('UpdatesManager', function () { return expect(this.results).to.deep.equal([ { meta: {}, - op: 'mock-op-1' + op: 'mock-op-1', }, { meta: {}, - op: 'mock-op-2' - } + op: 'mock-op-2', + }, ]) }) }) @@ -1011,19 +1011,19 @@ describe('UpdatesManager', function () { meta: { user_id: this.user_1.id, start_ts: this.now + 20, - end_ts: this.now + 30 + end_ts: this.now + 30, }, - v: 5 + v: 5, }, { doc_id: 'doc-id-1', meta: { user_id: this.user_2.id, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 - } + v: 4, + }, ]) return expect(result).to.deep.equal([ @@ -1031,15 +1031,15 @@ describe('UpdatesManager', function () { docs: { 'doc-id-1': { fromV: 4, - toV: 5 - } + toV: 5, + }, }, meta: { user_ids: [this.user_1.id, this.user_2.id], start_ts: this.now, - end_ts: this.now + 30 - } - } + end_ts: this.now + 30, + }, + }, ]) }) @@ -1051,47 +1051,47 @@ describe('UpdatesManager', function () { meta: { user_id: this.user_2.id, start_ts: this.now + oneDay, - end_ts: this.now + oneDay + 10 + end_ts: this.now + oneDay + 10, }, - v: 5 + v: 5, }, { doc_id: 'doc-id-1', meta: { user_id: this.user_1.id, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 - } + v: 4, + }, ]) return expect(result).to.deep.equal([ { docs: { 'doc-id-1': { fromV: 5, - toV: 5 - } + toV: 5, + }, }, meta: { user_ids: [this.user_2.id], start_ts: this.now + oneDay, - end_ts: this.now + oneDay + 10 - } + end_ts: this.now + oneDay + 10, + }, }, { docs: { 'doc-id-1': { fromV: 4, - toV: 4 - } + toV: 4, + }, }, meta: { user_ids: [this.user_1.id], start_ts: this.now, - end_ts: this.now + 10 - } - } + end_ts: this.now + 10, + }, + }, ]) }) @@ -1103,34 +1103,34 @@ describe('UpdatesManager', function () { meta: { user_id: this.user_1.id, start_ts: this.now + 20, - end_ts: this.now + 30 + end_ts: this.now + 30, }, - v: 5 + v: 5, }, { doc_id: 'doc-id-2', meta: { user_id: this.user_2.id, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 - } + v: 4, + }, ], [ { docs: { 'doc-id-1': { fromV: 6, - toV: 8 - } + toV: 8, + }, }, meta: { user_ids: [this.user_1.id], start_ts: this.now + 40, - end_ts: this.now + 50 - } - } + end_ts: this.now + 50, + }, + }, ] ) return expect(result).to.deep.equal([ @@ -1138,19 +1138,19 @@ describe('UpdatesManager', function () { docs: { 'doc-id-1': { toV: 8, - fromV: 6 + fromV: 6, }, 'doc-id-2': { toV: 5, - fromV: 4 - } + fromV: 4, + }, }, meta: { user_ids: [this.user_1.id, this.user_2.id], start_ts: this.now, - end_ts: this.now + 50 - } - } + end_ts: this.now + 50, + }, + }, ]) }) @@ -1161,34 +1161,34 @@ describe('UpdatesManager', function () { meta: { user_id: this.user_1.id, start_ts: this.now + 20, - end_ts: this.now + 30 + end_ts: this.now + 30, }, - v: 5 + v: 5, }, { doc_id: 'doc-id-1', meta: { user_id: null, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 - } + v: 4, + }, ]) return expect(result).to.deep.equal([ { docs: { 'doc-id-1': { fromV: 4, - toV: 5 - } + toV: 5, + }, }, meta: { user_ids: [this.user_1.id, null], start_ts: this.now, - end_ts: this.now + 30 - } - } + end_ts: this.now + 30, + }, + }, ]) }) @@ -1199,34 +1199,34 @@ describe('UpdatesManager', function () { meta: { user_id: null, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 + v: 4, }, { doc_id: 'doc-id-1', meta: { user_id: this.user_1.id, start_ts: this.now + 20, - end_ts: this.now + 30 + end_ts: this.now + 30, }, - v: 5 - } + v: 5, + }, ]) return expect(result).to.deep.equal([ { docs: { 'doc-id-1': { fromV: 4, - toV: 5 - } + toV: 5, + }, }, meta: { user_ids: [null, this.user_1.id], start_ts: this.now, - end_ts: this.now + 30 - } - } + end_ts: this.now + 30, + }, + }, ]) }) @@ -1237,43 +1237,43 @@ describe('UpdatesManager', function () { meta: { user_id: this.user_1.id, start_ts: this.now + 20, - end_ts: this.now + 30 + end_ts: this.now + 30, }, - v: 5 + v: 5, }, { doc_id: 'doc-id-1', meta: { user_id: null, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 + v: 4, }, { doc_id: 'doc-id-1', meta: { user_id: null, start_ts: this.now + 2, - end_ts: this.now + 4 + end_ts: this.now + 4, }, - v: 4 - } + v: 4, + }, ]) return expect(result).to.deep.equal([ { docs: { 'doc-id-1': { fromV: 4, - toV: 5 - } + toV: 5, + }, }, meta: { user_ids: [this.user_1.id, null], start_ts: this.now, - end_ts: this.now + 30 - } - } + end_ts: this.now + 30, + }, + }, ]) }) @@ -1285,19 +1285,19 @@ describe('UpdatesManager', function () { meta: { user_id: this.user_1.id, start_ts: this.now + 20, - end_ts: this.now + 30 + end_ts: this.now + 30, }, - v: 5 + v: 5, }, { doc_id: 'doc-id-1', meta: { user_id: this.user_2.id, start_ts: this.now, - end_ts: this.now + 10 + end_ts: this.now + 10, }, - v: 4 - } + v: 4, + }, ]) return expect(result).to.deep.equal([ @@ -1305,28 +1305,28 @@ describe('UpdatesManager', function () { docs: { 'doc-id-1': { fromV: 5, - toV: 5 - } + toV: 5, + }, }, meta: { user_ids: [this.user_1.id], start_ts: this.now + 20, - end_ts: this.now + 30 - } + end_ts: this.now + 30, + }, }, { docs: { 'doc-id-1': { fromV: 4, - toV: 4 - } + toV: 4, + }, }, meta: { user_ids: [this.user_2.id], start_ts: this.now, - end_ts: this.now + 10 - } - } + end_ts: this.now + 10, + }, + }, ]) }) }) diff --git a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js index ce2f6f6890..9caba5185e 100644 --- a/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js +++ b/services/track-changes/test/unit/js/WebApiManager/WebApiManagerTests.js @@ -24,11 +24,11 @@ describe('WebApiManager', function () { web: { url: 'http://example.com', user: 'sharelatex', - pass: 'password' - } - } - }) - } + pass: 'password', + }, + }, + }), + }, }) this.callback = sinon.stub() this.user_id = 'mock-user-id' @@ -38,7 +38,7 @@ describe('WebApiManager', function () { id: this.user_id, first_name: 'Leo', last_nane: 'Lion', - extra_param: 'blah' + extra_param: 'blah', } return (this.project = { features: 'mock-features' }) }) @@ -60,8 +60,8 @@ describe('WebApiManager', function () { auth: { user: this.settings.apis.web.user, pass: this.settings.apis.web.pass, - sendImmediately: true - } + sendImmediately: true, + }, }) .should.equal(true) }) @@ -72,7 +72,7 @@ describe('WebApiManager', function () { id: this.user_id, email: this.user_info.email, first_name: this.user_info.first_name, - last_name: this.user_info.last_name + last_name: this.user_info.last_name, }) .should.equal(true) }) @@ -150,8 +150,8 @@ describe('WebApiManager', function () { auth: { user: this.settings.apis.web.user, pass: this.settings.apis.web.pass, - sendImmediately: true - } + sendImmediately: true, + }, }) .should.equal(true) }) From 181a895c04e1f4df2ac7f7b7becae7626c31344b Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 13 Jul 2021 12:21:08 +0100 Subject: [PATCH 548/549] [misc] temporary override a few new/changed eslint rules --- services/track-changes/.eslintrc | 15 ++++++++++++++- services/track-changes/app/js/HealthChecker.js | 1 - 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/services/track-changes/.eslintrc b/services/track-changes/.eslintrc index 1c14f50efe..a97661b15f 100644 --- a/services/track-changes/.eslintrc +++ b/services/track-changes/.eslintrc @@ -5,7 +5,7 @@ "extends": [ "eslint:recommended", "standard", - "prettier", + "prettier" ], "parserOptions": { "ecmaVersion": 2018 @@ -20,6 +20,19 @@ "mocha": true }, "rules": { + // TODO(das7pad): remove overrides after fixing all the violations manually (https://github.com/overleaf/issues/issues/3882#issuecomment-878999671) + // START of temporary overrides + "array-callback-return": "off", + "no-dupe-else-if": "off", + "no-var": "off", + "no-empty": "off", + "node/handle-callback-err": "off", + "no-loss-of-precision": "off", + "node/no-callback-literal": "off", + "node/no-path-concat": "off", + "prefer-regex-literals": "off", + // END of temporary overrides + // Swap the no-unused-expressions rule with a more chai-friendly one "no-unused-expressions": 0, "chai-friendly/no-unused-expressions": "error", diff --git a/services/track-changes/app/js/HealthChecker.js b/services/track-changes/app/js/HealthChecker.js index 94ba2371a8..978f586f6e 100644 --- a/services/track-changes/app/js/HealthChecker.js +++ b/services/track-changes/app/js/HealthChecker.js @@ -1,6 +1,5 @@ /* eslint-disable camelcase, - standard/no-callback-literal, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. From c2d44d16e36722a40e784dbfd32751e6e0790fef Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Tue, 13 Jul 2021 12:26:31 +0100 Subject: [PATCH 549/549] [misc] upgrade node version to latest v12 LTS version 12.22.3 --- services/track-changes/.nvmrc | 2 +- services/track-changes/Dockerfile | 2 +- services/track-changes/buildscript.txt | 2 +- services/track-changes/docker-compose.yml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/track-changes/.nvmrc b/services/track-changes/.nvmrc index e68b860383..5a80a7e912 100644 --- a/services/track-changes/.nvmrc +++ b/services/track-changes/.nvmrc @@ -1 +1 @@ -12.21.0 +12.22.3 diff --git a/services/track-changes/Dockerfile b/services/track-changes/Dockerfile index 4f417a2a4b..6b286376dc 100644 --- a/services/track-changes/Dockerfile +++ b/services/track-changes/Dockerfile @@ -2,7 +2,7 @@ # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -FROM node:12.21.0 as base +FROM node:12.22.3 as base WORKDIR /app diff --git a/services/track-changes/buildscript.txt b/services/track-changes/buildscript.txt index a1522ab57e..36ed3a783a 100644 --- a/services/track-changes/buildscript.txt +++ b/services/track-changes/buildscript.txt @@ -3,6 +3,6 @@ track-changes --docker-repos=gcr.io/overleaf-ops --env-add=AWS_BUCKET=bucket --env-pass-through= ---node-version=12.21.0 +--node-version=12.22.3 --public-repo=True --script-version=3.11.0 diff --git a/services/track-changes/docker-compose.yml b/services/track-changes/docker-compose.yml index 89df8581d7..c4bac32698 100644 --- a/services/track-changes/docker-compose.yml +++ b/services/track-changes/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.3" services: test_unit: - image: node:12.21.0 + image: node:12.22.3 volumes: - .:/app working_dir: /app @@ -18,7 +18,7 @@ services: user: node test_acceptance: - image: node:12.21.0 + image: node:12.22.3 volumes: - .:/app working_dir: /app