clearbit_enrichment.rb 9.4 KB

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