mirror of https://github.com/status-im/codimd.git
refactor(realtime): extract "update dirty note" to new job
Signed-off-by: BoHong Li <a60814billy@gmail.com>
This commit is contained in:
parent
702fc48fa8
commit
0352057c8b
|
@ -1,30 +1,31 @@
|
|||
'use strict'
|
||||
// realtime
|
||||
// external modules
|
||||
var cookie = require('cookie')
|
||||
var cookieParser = require('cookie-parser')
|
||||
var url = require('url')
|
||||
var async = require('async')
|
||||
var randomcolor = require('randomcolor')
|
||||
var Chance = require('chance')
|
||||
var chance = new Chance()
|
||||
var moment = require('moment')
|
||||
const cookie = require('cookie')
|
||||
const cookieParser = require('cookie-parser')
|
||||
const url = require('url')
|
||||
const async = require('async')
|
||||
const randomcolor = require('randomcolor')
|
||||
const Chance = require('chance')
|
||||
const chance = new Chance()
|
||||
const moment = require('moment')
|
||||
|
||||
const get = require('lodash/get')
|
||||
|
||||
// core
|
||||
var config = require('./config')
|
||||
var logger = require('./logger')
|
||||
var history = require('./history')
|
||||
var models = require('./models')
|
||||
const config = require('./config')
|
||||
const logger = require('./logger')
|
||||
const history = require('./history')
|
||||
const models = require('./models')
|
||||
|
||||
// ot
|
||||
var ot = require('./ot')
|
||||
const ot = require('./ot')
|
||||
|
||||
const {RealtimeClientConnection} = require('./realtimeClientConnection')
|
||||
const { RealtimeClientConnection } = require('./realtimeClientConnection')
|
||||
const { UpdateDirtyNoteJob } = require('./realtimeUpdateDirtyNoteJob')
|
||||
|
||||
// public
|
||||
var realtime = {
|
||||
const realtime = {
|
||||
io: null,
|
||||
onAuthorizeSuccess: onAuthorizeSuccess,
|
||||
onAuthorizeFail: onAuthorizeFail,
|
||||
|
@ -83,44 +84,20 @@ function emitCheck (note) {
|
|||
// actions
|
||||
var users = {}
|
||||
var notes = {}
|
||||
// update when the note is dirty
|
||||
setInterval(function () {
|
||||
async.each(Object.keys(notes), function (key, callback) {
|
||||
var note = notes[key]
|
||||
if (note.server.isDirty) {
|
||||
if (config.debug) logger.info('updater found dirty note: ' + key)
|
||||
note.server.isDirty = false
|
||||
exports.updateNote(note, function (err, _note) {
|
||||
// handle when note already been clean up
|
||||
if (!notes[key] || !notes[key].server) return callback(null, null)
|
||||
if (!_note) {
|
||||
realtime.io.to(note.id).emit('info', {
|
||||
code: 404
|
||||
})
|
||||
logger.error('note not found: ', note.id)
|
||||
}
|
||||
if (err || !_note) {
|
||||
for (var i = 0, l = note.socks.length; i < l; i++) {
|
||||
var sock = note.socks[i]
|
||||
if (typeof sock !== 'undefined' && sock) {
|
||||
setTimeout(function () {
|
||||
sock.disconnect(true)
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
return callback(err, null)
|
||||
}
|
||||
note.updatetime = moment(_note.lastchangeAt).valueOf()
|
||||
emitCheck(note)
|
||||
return callback(null, null)
|
||||
|
||||
const updateDirtyNoteJob = new UpdateDirtyNoteJob(realtime)
|
||||
updateDirtyNoteJob.start(realtime)
|
||||
|
||||
function disconnectSocketOnNote (note) {
|
||||
note.socks.forEach((sock) => {
|
||||
if (sock) {
|
||||
sock.emit('delete')
|
||||
setImmediate(() => {
|
||||
sock.disconnect(true)
|
||||
})
|
||||
} else {
|
||||
return callback(null, null)
|
||||
}
|
||||
}, function (err) {
|
||||
if (err) return logger.error('updater error', err)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
function updateNote (note, callback) {
|
||||
models.Note.findOne({
|
||||
|
@ -858,6 +835,9 @@ exports.checkViewPermission = checkViewPermission
|
|||
exports.getNoteFromNotePool = getNoteFromNotePool
|
||||
exports.getUserFromUserPool = getUserFromUserPool
|
||||
exports.buildUserOutData = buildUserOutData
|
||||
exports.getNotePool = getNotePool
|
||||
exports.emitCheck = emitCheck
|
||||
exports.disconnectSocketOnNote = disconnectSocketOnNote
|
||||
exports.notes = notes
|
||||
exports.users = users
|
||||
exports.disconnectSocketQueue = disconnectSocketQueue
|
||||
|
|
|
@ -57,17 +57,6 @@ class RealtimeClientConnection {
|
|||
return config.allowAnonymous || config.allowAnonymousEdits
|
||||
}
|
||||
|
||||
disconnectSocketOnNote (note) {
|
||||
note.socks.forEach((sock) => {
|
||||
if (sock) {
|
||||
sock.emit('delete')
|
||||
setImmediate(() => {
|
||||
sock.disconnect(true)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getCurrentUser () {
|
||||
if (!this.socket.id) return
|
||||
return this.realtime.getUserFromUserPool(this.socket.id)
|
||||
|
@ -206,7 +195,7 @@ class RealtimeClientConnection {
|
|||
this.destroyNote(note.id)
|
||||
.then((successRows) => {
|
||||
if (!successRows) return
|
||||
this.disconnectSocketOnNote(note)
|
||||
this.realtime.disconnectSocketOnNote(note)
|
||||
})
|
||||
.catch(function (err) {
|
||||
return logger.error('delete note failed: ' + err)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
'use strict'
|
||||
|
||||
const async = require('async')
|
||||
const config = require('./config')
|
||||
const logger = require('./logger')
|
||||
const moment = require('moment')
|
||||
|
||||
class UpdateDirtyNoteJob {
|
||||
constructor (realtime) {
|
||||
this.realtime = realtime
|
||||
}
|
||||
|
||||
start () {
|
||||
setInterval(this.updateDirtyNote.bind(this), 1000)
|
||||
}
|
||||
|
||||
updateDirtyNote () {
|
||||
const notes = this.realtime.getNotePool()
|
||||
async.each(Object.keys(notes), (key, callback) => {
|
||||
const note = notes[key]
|
||||
if (!note.server.isDirty) return callback(null, null)
|
||||
|
||||
if (config.debug) logger.info('updater found dirty note: ' + key)
|
||||
note.server.isDirty = false
|
||||
this.realtime.updateNote(note, (err, _note) => {
|
||||
// handle when note already been clean up
|
||||
if (!notes[key] || !notes[key].server) return callback(null, null)
|
||||
if (!_note) {
|
||||
this.realtime.io.to(note.id).emit('info', {
|
||||
code: 404
|
||||
})
|
||||
logger.error('note not found: ', note.id)
|
||||
}
|
||||
|
||||
if (err || !_note) {
|
||||
this.realtime.disconnectSocketOnNote(note)
|
||||
return callback(err, null)
|
||||
}
|
||||
|
||||
note.updatetime = moment(_note.lastchangeAt).valueOf()
|
||||
this.realtime.emitCheck(note)
|
||||
return callback(null, null)
|
||||
})
|
||||
}, (err) => {
|
||||
if (err) return logger.error('updater error', err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.UpdateDirtyNoteJob = UpdateDirtyNoteJob
|
|
@ -0,0 +1,130 @@
|
|||
/* eslint-env node, mocha */
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const mock = require('mock-require')
|
||||
const sinon = require('sinon')
|
||||
const { removeModuleFromRequireCache, makeMockSocket } = require('./utils')
|
||||
|
||||
describe('realtime#update note is dirty timer', function () {
|
||||
let realtime
|
||||
let clock
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers({
|
||||
toFake: ['setInterval']
|
||||
})
|
||||
mock('../../lib/logger', {
|
||||
error: () => {
|
||||
}
|
||||
})
|
||||
mock('../../lib/history', {})
|
||||
mock('../../lib/models', {
|
||||
Revision: {
|
||||
saveAllNotesRevision: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
mock('../../lib/config', {})
|
||||
realtime = require('../../lib/realtime')
|
||||
|
||||
realtime.io = {
|
||||
to: sinon.stub().callsFake(function () {
|
||||
return {
|
||||
emit: sinon.fake()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
removeModuleFromRequireCache('../../lib/realtimeUpdateDirtyNoteJob')
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
mock.stopAll()
|
||||
clock.restore()
|
||||
})
|
||||
|
||||
it('should update note when note is dirty', (done) => {
|
||||
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
|
||||
callback(null, note)
|
||||
})
|
||||
|
||||
realtime.notes['note1'] = {
|
||||
server: {
|
||||
isDirty: false
|
||||
},
|
||||
socks: []
|
||||
}
|
||||
|
||||
let note2 = {
|
||||
server: {
|
||||
isDirty: true
|
||||
},
|
||||
socks: []
|
||||
}
|
||||
|
||||
realtime.notes['note2'] = note2
|
||||
|
||||
clock.tick(1000)
|
||||
|
||||
setTimeout(() => {
|
||||
assert(note2.server.isDirty === false)
|
||||
done()
|
||||
}, 5)
|
||||
})
|
||||
|
||||
it('should not do anything when note missing', function (done) {
|
||||
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
|
||||
delete realtime.notes['note']
|
||||
callback(null, note)
|
||||
})
|
||||
|
||||
let note = {
|
||||
server: {
|
||||
isDirty: true
|
||||
},
|
||||
socks: [makeMockSocket()]
|
||||
}
|
||||
realtime.notes['note'] = note
|
||||
|
||||
clock.tick(1000)
|
||||
|
||||
setTimeout(() => {
|
||||
assert(note.server.isDirty === false)
|
||||
assert(note.socks[0].disconnect.called === false)
|
||||
done()
|
||||
}, 50)
|
||||
})
|
||||
|
||||
it('should disconnect all clients when update note error', function (done) {
|
||||
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
|
||||
callback(new Error('some error'), null)
|
||||
})
|
||||
|
||||
realtime.io = {
|
||||
to: sinon.stub().callsFake(function () {
|
||||
return {
|
||||
emit: sinon.fake()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let note = {
|
||||
server: {
|
||||
isDirty: true
|
||||
},
|
||||
socks: [makeMockSocket(), undefined, makeMockSocket()]
|
||||
}
|
||||
realtime.notes['note'] = note
|
||||
|
||||
clock.tick(1000)
|
||||
|
||||
setTimeout(() => {
|
||||
assert(note.server.isDirty === false)
|
||||
assert(note.socks[0].disconnect.called)
|
||||
assert(note.socks[2].disconnect.called)
|
||||
done()
|
||||
}, 50)
|
||||
})
|
||||
|
||||
})
|
|
@ -37,67 +37,6 @@ function removeModuleFromRequireCache (modulePath) {
|
|||
}
|
||||
|
||||
describe('realtime', function () {
|
||||
|
||||
describe('update note is dirty timer', function () {
|
||||
let realtime
|
||||
beforeEach(() => {
|
||||
mock('../../lib/logger', {
|
||||
error: () => {
|
||||
}
|
||||
})
|
||||
mock('../../lib/history', {})
|
||||
mock('../../lib/models', {
|
||||
Revision: {
|
||||
saveAllNotesRevision: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
mock('../../lib/config', {})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
removeModuleFromRequireCache('../../lib/realtime')
|
||||
mock.stopAll()
|
||||
})
|
||||
|
||||
it('should update note when note is dirty', (done) => {
|
||||
const clock = sinon.useFakeTimers()
|
||||
realtime = require('../../lib/realtime')
|
||||
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
|
||||
callback(null, null)
|
||||
})
|
||||
const socketIoEmitFake = sinon.fake()
|
||||
realtime.io = {
|
||||
to: sinon.stub().callsFake(function () {
|
||||
return {
|
||||
emit: socketIoEmitFake
|
||||
}
|
||||
})
|
||||
}
|
||||
realtime.notes['note1'] = {
|
||||
server: {
|
||||
isDirty: false
|
||||
},
|
||||
socks: []
|
||||
}
|
||||
let note2 = {
|
||||
server: {
|
||||
isDirty: true
|
||||
},
|
||||
socks: []
|
||||
}
|
||||
realtime.notes['note2'] = note2
|
||||
|
||||
clock.tick(1000)
|
||||
clock.restore()
|
||||
|
||||
setTimeout(() => {
|
||||
assert(note2.server.isDirty === false)
|
||||
done()
|
||||
}, 50)
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateNote', function () {
|
||||
let realtime, fakeNote
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -537,6 +537,5 @@ describe('realtime#socket event', function () {
|
|||
done()
|
||||
}, 5)
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue