clearbit_enrichment.rb 8.8 KB

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