clearbit_enrichment.rb 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
  2. class Transaction::ClearbitEnrichment
  3. =begin
  4. {
  5. object: 'User',
  6. type: 'create',
  7. object_id: 123,
  8. changes: {
  9. 'attribute1' => [before, now],
  10. 'attribute2' => [before, now],
  11. }
  12. user_id: 123,
  13. },
  14. =end
  15. def initialize(item, params = {})
  16. @item = item
  17. @params = params
  18. end
  19. def perform
  20. # return if we run import mode
  21. return if Setting.get('import_mode')
  22. return if @item[:object] != 'User'
  23. return if @item[:type] != 'create'
  24. return if !Setting.get('clearbit_integration')
  25. config = Setting.get('clearbit_config')
  26. return if !config
  27. return if config['api_key'].empty?
  28. user = User.lookup(id: @item[:object_id])
  29. return if !user
  30. Transaction::ClearbitEnrichment.sync_user(user)
  31. end
  32. def self.sync
  33. users = User.of_role('Customer')
  34. users.each {|user|
  35. sync_user(user)
  36. }
  37. end
  38. def self.sync_user(user)
  39. UserInfo.current_user_id = 1
  40. return if user.email.empty?
  41. data = fetch(user.email)
  42. #p 'OO: ' + data.inspect
  43. return if !data
  44. config = Setting.get('clearbit_config')
  45. return if !config
  46. # get new user sync attributes
  47. user_sync = config['user_sync']
  48. user_sync_values = {}
  49. if user_sync
  50. user_sync.each {|callback, destination|
  51. next if !user_sync_values[destination].empty?
  52. value = _replace(callback, data)
  53. next if !value
  54. user_sync_values[destination] = value
  55. }
  56. end
  57. # get new organization sync attributes
  58. organization_sync = config['organization_sync']
  59. organization_sync_values = {}
  60. if organization_sync
  61. organization_sync.each {|callback, destination|
  62. next if !organization_sync_values[destination].empty?
  63. value = _replace(callback, data)
  64. next if !value
  65. organization_sync_values[destination] = value
  66. }
  67. end
  68. # get latest user synced attributes
  69. external_syn_user = nil
  70. user_sync_values_last_time = {}
  71. if data && data['person'] && data['person']['id']
  72. external_syn_user = ExternalSync.find_by(
  73. source: 'clearbit',
  74. source_id: data['person']['id'],
  75. object: 'User',
  76. o_id: user.id,
  77. )
  78. if external_syn_user && external_syn_user.last_payload
  79. user_sync.each {|callback, destination|
  80. next if !user_sync_values_last_time[destination].empty?
  81. value = _replace(callback, external_syn_user.last_payload)
  82. next if !value
  83. user_sync_values_last_time[destination] = value
  84. }
  85. end
  86. end
  87. # if person record exists
  88. user_has_changed = false
  89. user_sync_values.each {|destination, value|
  90. attribute = destination.sub(/^user\./, '')
  91. next if user[attribute] == value
  92. next if !user[attribute].empty? && user_sync_values_last_time[destination] != user[attribute]
  93. begin
  94. user[attribute] = value
  95. rescue => e
  96. Rails.logger.error "ERROR: Unable to assign user.#{attribute}: #{e.inspect}"
  97. end
  98. user_has_changed = true
  99. }
  100. if user_has_changed
  101. user.updated_by_id = 1
  102. if data['person'] && data['person']['id']
  103. if external_syn_user
  104. external_syn_user.last_payload = data
  105. external_syn_user.save
  106. else
  107. external_syn_user = ExternalSync.create(
  108. source: 'clearbit',
  109. source_id: data['person']['id'],
  110. object: 'User',
  111. o_id: user.id,
  112. last_payload: data,
  113. )
  114. end
  115. end
  116. end
  117. # if no company record exists or no organization should be created
  118. if !data['company'] || config['organization_autocreate'] != true
  119. if user_has_changed
  120. user.save
  121. end
  122. Observer::Transaction.commit
  123. return
  124. end
  125. # if company record exists
  126. external_syn_organization = ExternalSync.find_by(
  127. source: 'clearbit',
  128. source_id: data['company']['id'],
  129. )
  130. # create new organization
  131. if !external_syn_organization
  132. # if organization is already assigned, do not create a new one
  133. if user.organization_id
  134. if user_has_changed
  135. user.save
  136. Observer::Transaction.commit
  137. end
  138. return
  139. end
  140. # can't create organization without name
  141. if organization_sync_values['organization.name'].empty?
  142. Observer::Transaction.commit
  143. return
  144. end
  145. # find by name
  146. organization = Organization.find_by(name: organization_sync_values['organization.name'])
  147. # create new organization
  148. if !organization
  149. organization = Organization.new(
  150. shared: config['organization_shared'],
  151. )
  152. organization_sync_values.each {|destination, value|
  153. attribute = destination.sub(/^organization\./, '')
  154. next if !organization[attribute].empty?
  155. begin
  156. organization[attribute] = value
  157. rescue => e
  158. Rails.logger.error "ERROR: Unable to assign organization.#{attribute}: #{e.inspect}"
  159. end
  160. }
  161. organization.save
  162. end
  163. ExternalSync.create(
  164. source: 'clearbit',
  165. source_id: data['company']['id'],
  166. object: 'Organization',
  167. o_id: organization.id,
  168. last_payload: data,
  169. )
  170. # assign new organization to user
  171. if !user.organization_id
  172. user.organization_id = organization.id
  173. user.save
  174. end
  175. Observer::Transaction.commit
  176. return
  177. end
  178. # get latest organization synced attributes
  179. organization_sync_values_last_time = {}
  180. if external_syn_organization && external_syn_organization.last_payload
  181. organization_sync.each {|callback, destination|
  182. next if !organization_sync_values_last_time[destination].empty?
  183. value = _replace(callback, external_syn_organization.last_payload)
  184. next if !value
  185. organization_sync_values_last_time[destination] = value
  186. }
  187. end
  188. # update existing organization
  189. organization = Organization.find(external_syn_organization[:o_id])
  190. organization_has_changed = false
  191. organization_sync_values.each {|destination, value|
  192. attribute = destination.sub(/^organization\./, '')
  193. next if organization[attribute] == value
  194. next if !organization[attribute].empty? && organization_sync_values_last_time[destination] != organization[attribute]
  195. begin
  196. organization[attribute] = value
  197. rescue => e
  198. Rails.logger.error "ERROR: Unable to assign organization.#{attribute}: #{e.inspect}"
  199. end
  200. organization_has_changed = true
  201. }
  202. if organization_has_changed
  203. organization.updated_by_id = 1
  204. organization.save
  205. external_syn_organization.last_payload = data
  206. external_syn_organization.save
  207. end
  208. # assign new organization to user
  209. if !user.organization_id
  210. user_has_changed = true
  211. user.organization_id = organization.id
  212. end
  213. if user_has_changed
  214. user.save
  215. end
  216. Observer::Transaction.commit
  217. true
  218. end
  219. def self._replace(callback, data)
  220. object_name = nil
  221. object_method = nil
  222. placeholder = nil
  223. if callback =~ /\A ( [\w]+ )\.( [\w\.]+ ) \z/x
  224. object_name = $1
  225. object_method = $2
  226. end
  227. return if !data
  228. return if !data[object_name]
  229. # do validaton, ignore some methodes
  230. if callback =~ /(`|\.(|\s*)(save|destroy|delete|remove|drop|update\(|update_att|create\(|new|all|where|find))/i
  231. placeholder = "#{callback} (not allowed)"
  232. # get value based on object_name and object_method
  233. elsif object_name && object_method
  234. object_refs = data[object_name]
  235. object_methods = object_method.split('.')
  236. object_methods_s = ''
  237. object_methods.each {|method|
  238. if object_methods_s != ''
  239. object_methods_s += '.'
  240. end
  241. object_methods_s += method
  242. # if method exists
  243. break if !object_refs.respond_to?(method.to_sym) && !object_refs[method]
  244. object_refs = if object_refs.respond_to?(method.to_sym)
  245. object_refs.send(method.to_sym)
  246. else
  247. object_refs[method]
  248. end
  249. }
  250. if object_refs.class == String
  251. placeholder = object_refs
  252. end
  253. end
  254. placeholder
  255. end
  256. def self.fetch(email)
  257. if !Rails.env.production?
  258. filename = "#{Rails.root}/test/fixtures/clearbit/#{email}.json"
  259. if File.exist?(filename)
  260. data = IO.binread(filename)
  261. return JSON.parse(data) if data
  262. end
  263. end
  264. config = Setting.get('clearbit_config')
  265. return if !config
  266. return if config['api_key'].empty?
  267. record = {
  268. direction: 'out',
  269. facility: 'clearbit',
  270. url: "clearbit -> #{email}",
  271. status: 200,
  272. ip: nil,
  273. request: { content: email },
  274. response: {},
  275. method: 'GET',
  276. }
  277. begin
  278. Clearbit.key = config['api_key']
  279. result = Clearbit::Enrichment.find(email: email, stream: true)
  280. record[:response] = { code: 200, content: result.to_s }
  281. rescue => e
  282. record[:status] = 500
  283. record[:response] = { code: 500, content: e.inspect }
  284. end
  285. HttpLog.create(record)
  286. result
  287. end
  288. end