clearbit_enrichment.rb 9.4 KB

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