transaction_dispatcher.rb 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. class TransactionDispatcher
  3. def self.reset
  4. EventBuffer.reset('transaction')
  5. end
  6. def self.commit(params = {})
  7. # add attribute of interface handle (e. g. to send (no) notifications if a agent
  8. # is creating a ticket via application_server, but send it if it's created via
  9. # postmaster)
  10. params[:interface_handle] = ApplicationHandleInfo.current
  11. # execute object transactions
  12. TransactionDispatcher.perform(params)
  13. end
  14. def self.perform(params)
  15. # return if we run import mode
  16. return if Setting.get('import_mode')
  17. # get buffer
  18. list = EventBuffer.list('transaction')
  19. # reset buffer
  20. EventBuffer.reset('transaction')
  21. # get async backends
  22. sync_backends = []
  23. Setting.where(area: 'Transaction::Backend::Sync').reorder(:name).each do |setting|
  24. backend = Setting.get(setting.name)
  25. next if params[:disable]&.include?(backend)
  26. sync_backends.push backend.constantize
  27. end
  28. # get uniq objects
  29. list_objects = get_uniq_changes(list)
  30. list_objects.each_value do |objects|
  31. objects.each_value do |item|
  32. # execute sync backends
  33. sync_backends.each do |backend|
  34. execute_single_backend(backend, item, params)
  35. end
  36. # execute async backends
  37. TransactionJob.perform_later(item, params)
  38. end
  39. end
  40. end
  41. def self.execute_single_backend(backend, item, params)
  42. Rails.logger.debug { "Execute single backend #{backend}" }
  43. begin
  44. UserInfo.current_user_id = nil
  45. integration = backend.new(item, params)
  46. integration.perform
  47. rescue => e
  48. Rails.logger.error e
  49. end
  50. end
  51. =begin
  52. result = get_uniq_changes(events)
  53. result = {
  54. 'Ticket' =>
  55. 1 => {
  56. object: 'Ticket',
  57. type: 'create',
  58. object_id: 123,
  59. article_id: 123,
  60. user_id: 123,
  61. created_at: Time.zone.now,
  62. },
  63. 9 => {
  64. object: 'Ticket',
  65. type: 'update',
  66. object_id: 123,
  67. changes: {
  68. attribute1: [before, now],
  69. attribute2: [before, now],
  70. },
  71. user_id: 123,
  72. created_at: Time.zone.now,
  73. },
  74. },
  75. }
  76. result = {
  77. 'Ticket' =>
  78. 9 => {
  79. object: 'Ticket',
  80. type: 'update',
  81. object_id: 123,
  82. article_id: 123,
  83. changes: {
  84. attribute1: [before, now],
  85. attribute2: [before, now],
  86. },
  87. user_id: 123,
  88. created_at: Time.zone.now,
  89. },
  90. },
  91. }
  92. =end
  93. def self.get_uniq_changes(events)
  94. list_objects = {}
  95. events.each do |event|
  96. # simulate article create as ticket update
  97. article = nil
  98. if event[:object] == 'Ticket::Article'
  99. article = Ticket::Article.find_by(id: event[:id])
  100. next if !article
  101. next if event[:type] == 'update'
  102. # set new event infos
  103. ticket = Ticket.find_by(id: article.ticket_id)
  104. event[:object] = 'Ticket'
  105. event[:id] = ticket.id
  106. event[:type] = 'update'
  107. event[:changes] = nil
  108. end
  109. # get current state of objects
  110. object = event[:object].constantize.find_by(id: event[:id])
  111. # next if object is already deleted
  112. next if !object
  113. if !list_objects[event[:object]]
  114. list_objects[event[:object]] = {}
  115. end
  116. if !list_objects[event[:object]][object.id]
  117. list_objects[event[:object]][object.id] = {}
  118. end
  119. store = list_objects[event[:object]][object.id]
  120. store[:object] = event[:object]
  121. store[:object_id] = object.id
  122. store[:user_id] = event[:user_id]
  123. store[:created_at] = event[:created_at]
  124. if !store[:type] || store[:type] == 'update'
  125. store[:type] = event[:type]
  126. end
  127. # merge changes
  128. if event[:changes]
  129. if store[:changes]
  130. event[:changes].each do |key, value|
  131. if store[:changes][key]
  132. store[:changes][key][1] = value[1]
  133. else
  134. store[:changes][key] = value
  135. end
  136. end
  137. else
  138. store[:changes] = event[:changes]
  139. end
  140. end
  141. # remember article id if exists
  142. if article
  143. store[:article_id] = article.id
  144. end
  145. end
  146. list_objects
  147. end
  148. # Used as ActiveRecord lifecycle callback on the class.
  149. def self.after_create(record)
  150. # return if we run import mode
  151. return true if Setting.get('import_mode')
  152. e = {
  153. object: record.class.name,
  154. type: 'create',
  155. data: record,
  156. id: record.id,
  157. user_id: record.created_by_id,
  158. created_at: Time.zone.now,
  159. }
  160. EventBuffer.add('transaction', e)
  161. true
  162. end
  163. # Used as ActiveRecord lifecycle callback on the class.
  164. def self.before_update(record)
  165. # return if we run import mode
  166. return true if Setting.get('import_mode')
  167. # ignore certain attributes
  168. real_changes = {}
  169. record.changes_to_save.each do |key, value|
  170. next if key == 'updated_at'
  171. next if key == 'article_count'
  172. next if key == 'create_article_type_id'
  173. next if key == 'create_article_sender_id'
  174. real_changes[key] = value
  175. end
  176. # do not send anything if nothing has changed
  177. return true if real_changes.blank?
  178. changed_by_id = if record.respond_to?(:updated_by_id)
  179. record.updated_by_id
  180. else
  181. record.created_by_id
  182. end
  183. e = {
  184. object: record.class.name,
  185. type: 'update',
  186. data: record,
  187. changes: real_changes,
  188. id: record.id,
  189. user_id: changed_by_id,
  190. created_at: Time.zone.now,
  191. }
  192. EventBuffer.add('transaction', e)
  193. true
  194. end
  195. end