Maintenance: Fix difference function to compare both ways and also make sure that the observer copies hashes to prevent references.

This commit is contained in:
Rolf Schmidt 2022-01-31 10:50:48 +01:00
parent 6b093828a5
commit 8dd045af75
9 changed files with 342 additions and 60 deletions

View file

@ -2,6 +2,7 @@ class App.ControllerObserver extends App.Controller
model: 'Ticket' model: 'Ticket'
template: 'tba' template: 'tba'
globalRerender: true globalRerender: true
lastAttributes: undefined
### ###
observe: observe:
@ -25,7 +26,7 @@ class App.ControllerObserver extends App.Controller
# rerender, e. g. on language change # rerender, e. g. on language change
if @globalRerender if @globalRerender
@controllerBind('ui:rerender', => @controllerBind('ui:rerender', =>
@lastAttributres = undefined @lastAttributes = undefined
@maybeRender(App[@model].fullLocal(@object_id)) @maybeRender(App[@model].fullLocal(@object_id))
) )
@ -43,32 +44,39 @@ class App.ControllerObserver extends App.Controller
if !@subscribeId if !@subscribeId
@subscribeId = object.subscribe(@subscribe) @subscribeId = object.subscribe(@subscribe)
# remember current attributes return if !@hasChanged(object)
@render(object)
hasChanged: (object) =>
currentAttributes = {} currentAttributes = {}
if @observe if @observe
for key, active of @observe for key, active of @observe
if active if active && !_.isFunction(value)
currentAttributes[key] = object[key] currentAttributes[key] = clone(object[key])
if @observeNot if @observeNot
for key, value of object for key, value of object
if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value) && !_.isObject(value) if key isnt 'cid' && !@observeNot[key] && !_.isFunction(value)
currentAttributes[key] = value currentAttributes[key] = clone(value)
if !@lastAttributres if !@lastAttributes
@lastAttributres = {} @lastAttributes = currentAttributes
else return true
diff = difference(currentAttributes, @lastAttributres)
diff = difference(currentAttributes, @lastAttributes)
if _.isEmpty(diff) if _.isEmpty(diff)
@log 'debug', 'maybeRender no diff, no rerender' @log 'debug', 'maybeRender no diff, no rerender'
return return false
@log 'debug', 'maybeRender.diff', diff, @observe, @model @log 'debug', 'maybeRender.diff', diff, @observe, @model
@lastAttributres = currentAttributes @lastAttributes = currentAttributes
@render(object, diff) true
render: (object, diff) => render: (object) =>
@log 'debug', 'render', @template, object, diff @log 'debug', 'render', @template, object
@html App.view(@template)( @html App.view(@template)(
object: object object: object
) )

View file

@ -202,7 +202,7 @@ class Object extends App.ControllerObserver
value = $(e.target).html() value = $(e.target).html()
org = App.Organization.find(@object_id) org = App.Organization.find(@object_id)
if org[name] isnt value if org[name] isnt value
@lastAttributres[name] = value @lastAttributes[name] = value
data = {} data = {}
data[name] = value data[name] = value
org.updateAttributes(data) org.updateAttributes(data)

View file

@ -39,7 +39,7 @@ class Internal
internal = true internal = true
if article.internal == true if article.internal == true
internal = false internal = false
ui.lastAttributres.internal = internal ui.lastAttributes.internal = internal
article.updateAttributes(internal: internal) article.updateAttributes(internal: internal)
# runtime update # runtime update

View file

@ -221,7 +221,7 @@ class ArticleViewItem extends App.ControllerObserver
el: @$('.js-article-actions') el: @$('.js-article-actions')
ticket: @ticket ticket: @ticket
article: article article: article
lastAttributres: @lastAttributres lastAttributes: @lastAttributes
form_id: @form_id form_id: @form_id
) )

View file

@ -19,7 +19,7 @@ class App.TicketZoomTitle extends App.ControllerObserver
title = $(e.target).ceg() || '' title = $(e.target).ceg() || ''
# update title # update title
return if title is @lastAttributres.title return if title is @lastAttributes.title
ticket = App.Ticket.find(@object_id) ticket = App.Ticket.find(@object_id)
ticket.title = title ticket.title = title

View file

@ -220,7 +220,7 @@ class Object extends App.ControllerObserver
value = $(e.target).html() value = $(e.target).html()
user = App.User.find(@object_id) user = App.User.find(@object_id)
if user[name] isnt value if user[name] isnt value
@lastAttributres[name] = value @lastAttributes[name] = value
data = {} data = {}
data[name] = value data[name] = value
user.updateAttributes(data) user.updateAttributes(data)

View file

@ -70,9 +70,9 @@ Date.prototype.getWeek = function() {
function difference(object1, object2) { function difference(object1, object2) {
var changes = {}; var changes = {};
for (var name in object1) { _.uniq(Object.keys(object1).concat(Object.keys(object2))).forEach(function(name) {
if (name in object2) { if (name in object1 && name in object2) {
if (_.isObject(object2[name]) && !_.isArray(object2[name])) { if (_.isObject(object1[name]) && !_.isArray(object1[name]) && _.isObject(object2[name]) && !_.isArray(object2[name])) {
var diff = difference(object1[name], object2[name]); var diff = difference(object1[name], object2[name]);
if (!_.isEmpty(diff)) { if (!_.isEmpty(diff)) {
changes[name] = diff; changes[name] = diff;
@ -80,8 +80,10 @@ function difference(object1, object2) {
} else if (!_.isEqual(object1[name], object2[name])) { } else if (!_.isEqual(object1[name], object2[name])) {
changes[name] = object2[name]; changes[name] = object2[name];
} }
} else {
changes[name] = object2[name]
} }
} })
return changes; return changes;
} }

View file

@ -0,0 +1,202 @@
QUnit.test( "controller observer tests - observe", assert => {
App.Ticket.refresh([{
id: 1,
title: 'ticket',
state_id: 1,
customer_id: 33,
organization_id: 1,
owner_id: 1,
preferences: { a: 1, b: 2 },
}])
var observer1 = new App.ControllerObserver({
object_id: 1,
template: 'version',
observe: {
title: true,
preferences: true,
},
})
var ticket = App.Ticket.find(1)
assert.equal(false, observer1.hasChanged(ticket))
// track title changes
ticket.title = 'title 2'
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.title = undefined
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.title = 'title 3'
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track no owner_id changes
ticket.owner_id = 2
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track preferences changes
ticket.preferences['a'] = 3
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.preferences['c'] = 3
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track no new_attribute1 changes
ticket.new_attribute1 = 'na 3'
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.new_attribute2 = function() { console.log(1) }
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.new_attribute2 = function() { console.log(2) }
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track title changes
ticket.title = function() { console.log(1) }
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.title = function() { console.log(2) }
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.title = 1
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
});
QUnit.test( "controller observer tests - observeNot", assert => {
App.Ticket.refresh([{
id: 2,
title: 'ticket',
state_id: 1,
customer_id: 33,
organization_id: 1,
owner_id: 1,
preferences: { a: 1, b: 2 },
}])
var observer1 = new App.ControllerObserver({
object_id: 2,
template: 'version',
observeNot: {
title: true,
preferences: true,
},
})
var ticket = App.Ticket.find(2)
assert.equal(false, observer1.hasChanged(ticket))
// track no title changes
ticket.title = 'title 2'
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track owner_id changes
ticket.owner_id = 2
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.owner_id = undefined
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.owner_id = 3
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track no preferences changes
ticket.preferences['a'] = 3
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.preferences['c'] = 3
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track preferences2 changes
ticket.preferences2 = {}
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.preferences2['a'] = 3
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.preferences2['a'] = 2
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.preferences2['c'] = 3
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track new_attribute1 changes
ticket.new_attribute1 = 'na 3'
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track no new_attribute2 changes (because of function content)
ticket.new_attribute2 = function() { console.log(1) }
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.new_attribute2 = function() { console.log(2) }
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
// track owner_id changes (pnly if content has no function content)
ticket.owner_id = function() { console.log(1) }
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.owner_id = function() { console.log(2) }
assert.equal(false, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
ticket.owner_id = 1
assert.equal(true, observer1.hasChanged(ticket))
assert.equal(false, observer1.hasChanged(ticket))
});

View file

@ -538,7 +538,9 @@ QUnit.test('difference', assert => {
object2 = { object2 = {
key1: 123, key1: 123,
} }
result = {} result = {
key2: undefined
}
item = difference(object1, object2) item = difference(object1, object2)
assert.deepEqual(item, result) assert.deepEqual(item, result)
@ -549,38 +551,106 @@ QUnit.test('difference', assert => {
key1: 123, key1: 123,
key2: 124 key2: 124
} }
result = {
key2: 124
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
organization_id: 2,
}
object2 = {
customer_id: 1,
organization_id: null,
}
result = {
organization_id: null,
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
organization_id: null,
}
object2 = {
customer_id: 1,
organization_id: 2,
}
result = {
organization_id: 2,
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
preferences: { resolved: true },
}
object2 = {
customer_id: 1,
preferences: {},
}
result = {
preferences: { resolved: undefined }
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
}
object2 = {
customer_id: 1,
preferences: { resolved: true },
}
result = {
preferences: { resolved: true }
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
preferences: {},
}
object2 = {
customer_id: 1,
preferences: { resolved: true },
}
result = {
preferences: { resolved: true }
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
preferences: { resolved: false },
}
object2 = {
customer_id: 1,
preferences: { resolved: true },
}
result = {
preferences: { resolved: true }
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
preferences: { resolved: true },
}
object2 = {
customer_id: 1,
preferences: { resolved: true },
}
result = {} result = {}
item = difference(object1, object2) item = difference(object1, object2)
assert.deepEqual(item, result) assert.deepEqual(item, result)
object1 = {
customer_id: 1,
organization_id: 2,
}
object2 = {
customer_id: 1,
organization_id: null,
}
result = {
organization_id: null,
}
item = difference(object1, object2)
assert.deepEqual(item, result)
object1 = {
customer_id: 1,
organization_id: null,
}
object2 = {
customer_id: 1,
organization_id: 2,
}
result = {
organization_id: 2,
}
item = difference(object1, object2)
assert.deepEqual(item, result)
}); });
QUnit.test('auth - not existing user', assert => { QUnit.test('auth - not existing user', assert => {