123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class TransactionDispatcher
- def self.reset
- EventBuffer.reset('transaction')
- end
- def self.commit(params = {})
- # add attribute of interface handle (e. g. to send (no) notifications if a agent
- # is creating a ticket via application_server, but send it if it's created via
- # postmaster)
- params[:interface_handle] = ApplicationHandleInfo.current
- # execute object transactions
- TransactionDispatcher.perform(params)
- end
- def self.perform(params)
- # return if we run import mode
- return if Setting.get('import_mode')
- # get buffer
- list = EventBuffer.list('transaction')
- # reset buffer
- EventBuffer.reset('transaction')
- # get async backends
- sync_backends = []
- Setting.where(area: 'Transaction::Backend::Sync').reorder(:name).each do |setting|
- backend = Setting.get(setting.name)
- next if params[:disable]&.include?(backend)
- sync_backends.push backend.constantize
- end
- # get uniq objects
- list_objects = get_uniq_changes(list)
- list_objects.each_value do |objects|
- objects.each_value do |item|
- # execute sync backends
- sync_backends.each do |backend|
- execute_single_backend(backend, item, params)
- end
- # execute async backends
- TransactionJob.perform_later(item, params)
- end
- end
- end
- def self.execute_single_backend(backend, item, params)
- Rails.logger.debug { "Execute single backend #{backend}" }
- begin
- UserInfo.current_user_id = nil
- integration = backend.new(item, params)
- integration.perform
- rescue => e
- Rails.logger.error e
- end
- end
- =begin
- result = get_uniq_changes(events)
- result = {
- 'Ticket' =>
- 1 => {
- object: 'Ticket',
- type: 'create',
- object_id: 123,
- article_id: 123,
- user_id: 123,
- created_at: Time.zone.now,
- },
- 9 => {
- object: 'Ticket',
- type: 'update',
- object_id: 123,
- changes: {
- attribute1: [before, now],
- attribute2: [before, now],
- },
- user_id: 123,
- created_at: Time.zone.now,
- },
- },
- }
- result = {
- 'Ticket' =>
- 9 => {
- object: 'Ticket',
- type: 'update',
- object_id: 123,
- article_id: 123,
- changes: {
- attribute1: [before, now],
- attribute2: [before, now],
- },
- user_id: 123,
- created_at: Time.zone.now,
- },
- },
- }
- =end
- def self.get_uniq_changes(events)
- list_objects = {}
- events.each do |event|
- # simulate article create as ticket update
- article = nil
- if event[:object] == 'Ticket::Article'
- article = Ticket::Article.find_by(id: event[:id])
- next if !article
- next if event[:type] == 'update'
- # set new event infos
- ticket = Ticket.find_by(id: article.ticket_id)
- event[:object] = 'Ticket'
- event[:id] = ticket.id
- event[:type] = 'update'
- event[:changes] = nil
- end
- # get current state of objects
- object = event[:object].constantize.find_by(id: event[:id])
- # next if object is already deleted
- next if !object
- if !list_objects[event[:object]]
- list_objects[event[:object]] = {}
- end
- if !list_objects[event[:object]][object.id]
- list_objects[event[:object]][object.id] = {}
- end
- store = list_objects[event[:object]][object.id]
- store[:object] = event[:object]
- store[:object_id] = object.id
- store[:user_id] = event[:user_id]
- store[:created_at] = event[:created_at]
- if !store[:type] || store[:type] == 'update'
- store[:type] = event[:type]
- end
- # merge changes
- if event[:changes]
- if store[:changes]
- event[:changes].each do |key, value|
- if store[:changes][key]
- store[:changes][key][1] = value[1]
- else
- store[:changes][key] = value
- end
- end
- else
- store[:changes] = event[:changes]
- end
- end
- # remember article id if exists
- if article
- store[:article_id] = article.id
- end
- end
- list_objects
- end
- # Used as ActiveRecord lifecycle callback on the class.
- def self.after_create(record)
- # return if we run import mode
- return true if Setting.get('import_mode')
- e = {
- object: record.class.name,
- type: 'create',
- data: record,
- id: record.id,
- user_id: record.created_by_id,
- created_at: Time.zone.now,
- }
- EventBuffer.add('transaction', e)
- true
- end
- # Used as ActiveRecord lifecycle callback on the class.
- def self.before_update(record)
- # return if we run import mode
- return true if Setting.get('import_mode')
- # ignore certain attributes
- real_changes = {}
- record.changes_to_save.each do |key, value|
- next if key == 'updated_at'
- next if key == 'article_count'
- next if key == 'create_article_type_id'
- next if key == 'create_article_sender_id'
- real_changes[key] = value
- end
- # do not send anything if nothing has changed
- return true if real_changes.blank?
- changed_by_id = if record.respond_to?(:updated_by_id)
- record.updated_by_id
- else
- record.created_by_id
- end
- e = {
- object: record.class.name,
- type: 'update',
- data: record,
- changes: real_changes,
- id: record.id,
- user_id: changed_by_id,
- created_at: Time.zone.now,
- }
- EventBuffer.add('transaction', e)
- true
- end
- end
|