feat: account onboarding with clearbit (#8857)

* feat: add clearbit lookup

* chore: fix typo in .env.example

* refactor: split lookup to reduce cognitive complexity

* feat: add more fields to lookup

* feat: extend accounts controller

* feat: save extra data to custom_attributes

* feat: allow v2 update with custom_attributes

* feat: add update route

* refactor: reduce complexity

* feat: move update to v1 controller

* test: add locale test

* feat: remove update from routes

* test: update API for custom attributes

* test: all custom attributes

* fix: v2 tests

* test: enterprise accounts controller

* fix: clearbit payload

* fix: with modified env

* feat: allow custom attributes updates to profile

* refactor: reduce complexity

* feat: allow clearbit api key in installation config

* refactor: move clearbit to internal

* feat: allow clearbit

* chore: add display_title for June

* feat: allow more internal options

* refactor: use globalconfig to fetch clearbit token

* test: move response body to a factory

* refactor: update ops

* chore: remove clearbit from .env.example

* chore: apply suggestions from code review

Co-authored-by: sojan-official <sojan@chatwoot.com>

---------

Co-authored-by: sojan-official <sojan@chatwoot.com>
This commit is contained in:
Shivam Mishra
2024-02-12 23:21:42 +05:30
committed by GitHub
parent fc6a22b072
commit 657843960c
14 changed files with 367 additions and 33 deletions

View File

@@ -0,0 +1,35 @@
module Enterprise::Api::V2::AccountsController
private
def fetch_account_and_user_info
data = fetch_from_clearbit
return if data.blank?
update_user_info(data)
update_account_info(data)
end
def fetch_from_clearbit
Enterprise::ClearbitLookupService.lookup(@user.email)
rescue StandardError => e
Rails.logger.error "Error fetching data from clearbit: #{e}"
nil
end
def update_user_info(data)
@user.update!(name: data[:name])
end
def update_account_info(data)
@account.update!(
name: data[:company_name],
custom_attributes: @account.custom_attributes.merge(
'industry' => data[:industry],
'company_size' => data[:company_size],
'timezone' => data[:timezone],
'logo' => data[:logo]
)
)
end
end

View File

@@ -6,20 +6,30 @@ module Enterprise::SuperAdmin::AppConfigsController
case @config
when 'custom_branding'
@allowed_configs = %w[
LOGO_THUMBNAIL
LOGO
LOGO_DARK
BRAND_NAME
INSTALLATION_NAME
BRAND_URL
WIDGET_BRAND_URL
TERMS_URL
PRIVACY_URL
DISPLAY_MANIFEST
]
@allowed_configs = custom_branding_options
when 'internal'
@allowed_configs = internal_config_options
else
super
end
end
def custom_branding_options
%w[
LOGO_THUMBNAIL
LOGO
LOGO_DARK
BRAND_NAME
INSTALLATION_NAME
BRAND_URL
WIDGET_BRAND_URL
TERMS_URL
PRIVACY_URL
DISPLAY_MANIFEST
]
end
def internal_config_options
%w[CHATWOOT_INBOX_TOKEN CHATWOOT_INBOX_HMAC_KEY ANALYTICS_TOKEN CLEARBIT_API_KEY]
end
end

View File

@@ -0,0 +1,92 @@
# The Enterprise::ClearbitLookupService class is responsible for interacting with the Clearbit API.
# It provides methods to lookup a person's information using their email.
# Clearbit API documentation: {https://dashboard.clearbit.com/docs?ruby#api-reference}
# We use the combined API which returns both the person and comapnies together
# Combined API: {https://dashboard.clearbit.com/docs?ruby=#enrichment-api-combined-api}
# Persons API: {https://dashboard.clearbit.com/docs?ruby=#enrichment-api-person-api}
# Companies API: {https://dashboard.clearbit.com/docs?ruby=#enrichment-api-company-api}
#
# Note: The Clearbit gem is not used in this service, since it is not longer maintained
# GitHub: {https://github.com/clearbit/clearbit-ruby}
#
# @example
# Enterprise::ClearbitLookupService.lookup('test@example.com')
class Enterprise::ClearbitLookupService
# Clearbit API endpoint for combined lookup
CLEARBIT_ENDPOINT = 'https://person.clearbit.com/v2/combined/find'.freeze
# Performs a lookup on the Clearbit API using the provided email.
#
# @param email [String] The email address to lookup.
# @return [Hash, nil] A hash containing the person's full name, company name, and company timezone, or nil if an error occurs.
def self.lookup(email)
return nil unless clearbit_enabled?
response = perform_request(email)
process_response(response)
rescue StandardError => e
Rails.logger.error "[ClearbitLookup] #{e.message}"
nil
end
# Performs a request to the Clearbit API using the provided email.
#
# @param email [String] The email address to lookup.
# @return [HTTParty::Response] The response from the Clearbit API.
def self.perform_request(email)
options = {
headers: { 'Authorization' => "Bearer #{clearbit_token}" },
query: { email: email }
}
HTTParty.get(CLEARBIT_ENDPOINT, options)
end
# Handles an error response from the Clearbit API.
#
# @param response [HTTParty::Response] The response from the Clearbit API.
# @return [nil] Always returns nil.
def self.handle_error(response)
Rails.logger.error "[ClearbitLookup] API Error: #{response.message} (Status: #{response.code})"
nil
end
# Checks if Clearbit is enabled by checking for the presence of the CLEARBIT_API_KEY environment variable.
#
# @return [Boolean] True if Clearbit is enabled, false otherwise.
def self.clearbit_enabled?
clearbit_token.present?
end
def self.clearbit_token
GlobalConfigService.load('CLEARBIT_API_KEY', '')
end
# Processes the response from the Clearbit API.
#
# @param response [HTTParty::Response] The response from the Clearbit API.
# @return [Hash, nil] A hash containing the person's full name, company name, and company timezone, or nil if an error occurs.
def self.process_response(response)
return handle_error(response) unless response.success?
format_response(response)
end
# Formats the response data from the Clearbit API.
#
# @param data [Hash] The raw data from the Clearbit API.
# @return [Hash] A hash containing the person's full name, company name, and company timezone.
def self.format_response(response)
data = response.parsed_response
{
name: data.dig('person', 'name', 'fullName'),
avatar: data.dig('person', 'avatar'),
company_name: data.dig('company', 'name'),
timezone: data.dig('company', 'timeZone'),
logo: data.dig('company', 'logo'),
industry: data.dig('company', 'category', 'industry'),
company_size: data.dig('company', 'metrics', 'employees')
}
end
end