mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
decaffeinate: Convert AppendingUpdatesTests.coffee and 11 other files to JS
This commit is contained in:
parent
c97a2a3c07
commit
11c39cde65
12 changed files with 1331 additions and 952 deletions
|
@ -1,311 +1,387 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* DS207: Consider shorter variations of null checks
|
||||||
mongojs = require "../../../app/js/mongojs"
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
ObjectId = mongojs.ObjectId
|
*/
|
||||||
Settings = require "settings-sharelatex"
|
const sinon = require("sinon");
|
||||||
request = require "request"
|
const chai = require("chai");
|
||||||
rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now
|
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"
|
const TrackChangesApp = require("./helpers/TrackChangesApp");
|
||||||
TrackChangesClient = require "./helpers/TrackChangesClient"
|
const TrackChangesClient = require("./helpers/TrackChangesClient");
|
||||||
MockWebApi = require "./helpers/MockWebApi"
|
const MockWebApi = require("./helpers/MockWebApi");
|
||||||
|
|
||||||
describe "Appending doc ops to the history", ->
|
describe("Appending doc ops to the history", function() {
|
||||||
before (done)->
|
before(done=> TrackChangesApp.ensureRunning(done));
|
||||||
TrackChangesApp.ensureRunning done
|
|
||||||
|
|
||||||
describe "when the history does not exist yet", ->
|
describe("when the history does not exist yet", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "o", p: 4 }]
|
op: [{ i: "o", p: 4 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 4
|
v: 4
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "o", p: 5 }]
|
op: [{ i: "o", p: 5 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 5
|
v: 5
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the compressed op into mongo", ->
|
it("should insert the compressed op into mongo", function() {
|
||||||
expect(@updates[0].pack[0].op).to.deep.equal [{
|
return expect(this.updates[0].pack[0].op).to.deep.equal([{
|
||||||
p: 3, i: "foo"
|
p: 3, i: "foo"
|
||||||
}]
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the correct version number into mongo", ->
|
it("should insert the correct version number into mongo", function() {
|
||||||
expect(@updates[0].v).to.equal 5
|
return expect(this.updates[0].v).to.equal(5);
|
||||||
|
});
|
||||||
|
|
||||||
it "should store the doc id", ->
|
it("should store the doc id", function() {
|
||||||
expect(@updates[0].doc_id.toString()).to.equal @doc_id
|
return expect(this.updates[0].doc_id.toString()).to.equal(this.doc_id);
|
||||||
|
});
|
||||||
|
|
||||||
it "should store the project id", ->
|
it("should store the project id", function() {
|
||||||
expect(@updates[0].project_id.toString()).to.equal @project_id
|
return expect(this.updates[0].project_id.toString()).to.equal(this.project_id);
|
||||||
|
});
|
||||||
|
|
||||||
it "should clear the doc from the DocsWithHistoryOps set", (done) ->
|
return it("should clear the doc from the DocsWithHistoryOps set", function(done) {
|
||||||
rclient.sismember "DocsWithHistoryOps:#{@project_id}", @doc_id, (error, member) ->
|
rclient.sismember(`DocsWithHistoryOps:${this.project_id}`, this.doc_id, function(error, member) {
|
||||||
member.should.equal 0
|
member.should.equal(0);
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "when the history has already been started", ->
|
describe("when the history has already been started", function() {
|
||||||
beforeEach (done) ->
|
beforeEach(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "o", p: 4 }]
|
op: [{ i: "o", p: 4 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 4
|
v: 4
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "o", p: 5 }]
|
op: [{ i: "o", p: 5 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 5
|
v: 5
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
describe "when the updates are recent and from the same user", ->
|
describe("when the updates are recent and from the same user", function() {
|
||||||
beforeEach (done) ->
|
beforeEach(function(done) {
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "b", p: 6 }]
|
op: [{ i: "b", p: 6 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 6
|
v: 6
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "a", p: 7 }]
|
op: [{ i: "a", p: 7 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 7
|
v: 7
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "r", p: 8 }]
|
op: [{ i: "r", p: 8 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 8
|
v: 8
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should combine all the updates into one pack", ->
|
it("should combine all the updates into one pack", function() {
|
||||||
expect(@updates[0].pack[1].op).to.deep.equal [{
|
return expect(this.updates[0].pack[1].op).to.deep.equal([{
|
||||||
p: 6, i: "bar"
|
p: 6, i: "bar"
|
||||||
}]
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the correct version number into mongo", ->
|
return it("should insert the correct version number into mongo", function() {
|
||||||
expect(@updates[0].v_end).to.equal 8
|
return expect(this.updates[0].v_end).to.equal(8);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe "when the updates are far apart", ->
|
return describe("when the updates are far apart", function() {
|
||||||
beforeEach (done) ->
|
beforeEach(function(done) {
|
||||||
oneDay = 24 * 60 * 60 * 1000
|
const oneDay = 24 * 60 * 60 * 1000;
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "b", p: 6 }]
|
op: [{ i: "b", p: 6 }],
|
||||||
meta: { ts: Date.now() + oneDay, user_id: @user_id }
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
||||||
v: 6
|
v: 6
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "a", p: 7 }]
|
op: [{ i: "a", p: 7 }],
|
||||||
meta: { ts: Date.now() + oneDay, user_id: @user_id }
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
||||||
v: 7
|
v: 7
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "r", p: 8 }]
|
op: [{ i: "r", p: 8 }],
|
||||||
meta: { ts: Date.now() + oneDay, user_id: @user_id }
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
||||||
v: 8
|
v: 8
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should combine the updates into one pack", ->
|
return it("should combine the updates into one pack", function() {
|
||||||
expect(@updates[0].pack[0].op).to.deep.equal [{
|
expect(this.updates[0].pack[0].op).to.deep.equal([{
|
||||||
p: 3, i: "foo"
|
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"
|
p: 6, i: "bar"
|
||||||
}]
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "when the updates need processing in batches", ->
|
describe("when the updates need processing in batches", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
updates = []
|
const updates = [];
|
||||||
@expectedOp = [{ p:0, i: "" }]
|
this.expectedOp = [{ p:0, i: "" }];
|
||||||
for i in [0..250]
|
for (let i = 0; i <= 250; i++) {
|
||||||
updates.push {
|
updates.push({
|
||||||
op: [{i: "a", p: 0}]
|
op: [{i: "a", p: 0}],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: i
|
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) =>
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, updates, error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates1) => {
|
||||||
throw error if error?
|
this.updates = updates1;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should concat the compressed op into mongo", ->
|
it("should concat the compressed op into mongo", function() {
|
||||||
expect(@updates[0].pack.length).to.deep.equal 3 # batch size is 100
|
return expect(this.updates[0].pack.length).to.deep.equal(3);
|
||||||
|
}); // batch size is 100
|
||||||
|
|
||||||
it "should insert the correct version number into mongo", ->
|
return it("should insert the correct version number into mongo", function() {
|
||||||
expect(@updates[0].v_end).to.equal 250
|
return expect(this.updates[0].v_end).to.equal(250);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe "when there are multiple ops in each update", ->
|
describe("when there are multiple ops in each update", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
oneDay = 24 * 60 * 60 * 1000
|
const oneDay = 24 * 60 * 60 * 1000;
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }]
|
op: [{ i: "f", p: 3 }, { i: "o", p: 4 }, { i: "o", p: 5 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
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 }]
|
op: [{ i: "b", p: 6 }, { i: "a", p: 7 }, { i: "r", p: 8 }],
|
||||||
meta: { ts: Date.now() + oneDay, user_id: @user_id }
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
||||||
v: 4
|
v: 4
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the compressed ops into mongo", ->
|
it("should insert the compressed ops into mongo", function() {
|
||||||
expect(@updates[0].pack[0].op).to.deep.equal [{
|
expect(this.updates[0].pack[0].op).to.deep.equal([{
|
||||||
p: 3, i: "foo"
|
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"
|
p: 6, i: "bar"
|
||||||
}]
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the correct version numbers into mongo", ->
|
return it("should insert the correct version numbers into mongo", function() {
|
||||||
expect(@updates[0].pack[0].v).to.equal 3
|
expect(this.updates[0].pack[0].v).to.equal(3);
|
||||||
expect(@updates[0].pack[1].v).to.equal 4
|
return expect(this.updates[0].pack[1].v).to.equal(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "when there is a no-op update", ->
|
describe("when there is a no-op update", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
oneDay = 24 * 60 * 60 * 1000
|
const oneDay = 24 * 60 * 60 * 1000;
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: []
|
op: [],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "foo", p: 3 }]
|
op: [{ i: "foo", p: 3 }],
|
||||||
meta: { ts: Date.now() + oneDay, user_id: @user_id }
|
meta: { ts: Date.now() + oneDay, user_id: this.user_id },
|
||||||
v: 4
|
v: 4
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the compressed no-op into mongo", ->
|
it("should insert the compressed no-op into mongo", function() {
|
||||||
expect(@updates[0].pack[0].op).to.deep.equal []
|
return expect(this.updates[0].pack[0].op).to.deep.equal([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it "should insert the compressed next update into mongo", ->
|
it("should insert the compressed next update into mongo", function() {
|
||||||
expect(@updates[0].pack[1].op).to.deep.equal [{
|
return expect(this.updates[0].pack[1].op).to.deep.equal([{
|
||||||
p: 3, i: "foo"
|
p: 3, i: "foo"
|
||||||
}]
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the correct version numbers into mongo", ->
|
return it("should insert the correct version numbers into mongo", function() {
|
||||||
expect(@updates[0].pack[0].v).to.equal 3
|
expect(this.updates[0].pack[0].v).to.equal(3);
|
||||||
expect(@updates[0].pack[1].v).to.equal 4
|
return expect(this.updates[0].pack[1].v).to.equal(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "when there is a comment update", ->
|
describe("when there is a comment update", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ c: "foo", p: 3 }, {d: "bar", p: 6}]
|
op: [{ c: "foo", p: 3 }, {d: "bar", p: 6}],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should ignore the comment op", ->
|
it("should ignore the comment op", function() {
|
||||||
expect(@updates[0].pack[0].op).to.deep.equal [{d: "bar", p: 6}]
|
return expect(this.updates[0].pack[0].op).to.deep.equal([{d: "bar", p: 6}]);
|
||||||
|
});
|
||||||
|
|
||||||
it "should insert the correct version numbers into mongo", ->
|
return it("should insert the correct version numbers into mongo", function() {
|
||||||
expect(@updates[0].pack[0].v).to.equal 3
|
return expect(this.updates[0].pack[0].v).to.equal(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "when the project has versioning enabled", ->
|
describe("when the project has versioning enabled", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: true
|
MockWebApi.projects[this.project_id] = {features: {versioning: true}};
|
||||||
|
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should not add a expiresAt entry in the update in mongo", ->
|
return it("should not add a expiresAt entry in the update in mongo", function() {
|
||||||
expect(@updates[0].expiresAt).to.be.undefined
|
return expect(this.updates[0].expiresAt).to.be.undefined;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "when the project does not have versioning enabled", ->
|
return describe("when the project does not have versioning enabled", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: false
|
MockWebApi.projects[this.project_id] = {features: {versioning: false}};
|
||||||
|
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushAndGetCompressedUpdates @project_id, @doc_id, (error, @updates) =>
|
return TrackChangesClient.flushAndGetCompressedUpdates(this.project_id, this.doc_id, (error, updates) => {
|
||||||
throw error if error?
|
this.updates = updates;
|
||||||
done()
|
if (error != null) { throw error; }
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should add a expiresAt entry in the update in mongo", ->
|
return it("should add a expiresAt entry in the update in mongo", function() {
|
||||||
expect(@updates[0].expiresAt).to.exist
|
return expect(this.updates[0].expiresAt).to.exist;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,134 +1,181 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* DS103: Rewrite code to no longer use __guard__
|
||||||
mongojs = require "../../../app/js/mongojs"
|
* DS202: Simplify dynamic range loops
|
||||||
db = mongojs.db
|
* DS207: Consider shorter variations of null checks
|
||||||
ObjectId = mongojs.ObjectId
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
Settings = require "settings-sharelatex"
|
*/
|
||||||
request = require "request"
|
const sinon = require("sinon");
|
||||||
rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now
|
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"
|
const TrackChangesApp = require("./helpers/TrackChangesApp");
|
||||||
TrackChangesClient = require "./helpers/TrackChangesClient"
|
const TrackChangesClient = require("./helpers/TrackChangesClient");
|
||||||
MockDocStoreApi = require "./helpers/MockDocStoreApi"
|
const MockDocStoreApi = require("./helpers/MockDocStoreApi");
|
||||||
MockWebApi = require "./helpers/MockWebApi"
|
const MockWebApi = require("./helpers/MockWebApi");
|
||||||
|
|
||||||
describe "Archiving updates", ->
|
describe("Archiving updates", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
if Settings?.trackchanges?.s3?.key.length < 1
|
if (__guard__(__guard__(Settings != null ? Settings.trackchanges : undefined, x1 => x1.s3), x => x.key.length) < 1) {
|
||||||
message = new Error("s3 keys not setup, this test setup will fail")
|
const message = new Error("s3 keys not setup, this test setup will fail");
|
||||||
return done(message)
|
return done(message);
|
||||||
|
}
|
||||||
|
|
||||||
TrackChangesClient.waitForS3 done
|
return TrackChangesClient.waitForS3(done);
|
||||||
|
});
|
||||||
|
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@now = Date.now()
|
this.now = Date.now();
|
||||||
@to = @now
|
this.to = this.now;
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
|
|
||||||
@minutes = 60 * 1000
|
this.minutes = 60 * 1000;
|
||||||
@hours = 60 * @minutes
|
this.hours = 60 * this.minutes;
|
||||||
|
|
||||||
MockWebApi.projects[@project_id] =
|
MockWebApi.projects[this.project_id] = {
|
||||||
features:
|
features: {
|
||||||
versioning: true
|
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 }]
|
sinon.spy(MockWebApi, "getProjectDetails");
|
||||||
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
|
|
||||||
|
|
||||||
after (done) ->
|
MockWebApi.users[this.user_id] = (this.user = {
|
||||||
MockWebApi.getUserInfo.restore()
|
email: "user@sharelatex.com",
|
||||||
db.docHistory.remove {project_id: ObjectId(@project_id)}, () =>
|
first_name: "Leo",
|
||||||
db.docHistoryIndex.remove {project_id: ObjectId(@project_id)}, () =>
|
last_name: "Lion",
|
||||||
TrackChangesClient.removeS3Doc @project_id, @doc_id, done
|
id: this.user_id
|
||||||
|
});
|
||||||
|
sinon.spy(MockWebApi, "getUserInfo");
|
||||||
|
|
||||||
describe "archiving a doc's updates", ->
|
MockDocStoreApi.docs[this.doc_id] = (this.doc = {
|
||||||
before (done) ->
|
_id: this.doc_id,
|
||||||
TrackChangesClient.pushDocHistory @project_id, @doc_id, (error) ->
|
project_id: this.project_id
|
||||||
throw error if error?
|
});
|
||||||
done()
|
sinon.spy(MockDocStoreApi, "getAllDoc");
|
||||||
return null
|
|
||||||
|
|
||||||
it "should have one cached pack", (done) ->
|
this.updates = [];
|
||||||
db.docHistory.count { doc_id: ObjectId(@doc_id), expiresAt:{$exists:true}}, (error, count) ->
|
for (let i = 0, end = 512+10, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) {
|
||||||
throw error if error?
|
this.updates.push({
|
||||||
count.should.equal 1
|
op: [{ i: "a", p: 0 }],
|
||||||
done()
|
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) ->
|
after(function(done) {
|
||||||
db.docHistory.remove {
|
MockWebApi.getUserInfo.restore();
|
||||||
doc_id: ObjectId(@doc_id),
|
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}
|
expiresAt:{$exists:true}
|
||||||
}, (err, result) =>
|
}, (err, result) => {
|
||||||
throw error if error?
|
if (typeof error !== 'undefined' && error !== null) { throw error; }
|
||||||
db.docHistory.count { doc_id: ObjectId(@doc_id)}, (error, count) ->
|
return db.docHistory.count({ doc_id: ObjectId(this.doc_id)}, function(error, count) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
count.should.equal 1
|
count.should.equal(1);
|
||||||
done()
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it "should have a docHistoryIndex entry marked as inS3", (done) ->
|
it("should have a docHistoryIndex entry marked as inS3", function(done) {
|
||||||
db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) ->
|
return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, function(error, index) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
index.packs[0].inS3.should.equal true
|
index.packs[0].inS3.should.equal(true);
|
||||||
done()
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it "should have a docHistoryIndex entry with the last version", (done) ->
|
it("should have a docHistoryIndex entry with the last version", function(done) {
|
||||||
db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) ->
|
return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, function(error, index) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
index.packs[0].v_end.should.equal 1024
|
index.packs[0].v_end.should.equal(1024);
|
||||||
done()
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it "should store 1024 doc changes in S3 in one pack", (done) ->
|
return it("should store 1024 doc changes in S3 in one pack", function(done) {
|
||||||
db.docHistoryIndex.findOne { _id: ObjectId(@doc_id) }, (error, index) =>
|
return db.docHistoryIndex.findOne({ _id: ObjectId(this.doc_id) }, (error, index) => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
pack_id = index.packs[0]._id
|
const pack_id = index.packs[0]._id;
|
||||||
TrackChangesClient.getS3Doc @project_id, @doc_id, pack_id, (error, doc) =>
|
return TrackChangesClient.getS3Doc(this.project_id, this.doc_id, pack_id, (error, doc) => {
|
||||||
doc.n.should.equal 1024
|
doc.n.should.equal(1024);
|
||||||
doc.pack.length.should.equal 1024
|
doc.pack.length.should.equal(1024);
|
||||||
done()
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "unarchiving a doc's updates", ->
|
return describe("unarchiving a doc's updates", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
TrackChangesClient.pullDocHistory @project_id, @doc_id, (error) ->
|
TrackChangesClient.pullDocHistory(this.project_id, this.doc_id, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should restore both packs", (done) ->
|
return it("should restore both packs", function(done) {
|
||||||
db.docHistory.count { doc_id: ObjectId(@doc_id) }, (error, count) ->
|
return db.docHistory.count({ doc_id: ObjectId(this.doc_id) }, function(error, count) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
count.should.equal 2
|
count.should.equal(2);
|
||||||
done()
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function __guard__(value, transform) {
|
||||||
|
return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
|
||||||
|
}
|
|
@ -1,151 +1,191 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* DS207: Consider shorter variations of null checks
|
||||||
mongojs = require "../../../app/js/mongojs"
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
ObjectId = mongojs.ObjectId
|
*/
|
||||||
Settings = require "settings-sharelatex"
|
const sinon = require("sinon");
|
||||||
request = require "request"
|
const chai = require("chai");
|
||||||
rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now
|
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"
|
const TrackChangesApp = require("./helpers/TrackChangesApp");
|
||||||
TrackChangesClient = require "./helpers/TrackChangesClient"
|
const TrackChangesClient = require("./helpers/TrackChangesClient");
|
||||||
MockWebApi = require "./helpers/MockWebApi"
|
const MockWebApi = require("./helpers/MockWebApi");
|
||||||
|
|
||||||
describe "Flushing updates", ->
|
describe("Flushing updates", function() {
|
||||||
before (done)->
|
before(done=> TrackChangesApp.ensureRunning(done));
|
||||||
TrackChangesApp.ensureRunning done
|
|
||||||
|
|
||||||
describe "flushing a doc's updates", ->
|
describe("flushing a doc's updates", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: true
|
MockWebApi.projects[this.project_id] = {features: {versioning: true}};
|
||||||
|
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushDoc @project_id, @doc_id, (error) ->
|
return TrackChangesClient.flushDoc(this.project_id, this.doc_id, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should flush the op into mongo", (done) ->
|
return it("should flush the op into mongo", function(done) {
|
||||||
TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) ->
|
TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) {
|
||||||
expect(updates[0].pack[0].op).to.deep.equal [{
|
expect(updates[0].pack[0].op).to.deep.equal([{
|
||||||
p: 3, i: "f"
|
p: 3, i: "f"
|
||||||
}]
|
}]);
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "flushing a project's updates", ->
|
return describe("flushing a project's updates", function() {
|
||||||
describe "with versioning enabled", ->
|
describe("with versioning enabled", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_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] =
|
MockWebApi.projects[this.project_id] = {
|
||||||
features:
|
features: {
|
||||||
versioning: true
|
versioning: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "g", p: 2 }]
|
op: [{ i: "g", p: 2 }],
|
||||||
meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id }
|
meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id },
|
||||||
v: 2
|
v: 2
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushProject @project_id, (error) ->
|
return TrackChangesClient.flushProject(this.project_id, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should not mark the updates for deletion", (done) ->
|
it("should not mark the updates for deletion", function(done) {
|
||||||
TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) ->
|
TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) {
|
||||||
expect(updates[0].expiresAt).to.not.exist
|
expect(updates[0].expiresAt).to.not.exist;
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should preserve history forever", (done) ->
|
return it("should preserve history forever", function(done) {
|
||||||
TrackChangesClient.getProjectMetaData @project_id, (error, project) ->
|
TrackChangesClient.getProjectMetaData(this.project_id, function(error, project) {
|
||||||
expect(project.preserveHistory).to.equal true
|
expect(project.preserveHistory).to.equal(true);
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "without versioning enabled", ->
|
describe("without versioning enabled", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_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] =
|
MockWebApi.projects[this.project_id] = {
|
||||||
features:
|
features: {
|
||||||
versioning: false
|
versioning: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "g", p: 2 }]
|
op: [{ i: "g", p: 2 }],
|
||||||
meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id }
|
meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id },
|
||||||
v: 2
|
v: 2
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushProject @project_id, (error) ->
|
return TrackChangesClient.flushProject(this.project_id, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should mark the updates for deletion", (done) ->
|
return it("should mark the updates for deletion", function(done) {
|
||||||
TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) ->
|
TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) {
|
||||||
expect(updates[0].expiresAt).to.exist
|
expect(updates[0].expiresAt).to.exist;
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "without versioning enabled but with preserveHistory set to true", ->
|
return describe("without versioning enabled but with preserveHistory set to true", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@user_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] =
|
MockWebApi.projects[this.project_id] = {
|
||||||
features:
|
features: {
|
||||||
versioning: false
|
versioning: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TrackChangesClient.setPreserveHistoryForProject @project_id, (error) =>
|
TrackChangesClient.setPreserveHistoryForProject(this.project_id, error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, [{
|
return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, [{
|
||||||
op: [{ i: "g", p: 2 }]
|
op: [{ i: "g", p: 2 }],
|
||||||
meta: { ts: Date.now() - 2 * @weeks, user_id: @user_id }
|
meta: { ts: Date.now() - (2 * this.weeks), user_id: this.user_id },
|
||||||
v: 2
|
v: 2
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "f", p: 3 }]
|
op: [{ i: "f", p: 3 }],
|
||||||
meta: { ts: Date.now(), user_id: @user_id }
|
meta: { ts: Date.now(), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}], (error) =>
|
}], error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.flushProject @project_id, (error) ->
|
return TrackChangesClient.flushProject(this.project_id, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should not mark the updates for deletion", (done) ->
|
return it("should not mark the updates for deletion", function(done) {
|
||||||
TrackChangesClient.getCompressedUpdates @doc_id, (error, updates) ->
|
TrackChangesClient.getCompressedUpdates(this.doc_id, function(error, updates) {
|
||||||
expect(updates[0].expiresAt).to.not.exist
|
expect(updates[0].expiresAt).to.not.exist;
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,84 +1,100 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* DS207: Consider shorter variations of null checks
|
||||||
mongojs = require "../../../app/js/mongojs"
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
db = mongojs.db
|
*/
|
||||||
ObjectId = mongojs.ObjectId
|
const sinon = require("sinon");
|
||||||
Settings = require "settings-sharelatex"
|
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"
|
const TrackChangesApp = require("./helpers/TrackChangesApp");
|
||||||
TrackChangesClient = require "./helpers/TrackChangesClient"
|
const TrackChangesClient = require("./helpers/TrackChangesClient");
|
||||||
MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi"
|
const MockDocUpdaterApi = require("./helpers/MockDocUpdaterApi");
|
||||||
MockWebApi = require "./helpers/MockWebApi"
|
const MockWebApi = require("./helpers/MockWebApi");
|
||||||
|
|
||||||
describe "Getting a diff", ->
|
describe("Getting a diff", function() {
|
||||||
|
|
||||||
beforeEach (done) ->
|
beforeEach(function(done) {
|
||||||
sinon.spy MockDocUpdaterApi, "getDoc"
|
sinon.spy(MockDocUpdaterApi, "getDoc");
|
||||||
|
|
||||||
@now = Date.now()
|
this.now = Date.now();
|
||||||
@from = @now - 100000000
|
this.from = this.now - 100000000;
|
||||||
@to = @now
|
this.to = this.now;
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: true
|
MockWebApi.projects[this.project_id] = {features: {versioning: true}};
|
||||||
|
|
||||||
MockWebApi.users[@user_id] = @user =
|
MockWebApi.users[this.user_id] = (this.user = {
|
||||||
email: "user@sharelatex.com"
|
email: "user@sharelatex.com",
|
||||||
first_name: "Leo"
|
first_name: "Leo",
|
||||||
last_name: "Lion"
|
last_name: "Lion",
|
||||||
id: @user_id
|
id: this.user_id
|
||||||
sinon.spy MockWebApi, "getUserInfo"
|
});
|
||||||
|
sinon.spy(MockWebApi, "getUserInfo");
|
||||||
|
|
||||||
twoMinutes = 2 * 60 * 1000
|
const twoMinutes = 2 * 60 * 1000;
|
||||||
|
|
||||||
@updates = [{
|
this.updates = [{
|
||||||
op: [{ i: "one ", p: 0 }]
|
op: [{ i: "one ", p: 0 }],
|
||||||
meta: { ts: @from - twoMinutes, user_id: @user_id }
|
meta: { ts: this.from - twoMinutes, user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "two ", p: 4 }]
|
op: [{ i: "two ", p: 4 }],
|
||||||
meta: { ts: @from + twoMinutes, user_id: @user_id }
|
meta: { ts: this.from + twoMinutes, user_id: this.user_id },
|
||||||
v: @fromVersion = 4
|
v: (this.fromVersion = 4)
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "three ", p: 8 }]
|
op: [{ i: "three ", p: 8 }],
|
||||||
meta: { ts: @to - twoMinutes, user_id: @user_id }
|
meta: { ts: this.to - twoMinutes, user_id: this.user_id },
|
||||||
v: @toVersion = 5
|
v: (this.toVersion = 5)
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "four", p: 14 }]
|
op: [{ i: "four", p: 14 }],
|
||||||
meta: { ts: @to + twoMinutes, user_id: @user_id }
|
meta: { ts: this.to + twoMinutes, user_id: this.user_id },
|
||||||
v: 6
|
v: 6
|
||||||
}]
|
}];
|
||||||
@lines = ["one two three four"]
|
this.lines = ["one two three four"];
|
||||||
@expected_diff = [
|
this.expected_diff = [
|
||||||
{ u: "one " }
|
{ u: "one " },
|
||||||
{ i: "two three ", meta: { start_ts: @from + twoMinutes, end_ts: @to - twoMinutes, user: @user } }
|
{ i: "two three ", meta: { start_ts: this.from + twoMinutes, end_ts: this.to - twoMinutes, user: this.user } }
|
||||||
]
|
];
|
||||||
|
|
||||||
MockDocUpdaterApi.docs[@doc_id] =
|
MockDocUpdaterApi.docs[this.doc_id] = {
|
||||||
lines: @lines
|
lines: this.lines,
|
||||||
version: 7
|
version: 7
|
||||||
TrackChangesApp.ensureRunning =>
|
};
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) =>
|
TrackChangesApp.ensureRunning(() => {
|
||||||
throw error if error?
|
return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => {
|
||||||
TrackChangesClient.getDiff @project_id, @doc_id, @fromVersion, @toVersion, (error, diff) =>
|
if (error != null) { throw error; }
|
||||||
throw error if error?
|
return TrackChangesClient.getDiff(this.project_id, this.doc_id, this.fromVersion, this.toVersion, (error, diff) => {
|
||||||
@diff = diff.diff
|
if (error != null) { throw error; }
|
||||||
done()
|
this.diff = diff.diff;
|
||||||
return null
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
afterEach () ->
|
afterEach(function() {
|
||||||
MockDocUpdaterApi.getDoc.restore()
|
MockDocUpdaterApi.getDoc.restore();
|
||||||
MockWebApi.getUserInfo.restore()
|
MockWebApi.getUserInfo.restore();
|
||||||
return null
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should return the diff", ->
|
it("should return the diff", function() {
|
||||||
expect(@diff).to.deep.equal @expected_diff
|
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
|
MockDocUpdaterApi.getDoc
|
||||||
.calledWith(@project_id, @doc_id)
|
.calledWith(this.project_id, this.doc_id)
|
||||||
.should.equal true
|
.should.equal(true);
|
||||||
return null
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,126 +1,157 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* DS207: Consider shorter variations of null checks
|
||||||
mongojs = require "../../../app/js/mongojs"
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
db = mongojs.db
|
*/
|
||||||
ObjectId = mongojs.ObjectId
|
const sinon = require("sinon");
|
||||||
Settings = require "settings-sharelatex"
|
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"
|
const TrackChangesApp = require("./helpers/TrackChangesApp");
|
||||||
TrackChangesClient = require "./helpers/TrackChangesClient"
|
const TrackChangesClient = require("./helpers/TrackChangesClient");
|
||||||
MockWebApi = require "./helpers/MockWebApi"
|
const MockWebApi = require("./helpers/MockWebApi");
|
||||||
|
|
||||||
describe "Getting updates", ->
|
describe("Getting updates", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
@now = Date.now()
|
this.now = Date.now();
|
||||||
@to = @now
|
this.to = this.now;
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
@deleted_user_id = 'deleted_user'
|
this.deleted_user_id = 'deleted_user';
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
|
|
||||||
@minutes = 60 * 1000
|
this.minutes = 60 * 1000;
|
||||||
@hours = 60 * @minutes
|
this.hours = 60 * this.minutes;
|
||||||
|
|
||||||
MockWebApi.projects[@project_id] =
|
MockWebApi.projects[this.project_id] = {
|
||||||
features:
|
features: {
|
||||||
versioning: true
|
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 =>
|
MockWebApi.users[this.user_id] = (this.user = {
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) =>
|
email: "user@sharelatex.com",
|
||||||
throw error if error?
|
first_name: "Leo",
|
||||||
done()
|
last_name: "Lion",
|
||||||
return null
|
id: this.user_id
|
||||||
|
});
|
||||||
|
sinon.spy(MockWebApi, "getUserInfo");
|
||||||
|
|
||||||
after: () ->
|
this.updates = [];
|
||||||
MockWebApi.getUserInfo.restore()
|
for (let i = 0; i <= 9; i++) {
|
||||||
return null
|
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", ->
|
TrackChangesApp.ensureRunning(() => {
|
||||||
before (done) ->
|
return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => {
|
||||||
TrackChangesClient.getUpdates @project_id, { before: @to + 1, min_count: 3 }, (error, body) =>
|
if (error != null) { throw error; }
|
||||||
throw error if error?
|
return done();
|
||||||
@updates = body.updates
|
});
|
||||||
done()
|
});
|
||||||
return null
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should fetch the user details from the web api", ->
|
({
|
||||||
MockWebApi.getUserInfo
|
after() {
|
||||||
.calledWith(@user_id)
|
MockWebApi.getUserInfo.restore();
|
||||||
.should.equal true
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it "should return at least the min_count number of summarized updates", ->
|
describe("getting updates up to the limit", function() {
|
||||||
docs1 = {}
|
before(function(done) {
|
||||||
docs1[@doc_id] = toV: 20, fromV: 19
|
TrackChangesClient.getUpdates(this.project_id, { before: this.to + 1, min_count: 3 }, (error, body) => {
|
||||||
docs2 = {}
|
if (error != null) { throw error; }
|
||||||
docs2[@doc_id] = toV: 18, fromV: 17
|
this.updates = body.updates;
|
||||||
docs3 = {}
|
return done();
|
||||||
docs3[@doc_id] = toV: 16, fromV: 15
|
});
|
||||||
expect(@updates.slice(0,3)).to.deep.equal [{
|
return null;
|
||||||
docs: docs1
|
});
|
||||||
meta:
|
|
||||||
start_ts: @to - 2 * @minutes
|
it("should fetch the user details from the web api", function() {
|
||||||
end_ts: @to
|
return MockWebApi.getUserInfo
|
||||||
users: [@user]
|
.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
|
docs: docs2,
|
||||||
meta:
|
meta: {
|
||||||
start_ts: @to - 1 * @hours - 2 * @minutes
|
start_ts: this.to - (1 * this.hours) - (2 * this.minutes),
|
||||||
end_ts: @to - 1 * @hours
|
end_ts: this.to - (1 * this.hours),
|
||||||
users: [@user]
|
users: [this.user]
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
docs: docs3
|
docs: docs3,
|
||||||
meta:
|
meta: {
|
||||||
start_ts: @to - 2 * @hours - 2 * @minutes
|
start_ts: this.to - (2 * this.hours) - (2 * this.minutes),
|
||||||
end_ts: @to - 2 * @hours
|
end_ts: this.to - (2 * this.hours),
|
||||||
users: [@user]
|
users: [this.user]
|
||||||
}]
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe "getting updates beyond the end of the database", ->
|
return describe("getting updates beyond the end of the database", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
TrackChangesClient.getUpdates @project_id, { before: @to - 8 * @hours + 1, min_count: 30 }, (error, body) =>
|
TrackChangesClient.getUpdates(this.project_id, { before: (this.to - (8 * this.hours)) + 1, min_count: 30 }, (error, body) => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
@updates = body.updates
|
this.updates = body.updates;
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should return as many updates as it can", ->
|
return it("should return as many updates as it can", function() {
|
||||||
docs1 = {}
|
const docs1 = {};
|
||||||
docs1[@doc_id] = toV: 4, fromV: 3
|
docs1[this.doc_id] = {toV: 4, fromV: 3};
|
||||||
docs2 = {}
|
const docs2 = {};
|
||||||
docs2[@doc_id] = toV: 2, fromV: 1
|
docs2[this.doc_id] = {toV: 2, fromV: 1};
|
||||||
expect(@updates).to.deep.equal [{
|
return expect(this.updates).to.deep.equal([{
|
||||||
docs: docs1
|
docs: docs1,
|
||||||
meta:
|
meta: {
|
||||||
start_ts: @to - 8 * @hours - 2 * @minutes
|
start_ts: this.to - (8 * this.hours) - (2 * this.minutes),
|
||||||
end_ts: @to - 8 * @hours
|
end_ts: this.to - (8 * this.hours),
|
||||||
users: [@user]
|
users: [this.user]
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
docs: docs2
|
docs: docs2,
|
||||||
meta:
|
meta: {
|
||||||
start_ts: @to - 9 * @hours - 2 * @minutes
|
start_ts: this.to - (9 * this.hours) - (2 * this.minutes),
|
||||||
end_ts: @to - 9 * @hours
|
end_ts: this.to - (9 * this.hours),
|
||||||
users: [@user, null]
|
users: [this.user, null]
|
||||||
}]
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,39 +1,54 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
mongojs = require "../../../app/js/mongojs"
|
*/
|
||||||
ObjectId = mongojs.ObjectId
|
const sinon = require("sinon");
|
||||||
Settings = require "settings-sharelatex"
|
const chai = require("chai");
|
||||||
LockManager = require "../../../app/js/LockManager"
|
chai.should();
|
||||||
rclient = require("redis").createClient(Settings.redis.history) # Only works locally for now
|
const { expect } = chai;
|
||||||
TrackChangesApp = require "./helpers/TrackChangesApp"
|
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)->
|
before(function(done){
|
||||||
TrackChangesApp.ensureRunning done
|
TrackChangesApp.ensureRunning(done);
|
||||||
return null
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
describe "when the lock has expired in redis", ->
|
return describe("when the lock has expired in redis", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
LockManager.LOCK_TTL = 1 # second
|
LockManager.LOCK_TTL = 1; // second
|
||||||
LockManager.runWithLock "doc123", (releaseA) =>
|
LockManager.runWithLock("doc123", releaseA => {
|
||||||
# we create a lock A and allow it to expire in redis
|
// we create a lock A and allow it to expire in redis
|
||||||
setTimeout () ->
|
return setTimeout(() =>
|
||||||
# now we create a new lock B and try to release A
|
// now we create a new lock B and try to release A
|
||||||
LockManager.runWithLock "doc123", (releaseB) =>
|
LockManager.runWithLock("doc123", releaseB => {
|
||||||
releaseA() # try to release lock A to see if it wipes out lock B
|
return releaseA();
|
||||||
, (error) ->
|
} // try to release lock A to see if it wipes out lock B
|
||||||
# we never release lock B so nothing should happen here
|
, function(error) {})
|
||||||
, 1500 # enough time to wait until the lock has expired
|
|
||||||
, (error) ->
|
// we never release lock B so nothing should happen here
|
||||||
# we get here after trying to release lock A
|
, 1500);
|
||||||
|
} // enough time to wait until the lock has expired
|
||||||
|
, error =>
|
||||||
|
// we get here after trying to release lock A
|
||||||
done()
|
done()
|
||||||
return null
|
);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "the new lock should not be removed by the expired locker", (done) ->
|
return it("the new lock should not be removed by the expired locker", function(done) {
|
||||||
LockManager.checkLock "doc123", (err, isFree) ->
|
LockManager.checkLock("doc123", function(err, isFree) {
|
||||||
expect(isFree).to.equal false
|
expect(isFree).to.equal(false);
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,74 +1,89 @@
|
||||||
sinon = require "sinon"
|
/*
|
||||||
chai = require("chai")
|
* decaffeinate suggestions:
|
||||||
chai.should()
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
expect = chai.expect
|
* DS207: Consider shorter variations of null checks
|
||||||
mongojs = require "../../../app/js/mongojs"
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
db = mongojs.db
|
*/
|
||||||
ObjectId = mongojs.ObjectId
|
const sinon = require("sinon");
|
||||||
Settings = require "settings-sharelatex"
|
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"
|
const TrackChangesApp = require("./helpers/TrackChangesApp");
|
||||||
TrackChangesClient = require "./helpers/TrackChangesClient"
|
const TrackChangesClient = require("./helpers/TrackChangesClient");
|
||||||
MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi"
|
const MockDocUpdaterApi = require("./helpers/MockDocUpdaterApi");
|
||||||
MockWebApi = require "./helpers/MockWebApi"
|
const MockWebApi = require("./helpers/MockWebApi");
|
||||||
|
|
||||||
describe "Restoring a version", ->
|
describe("Restoring a version", function() {
|
||||||
before (done) ->
|
before(function(done) {
|
||||||
sinon.spy MockDocUpdaterApi, "setDoc"
|
sinon.spy(MockDocUpdaterApi, "setDoc");
|
||||||
|
|
||||||
@now = Date.now()
|
this.now = Date.now();
|
||||||
@user_id = ObjectId().toString()
|
this.user_id = ObjectId().toString();
|
||||||
@doc_id = ObjectId().toString()
|
this.doc_id = ObjectId().toString();
|
||||||
@project_id = ObjectId().toString()
|
this.project_id = ObjectId().toString();
|
||||||
MockWebApi.projects[@project_id] = features: versioning: true
|
MockWebApi.projects[this.project_id] = {features: {versioning: true}};
|
||||||
|
|
||||||
minutes = 60 * 1000
|
const minutes = 60 * 1000;
|
||||||
|
|
||||||
@updates = [{
|
this.updates = [{
|
||||||
op: [{ i: "one ", p: 0 }]
|
op: [{ i: "one ", p: 0 }],
|
||||||
meta: { ts: @now - 6 * minutes, user_id: @user_id }
|
meta: { ts: this.now - (6 * minutes), user_id: this.user_id },
|
||||||
v: 3
|
v: 3
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "two ", p: 4 }]
|
op: [{ i: "two ", p: 4 }],
|
||||||
meta: { ts: @now - 4 * minutes, user_id: @user_id }
|
meta: { ts: this.now - (4 * minutes), user_id: this.user_id },
|
||||||
v: 4
|
v: 4
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "three ", p: 8 }]
|
op: [{ i: "three ", p: 8 }],
|
||||||
meta: { ts: @now - 2 * minutes, user_id: @user_id }
|
meta: { ts: this.now - (2 * minutes), user_id: this.user_id },
|
||||||
v: 5
|
v: 5
|
||||||
}, {
|
}, {
|
||||||
op: [{ i: "four", p: 14 }]
|
op: [{ i: "four", p: 14 }],
|
||||||
meta: { ts: @now, user_id: @user_id }
|
meta: { ts: this.now, user_id: this.user_id },
|
||||||
v: 6
|
v: 6
|
||||||
}]
|
}];
|
||||||
@lines = ["one two three four"]
|
this.lines = ["one two three four"];
|
||||||
@restored_lines = ["one two "]
|
this.restored_lines = ["one two "];
|
||||||
@beforeVersion = 5
|
this.beforeVersion = 5;
|
||||||
|
|
||||||
MockWebApi.users[@user_id] = @user =
|
MockWebApi.users[this.user_id] = (this.user = {
|
||||||
email: "user@sharelatex.com"
|
email: "user@sharelatex.com",
|
||||||
first_name: "Leo"
|
first_name: "Leo",
|
||||||
last_name: "Lion"
|
last_name: "Lion",
|
||||||
id: @user_id
|
id: this.user_id
|
||||||
|
});
|
||||||
|
|
||||||
MockDocUpdaterApi.docs[@doc_id] =
|
MockDocUpdaterApi.docs[this.doc_id] = {
|
||||||
lines: @lines
|
lines: this.lines,
|
||||||
version: 7
|
version: 7
|
||||||
|
};
|
||||||
|
|
||||||
TrackChangesApp.ensureRunning =>
|
TrackChangesApp.ensureRunning(() => {
|
||||||
TrackChangesClient.pushRawUpdates @project_id, @doc_id, @updates, (error) =>
|
return TrackChangesClient.pushRawUpdates(this.project_id, this.doc_id, this.updates, error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
TrackChangesClient.restoreDoc @project_id, @doc_id, @beforeVersion, @user_id, (error) =>
|
return TrackChangesClient.restoreDoc(this.project_id, this.doc_id, this.beforeVersion, this.user_id, error => {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
done()
|
return done();
|
||||||
return null
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
after () ->
|
after(function() {
|
||||||
MockDocUpdaterApi.setDoc.restore()
|
MockDocUpdaterApi.setDoc.restore();
|
||||||
return null
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
it "should set the doc in the doc updater", ->
|
return it("should set the doc in the doc updater", function() {
|
||||||
MockDocUpdaterApi.setDoc
|
MockDocUpdaterApi.setDoc
|
||||||
.calledWith(@project_id, @doc_id, @restored_lines, @user_id, true)
|
.calledWith(this.project_id, this.doc_id, this.restored_lines, this.user_id, true)
|
||||||
.should.equal true
|
.should.equal(true);
|
||||||
return null
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -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 =
|
module.exports = (MockDocUpdaterApi = {
|
||||||
docs: {}
|
docs: {},
|
||||||
|
|
||||||
getAllDoc: (project_id, callback = (error) ->) ->
|
getAllDoc(project_id, callback) {
|
||||||
callback null, @docs
|
if (callback == null) { callback = function(error) {}; }
|
||||||
|
return callback(null, this.docs);
|
||||||
|
},
|
||||||
|
|
||||||
run: () ->
|
run() {
|
||||||
app.get "/project/:project_id/doc", (req, res, next) =>
|
app.get("/project/:project_id/doc", (req, res, next) => {
|
||||||
@getAllDoc req.params.project_id, (error, docs) ->
|
return this.getAllDoc(req.params.project_id, function(error, docs) {
|
||||||
if error?
|
if (error != null) {
|
||||||
res.send 500
|
res.send(500);
|
||||||
if !docs?
|
}
|
||||||
res.send 404
|
if ((docs == null)) {
|
||||||
else
|
return res.send(404);
|
||||||
res.send JSON.stringify docs
|
} else {
|
||||||
|
return res.send(JSON.stringify(docs));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.listen 3016, (error) ->
|
return app.listen(3016, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
.on "error", (error) ->
|
}).on("error", function(error) {
|
||||||
console.error "error starting MockDocStoreApi:", error.message
|
console.error("error starting MockDocStoreApi:", error.message);
|
||||||
process.exit(1)
|
return process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
MockDocUpdaterApi.run()
|
MockDocUpdaterApi.run();
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
module.exports = (MockDocUpdaterApi = {
|
||||||
docs: {}
|
docs: {},
|
||||||
|
|
||||||
getDoc: (project_id, doc_id, callback = (error) ->) ->
|
getDoc(project_id, doc_id, callback) {
|
||||||
callback null, @docs[doc_id]
|
if (callback == null) { callback = function(error) {}; }
|
||||||
|
return callback(null, this.docs[doc_id]);
|
||||||
|
},
|
||||||
|
|
||||||
setDoc: (project_id, doc_id, lines, user_id, undoing, callback = (error) ->) ->
|
setDoc(project_id, doc_id, lines, user_id, undoing, callback) {
|
||||||
@docs[doc_id] ||= {}
|
if (callback == null) { callback = function(error) {}; }
|
||||||
@docs[doc_id].lines = lines
|
if (!this.docs[doc_id]) { this.docs[doc_id] = {}; }
|
||||||
callback()
|
this.docs[doc_id].lines = lines;
|
||||||
|
return callback();
|
||||||
|
},
|
||||||
|
|
||||||
run: () ->
|
run() {
|
||||||
app.get "/project/:project_id/doc/:doc_id", (req, res, next) =>
|
app.get("/project/:project_id/doc/:doc_id", (req, res, next) => {
|
||||||
@getDoc req.params.project_id, req.params.doc_id, (error, doc) ->
|
return this.getDoc(req.params.project_id, req.params.doc_id, function(error, doc) {
|
||||||
if error?
|
if (error != null) {
|
||||||
res.send 500
|
res.send(500);
|
||||||
if !doc?
|
}
|
||||||
res.send 404
|
if ((doc == null)) {
|
||||||
else
|
return res.send(404);
|
||||||
res.send JSON.stringify doc
|
} else {
|
||||||
|
return res.send(JSON.stringify(doc));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.post "/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) =>
|
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) ->
|
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 error?
|
if (typeof error !== 'undefined' && error !== null) {
|
||||||
res.send 500
|
return res.send(500);
|
||||||
else
|
} else {
|
||||||
res.send 204
|
return res.send(204);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.listen 3003, (error) ->
|
return app.listen(3003, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
.on "error", (error) ->
|
}).on("error", function(error) {
|
||||||
console.error "error starting MockDocUpdaterApi:", error.message
|
console.error("error starting MockDocUpdaterApi:", error.message);
|
||||||
process.exit(1)
|
return process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
MockDocUpdaterApi.run()
|
MockDocUpdaterApi.run();
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
module.exports = (MockWebApi = {
|
||||||
users: {}
|
users: {},
|
||||||
|
|
||||||
projects: {}
|
projects: {},
|
||||||
|
|
||||||
getUserInfo: (user_id, callback = (error) ->) ->
|
getUserInfo(user_id, callback) {
|
||||||
callback null, @users[user_id] or null
|
if (callback == null) { callback = function(error) {}; }
|
||||||
|
return callback(null, this.users[user_id] || null);
|
||||||
|
},
|
||||||
|
|
||||||
getProjectDetails: (project_id, callback = (error, project) ->) ->
|
getProjectDetails(project_id, callback) {
|
||||||
callback null, @projects[project_id]
|
if (callback == null) { callback = function(error, project) {}; }
|
||||||
|
return callback(null, this.projects[project_id]);
|
||||||
|
},
|
||||||
|
|
||||||
run: () ->
|
run() {
|
||||||
app.get "/user/:user_id/personal_info", (req, res, next) =>
|
app.get("/user/:user_id/personal_info", (req, res, next) => {
|
||||||
@getUserInfo req.params.user_id, (error, user) ->
|
return this.getUserInfo(req.params.user_id, function(error, user) {
|
||||||
if error?
|
if (error != null) {
|
||||||
res.send 500
|
res.send(500);
|
||||||
if !user?
|
}
|
||||||
res.send 404
|
if ((user == null)) {
|
||||||
else
|
return res.send(404);
|
||||||
res.send JSON.stringify user
|
} else {
|
||||||
|
return res.send(JSON.stringify(user));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.get "/project/:project_id/details", (req, res, next) =>
|
app.get("/project/:project_id/details", (req, res, next) => {
|
||||||
@getProjectDetails req.params.project_id, (error, project) ->
|
return this.getProjectDetails(req.params.project_id, function(error, project) {
|
||||||
if error?
|
if (error != null) {
|
||||||
res.send 500
|
res.send(500);
|
||||||
if !project?
|
}
|
||||||
res.send 404
|
if ((project == null)) {
|
||||||
else
|
return res.send(404);
|
||||||
res.send JSON.stringify project
|
} else {
|
||||||
|
return res.send(JSON.stringify(project));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.listen 3000, (error) ->
|
return app.listen(3000, function(error) {
|
||||||
throw error if error?
|
if (error != null) { throw error; }
|
||||||
.on "error", (error) ->
|
}).on("error", function(error) {
|
||||||
console.error "error starting MockWebApiServer:", error.message
|
console.error("error starting MockWebApiServer:", error.message);
|
||||||
process.exit(1)
|
return process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
MockWebApi.run()
|
MockWebApi.run();
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,46 @@
|
||||||
app = require('../../../../app')
|
/*
|
||||||
require("logger-sharelatex")
|
* decaffeinate suggestions:
|
||||||
logger = require("logger-sharelatex")
|
* DS101: Remove unnecessary use of Array.from
|
||||||
Settings = require("settings-sharelatex")
|
* 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 =
|
module.exports = {
|
||||||
running: false
|
running: false,
|
||||||
initing: false
|
initing: false,
|
||||||
callbacks: []
|
callbacks: [],
|
||||||
ensureRunning: (callback = (error) ->) ->
|
ensureRunning(callback) {
|
||||||
if @running
|
if (callback == null) { callback = function(error) {}; }
|
||||||
return callback()
|
if (this.running) {
|
||||||
else if @initing
|
return callback();
|
||||||
@callbacks.push callback
|
} else if (this.initing) {
|
||||||
else
|
return this.callbacks.push(callback);
|
||||||
@initing = true
|
} else {
|
||||||
@callbacks.push callback
|
this.initing = true;
|
||||||
app.listen Settings.internal?.trackchanges?.port, "localhost", (error) =>
|
this.callbacks.push(callback);
|
||||||
throw error if error?
|
return app.listen(__guard__(Settings.internal != null ? Settings.internal.trackchanges : undefined, x => x.port), "localhost", error => {
|
||||||
@running = true
|
if (error != null) { throw error; }
|
||||||
logger.log("track changes running in dev mode")
|
this.running = true;
|
||||||
|
logger.log("track changes running in dev mode");
|
||||||
|
|
||||||
for callback in @callbacks
|
return (() => {
|
||||||
callback()
|
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;
|
||||||
|
}
|
|
@ -1,144 +1,202 @@
|
||||||
async = require 'async'
|
/*
|
||||||
zlib = require 'zlib'
|
* decaffeinate suggestions:
|
||||||
request = require "request"
|
* DS101: Remove unnecessary use of Array.from
|
||||||
Settings = require "settings-sharelatex"
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
rclient = require("redis-sharelatex").createClient(Settings.redis.history) # Only works locally for now
|
* DS207: Consider shorter variations of null checks
|
||||||
Keys = Settings.redis.history.key_schema
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
{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");
|
||||||
|
|
||||||
aws = require "aws-sdk"
|
const aws = require("aws-sdk");
|
||||||
s3 = new aws.S3(
|
const s3 = new aws.S3({
|
||||||
accessKeyId: Settings.trackchanges.s3.key
|
accessKeyId: Settings.trackchanges.s3.key,
|
||||||
secretAccessKey: Settings.trackchanges.s3.secret
|
secretAccessKey: Settings.trackchanges.s3.secret,
|
||||||
endpoint: Settings.trackchanges.s3.endpoint
|
endpoint: Settings.trackchanges.s3.endpoint,
|
||||||
s3ForcePathStyle: Settings.trackchanges.s3.pathStyle
|
s3ForcePathStyle: Settings.trackchanges.s3.pathStyle
|
||||||
)
|
});
|
||||||
S3_BUCKET = Settings.trackchanges.stores.doc_history
|
const S3_BUCKET = Settings.trackchanges.stores.doc_history;
|
||||||
|
|
||||||
module.exports = TrackChangesClient =
|
module.exports = (TrackChangesClient = {
|
||||||
flushAndGetCompressedUpdates: (project_id, doc_id, callback = (error, updates) ->) ->
|
flushAndGetCompressedUpdates(project_id, doc_id, callback) {
|
||||||
TrackChangesClient.flushDoc project_id, doc_id, (error) ->
|
if (callback == null) { callback = function(error, updates) {}; }
|
||||||
return callback(error) if error?
|
return TrackChangesClient.flushDoc(project_id, doc_id, function(error) {
|
||||||
TrackChangesClient.getCompressedUpdates doc_id, callback
|
if (error != null) { return callback(error); }
|
||||||
|
return TrackChangesClient.getCompressedUpdates(doc_id, callback);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
flushDoc: (project_id, doc_id, callback = (error) ->) ->
|
flushDoc(project_id, doc_id, callback) {
|
||||||
request.post {
|
if (callback == null) { callback = function(error) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/flush"
|
return request.post({
|
||||||
}, (error, response, body) =>
|
url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/flush`
|
||||||
response.statusCode.should.equal 204
|
}, (error, response, body) => {
|
||||||
callback(error)
|
response.statusCode.should.equal(204);
|
||||||
|
return callback(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
flushProject: (project_id, callback = (error) ->) ->
|
flushProject(project_id, callback) {
|
||||||
request.post {
|
if (callback == null) { callback = function(error) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/flush"
|
return request.post({
|
||||||
}, (error, response, body) =>
|
url: `http://localhost:3015/project/${project_id}/flush`
|
||||||
response.statusCode.should.equal 204
|
}, (error, response, body) => {
|
||||||
callback(error)
|
response.statusCode.should.equal(204);
|
||||||
|
return callback(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getCompressedUpdates: (doc_id, callback = (error, updates) ->) ->
|
getCompressedUpdates(doc_id, callback) {
|
||||||
db.docHistory
|
if (callback == null) { callback = function(error, updates) {}; }
|
||||||
.find(doc_id: ObjectId(doc_id))
|
return db.docHistory
|
||||||
.sort("meta.end_ts": 1)
|
.find({doc_id: ObjectId(doc_id)})
|
||||||
.toArray callback
|
.sort({"meta.end_ts": 1})
|
||||||
|
.toArray(callback);
|
||||||
|
},
|
||||||
|
|
||||||
getProjectMetaData: (project_id, callback = (error, updates) ->) ->
|
getProjectMetaData(project_id, callback) {
|
||||||
db.projectHistoryMetaData
|
if (callback == null) { callback = function(error, updates) {}; }
|
||||||
.find {
|
return db.projectHistoryMetaData
|
||||||
|
.find({
|
||||||
project_id: ObjectId(project_id)
|
project_id: ObjectId(project_id)
|
||||||
},
|
},
|
||||||
(error, projects) ->
|
(error, projects) => callback(error, projects[0]));
|
||||||
callback error, projects[0]
|
},
|
||||||
|
|
||||||
setPreserveHistoryForProject: (project_id, callback = (error) ->) ->
|
setPreserveHistoryForProject(project_id, callback) {
|
||||||
db.projectHistoryMetaData.update {
|
if (callback == null) { callback = function(error) {}; }
|
||||||
|
return db.projectHistoryMetaData.update({
|
||||||
project_id: ObjectId(project_id)
|
project_id: ObjectId(project_id)
|
||||||
}, {
|
}, {
|
||||||
$set: { preserveHistory: true }
|
$set: { preserveHistory: true }
|
||||||
}, {
|
}, {
|
||||||
upsert: true
|
upsert: true
|
||||||
}, callback
|
}, callback);
|
||||||
|
},
|
||||||
|
|
||||||
pushRawUpdates: (project_id, doc_id, updates, callback = (error) ->) ->
|
pushRawUpdates(project_id, doc_id, updates, callback) {
|
||||||
rclient.sadd Keys.docsWithHistoryOps({project_id}), doc_id, (error) ->
|
if (callback == null) { callback = function(error) {}; }
|
||||||
return callback(error) if error?
|
return rclient.sadd(Keys.docsWithHistoryOps({project_id}), doc_id, function(error) {
|
||||||
rclient.rpush Keys.uncompressedHistoryOps({doc_id}), (JSON.stringify(u) for u in updates)..., callback
|
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) ->) ->
|
getDiff(project_id, doc_id, from, to, callback) {
|
||||||
request.get {
|
if (callback == null) { callback = function(error, diff) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/diff?from=#{from}&to=#{to}"
|
return request.get({
|
||||||
}, (error, response, body) =>
|
url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/diff?from=${from}&to=${to}`
|
||||||
response.statusCode.should.equal 200
|
}, (error, response, body) => {
|
||||||
callback null, JSON.parse(body)
|
response.statusCode.should.equal(200);
|
||||||
|
return callback(null, JSON.parse(body));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getUpdates: (project_id, options, callback = (error, body) ->) ->
|
getUpdates(project_id, options, callback) {
|
||||||
request.get {
|
if (callback == null) { callback = function(error, body) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/updates?before=#{options.before}&min_count=#{options.min_count}"
|
return request.get({
|
||||||
}, (error, response, body) =>
|
url: `http://localhost:3015/project/${project_id}/updates?before=${options.before}&min_count=${options.min_count}`
|
||||||
response.statusCode.should.equal 200
|
}, (error, response, body) => {
|
||||||
callback null, JSON.parse(body)
|
response.statusCode.should.equal(200);
|
||||||
|
return callback(null, JSON.parse(body));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
restoreDoc: (project_id, doc_id, version, user_id, callback = (error) ->) ->
|
restoreDoc(project_id, doc_id, version, user_id, callback) {
|
||||||
request.post {
|
if (callback == null) { callback = function(error) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/version/#{version}/restore"
|
return request.post({
|
||||||
headers:
|
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
|
}, (error, response, body) => {
|
||||||
callback null
|
response.statusCode.should.equal(204);
|
||||||
|
return callback(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
pushDocHistory: (project_id, doc_id, callback = (error) ->) ->
|
pushDocHistory(project_id, doc_id, callback) {
|
||||||
request.post {
|
if (callback == null) { callback = function(error) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/push"
|
return request.post({
|
||||||
}, (error, response, body) =>
|
url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/push`
|
||||||
response.statusCode.should.equal 204
|
}, (error, response, body) => {
|
||||||
callback(error)
|
response.statusCode.should.equal(204);
|
||||||
|
return callback(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
pullDocHistory: (project_id, doc_id, callback = (error) ->) ->
|
pullDocHistory(project_id, doc_id, callback) {
|
||||||
request.post {
|
if (callback == null) { callback = function(error) {}; }
|
||||||
url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/pull"
|
return request.post({
|
||||||
}, (error, response, body) =>
|
url: `http://localhost:3015/project/${project_id}/doc/${doc_id}/pull`
|
||||||
response.statusCode.should.equal 204
|
}, (error, response, body) => {
|
||||||
callback(error)
|
response.statusCode.should.equal(204);
|
||||||
|
return callback(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
waitForS3: (done, retries=42) ->
|
waitForS3(done, retries) {
|
||||||
if !Settings.trackchanges.s3.endpoint
|
if (retries == null) { retries = 42; }
|
||||||
return done()
|
if (!Settings.trackchanges.s3.endpoint) {
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
|
||||||
request.get "#{Settings.trackchanges.s3.endpoint}/", (err, res) ->
|
return request.get(`${Settings.trackchanges.s3.endpoint}/`, function(err, res) {
|
||||||
if res && res.statusCode < 500
|
if (res && (res.statusCode < 500)) {
|
||||||
return done()
|
return done();
|
||||||
|
}
|
||||||
|
|
||||||
if retries == 0
|
if (retries === 0) {
|
||||||
return done(err or new Error("s3 returned #{res.statusCode}"))
|
return done(err || new Error(`s3 returned ${res.statusCode}`));
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout () ->
|
return setTimeout(() => TrackChangesClient.waitForS3(done, --retries)
|
||||||
TrackChangesClient.waitForS3(done, --retries)
|
, 1000);
|
||||||
, 1000
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getS3Doc: (project_id, doc_id, pack_id, callback = (error, body) ->) ->
|
getS3Doc(project_id, doc_id, pack_id, callback) {
|
||||||
params =
|
if (callback == null) { callback = function(error, body) {}; }
|
||||||
Bucket: S3_BUCKET
|
const params = {
|
||||||
Key: "#{project_id}/changes-#{doc_id}/pack-#{pack_id}"
|
Bucket: S3_BUCKET,
|
||||||
|
Key: `${project_id}/changes-${doc_id}/pack-${pack_id}`
|
||||||
|
};
|
||||||
|
|
||||||
s3.getObject params, (error, data) ->
|
return s3.getObject(params, function(error, data) {
|
||||||
return callback(error) if error?
|
if (error != null) { return callback(error); }
|
||||||
body = data.Body
|
const body = data.Body;
|
||||||
return callback(new Error("empty response from s3")) if not body?
|
if ((body == null)) { return callback(new Error("empty response from s3")); }
|
||||||
zlib.gunzip body, (err, result) ->
|
return zlib.gunzip(body, function(err, result) {
|
||||||
return callback(err) if err?
|
if (err != null) { return callback(err); }
|
||||||
callback(null, JSON.parse(result.toString()))
|
return callback(null, JSON.parse(result.toString()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
removeS3Doc: (project_id, doc_id, callback = (error, res, body) ->) ->
|
removeS3Doc(project_id, doc_id, callback) {
|
||||||
params =
|
if (callback == null) { callback = function(error, res, body) {}; }
|
||||||
Bucket: S3_BUCKET
|
let params = {
|
||||||
Prefix: "#{project_id}/changes-#{doc_id}"
|
Bucket: S3_BUCKET,
|
||||||
|
Prefix: `${project_id}/changes-${doc_id}`
|
||||||
|
};
|
||||||
|
|
||||||
s3.listObjects params, (error, data) ->
|
return s3.listObjects(params, function(error, data) {
|
||||||
return callback(error) if error?
|
if (error != null) { return callback(error); }
|
||||||
|
|
||||||
params =
|
params = {
|
||||||
Bucket: S3_BUCKET
|
Bucket: S3_BUCKET,
|
||||||
Delete:
|
Delete: {
|
||||||
Objects: data.Contents.map((s3object) -> {Key: s3object.Key})
|
Objects: data.Contents.map(s3object => ({Key: s3object.Key}))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
s3.deleteObjects params, callback
|
return s3.deleteObjects(params, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue