-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add hubspot connection services (#2597)
- Loading branch information
1 parent
76a0dbf
commit a66c402
Showing
9 changed files
with
407 additions
and
0 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
app/jobs/integrations/aggregator/send_private_app_token_job.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# frozen_string_literal: true | ||
|
||
module Integrations | ||
module Aggregator | ||
class SendPrivateAppTokenJob < ApplicationJob | ||
queue_as 'integrations' | ||
|
||
retry_on LagoHttpClient::HttpError, wait: :polynomially_longer, attempts: 3 | ||
|
||
def perform(integration:) | ||
result = Integrations::Aggregator::SendPrivateAppTokenService.call(integration:) | ||
result.raise_if_error! | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
app/services/integrations/aggregator/send_private_app_token_service.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# frozen_string_literal: true | ||
|
||
module Integrations | ||
module Aggregator | ||
class SendPrivateAppTokenService < BaseService | ||
def action_path | ||
"connection/#{integration.connection_id}/metadata" | ||
end | ||
|
||
def call | ||
return unless integration.type == 'Integrations::HubspotIntegration' | ||
return unless integration.private_app_token | ||
|
||
payload = { | ||
privateAppToken: integration.private_app_token | ||
} | ||
|
||
response = http_client.post_with_response(payload, headers) | ||
result.response = response | ||
|
||
result | ||
end | ||
|
||
private | ||
|
||
def headers | ||
{ | ||
'Provider-Config-Key' => 'hubspot', | ||
'Authorization' => "Bearer #{secret_key}" | ||
} | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
|
||
module Integrations | ||
module Hubspot | ||
class CreateService < BaseService | ||
attr_reader :params | ||
def initialize(params:) | ||
@params = params | ||
|
||
super | ||
end | ||
|
||
def call | ||
organization = Organization.find_by(id: params[:organization_id]) | ||
|
||
unless organization.premium_integrations.include?('hubspot') | ||
return result.not_allowed_failure!(code: 'premium_integration_missing') | ||
end | ||
|
||
integration = Integrations::HubspotIntegration.new( | ||
organization:, | ||
name: params[:name], | ||
code: params[:code], | ||
connection_id: params[:connection_id], | ||
private_app_token: params[:private_app_token], | ||
default_targeted_object: params[:default_targeted_object], | ||
sync_invoices: ActiveModel::Type::Boolean.new.cast(params[:sync_invoices]), | ||
sync_subscriptions: ActiveModel::Type::Boolean.new.cast(params[:sync_subscriptions]) | ||
) | ||
|
||
integration.save! | ||
|
||
if integration.type == 'Integrations::HubspotIntegration' | ||
Integrations::Aggregator::SendPrivateAppTokenJob.perform_later(integration:) | ||
end | ||
|
||
result.integration = integration | ||
result | ||
rescue ActiveRecord::RecordInvalid => e | ||
result.record_validation_failure!(record: e.record) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# frozen_string_literal: true | ||
|
||
module Integrations | ||
module Hubspot | ||
class UpdateService < BaseService | ||
def initialize(integration:, params:) | ||
@integration = integration | ||
@params = params | ||
|
||
super | ||
end | ||
|
||
def call | ||
return result.not_found_failure!(resource: 'integration') unless integration | ||
|
||
unless integration.organization.premium_integrations.include?('hubspot') | ||
return result.not_allowed_failure!(code: 'premium_integration_missing') | ||
end | ||
|
||
old_private_app_token = integration.private_app_token | ||
|
||
integration.name = params[:name] if params.key?(:name) | ||
integration.code = params[:code] if params.key?(:code) | ||
integration.private_app_token = params[:private_app_token] if params.key?(:private_app_token) | ||
integration.default_targeted_object = params[:default_targeted_object] if params.key?(:default_targeted_object) | ||
integration.sync_invoices = params[:sync_invoices] if params.key?(:sync_invoices) | ||
integration.sync_subscriptions = params[:sync_subscriptions] if params.key?(:sync_subscriptions) | ||
|
||
integration.save! | ||
|
||
if integration.type == 'Integrations::HubspotIntegration' && integration.private_app_token != old_private_app_token | ||
Integrations::Aggregator::SendPrivateAppTokenJob.perform_later(integration:) | ||
end | ||
|
||
result.integration = integration | ||
result | ||
rescue ActiveRecord::RecordInvalid => e | ||
result.record_validation_failure!(record: e.record) | ||
end | ||
|
||
private | ||
|
||
attr_reader :integration, :params | ||
end | ||
end | ||
end |
25 changes: 25 additions & 0 deletions
25
spec/jobs/integrations/aggregator/send_private_app_token_job_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe Integrations::Aggregator::SendPrivateAppTokenJob, type: :job do | ||
describe '#perform' do | ||
subject(:send_token_job) { described_class } | ||
|
||
let(:send_token_service) { instance_double(Integrations::Aggregator::SendPrivateAppTokenService) } | ||
let(:integration) { create(:hubspot_integration) } | ||
let(:result) { BaseService::Result.new } | ||
|
||
before do | ||
allow(Integrations::Aggregator::SendPrivateAppTokenService).to receive(:new).and_return(send_token_service) | ||
allow(send_token_service).to receive(:call).and_return(result) | ||
end | ||
|
||
it 'sends the private app token the nango' do | ||
described_class.perform_now(integration:) | ||
|
||
expect(Integrations::Aggregator::SendPrivateAppTokenService).to have_received(:new) | ||
expect(send_token_service).to have_received(:call) | ||
end | ||
end | ||
end |
36 changes: 36 additions & 0 deletions
36
spec/services/integrations/aggregator/send_private_app_token_service_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe Integrations::Aggregator::SendPrivateAppTokenService do | ||
subject(:send_private_token_service) { described_class.new(integration:) } | ||
|
||
let(:integration) { create(:hubspot_integration) } | ||
|
||
describe '.call' do | ||
let(:lago_client) { instance_double(LagoHttpClient::Client) } | ||
let(:endpoint) { "https://api.nango.dev/connection/#{integration.connection_id}/metadata" } | ||
|
||
before do | ||
allow(LagoHttpClient::Client).to receive(:new) | ||
.with(endpoint) | ||
.and_return(lago_client) | ||
allow(lago_client).to receive(:post_with_response) | ||
|
||
integration.private_app_token = 'privatetoken' | ||
integration.save! | ||
end | ||
|
||
it 'successfully sends token to nango' do | ||
send_private_token_service.call | ||
|
||
aggregate_failures do | ||
expect(LagoHttpClient::Client).to have_received(:new) | ||
.with(endpoint) | ||
expect(lago_client).to have_received(:post_with_response) do |payload| | ||
expect(payload[:privateAppToken]).to eq('privatetoken') | ||
end | ||
end | ||
end | ||
end | ||
end |
109 changes: 109 additions & 0 deletions
109
spec/services/integrations/hubspot/create_service_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe Integrations::Hubspot::CreateService, type: :service do | ||
let(:membership) { create(:membership) } | ||
let(:organization) { membership.organization } | ||
|
||
describe '#call' do | ||
subject(:service_call) { described_class.call(params: create_args) } | ||
|
||
let(:name) { 'Hubspot 1' } | ||
let(:script_endpoint_url) { Faker::Internet.url } | ||
|
||
let(:create_args) do | ||
{ | ||
name:, | ||
code: 'hubspot1', | ||
organization_id: organization.id, | ||
connection_id: 'conn1', | ||
private_app_token: 'token', | ||
client_secret: 'secret', | ||
default_targeted_object: "test", | ||
sync_invoices: false, | ||
sync_subscriptions: false | ||
} | ||
end | ||
|
||
context 'without premium license' do | ||
it 'does not create an integration' do | ||
expect { service_call }.not_to change(Integrations::HubspotIntegration, :count) | ||
end | ||
|
||
it 'returns an error' do | ||
result = service_call | ||
|
||
aggregate_failures do | ||
expect(result).not_to be_success | ||
expect(result.error).to be_a(BaseService::MethodNotAllowedFailure) | ||
end | ||
end | ||
end | ||
|
||
context 'with premium license' do | ||
around { |test| lago_premium!(&test) } | ||
|
||
context 'with hubspot premium integration not present' do | ||
it 'returns an error' do | ||
result = service_call | ||
|
||
aggregate_failures do | ||
expect(result).not_to be_success | ||
expect(result.error).to be_a(BaseService::MethodNotAllowedFailure) | ||
end | ||
end | ||
end | ||
|
||
context 'with hubspot premium integration present' do | ||
before do | ||
organization.update!(premium_integrations: ['hubspot']) | ||
allow(Integrations::Aggregator::SendPrivateAppTokenJob).to receive(:perform_later) | ||
end | ||
|
||
context 'without validation errors' do | ||
it 'creates an integration' do | ||
expect { service_call }.to change(Integrations::HubspotIntegration, :count).by(1) | ||
|
||
integration = Integrations::HubspotIntegration.order(:created_at).last | ||
expect(integration.name).to eq(name) | ||
expect(integration.code).to eq(create_args[:code]) | ||
expect(integration.connection_id).to eq(create_args[:connection_id]) | ||
expect(integration.private_app_token).to eq(create_args[:private_app_token]) | ||
expect(integration.default_targeted_object).to eq(create_args[:default_targeted_object]) | ||
expect(integration.sync_invoices).to eq(create_args[:sync_invoices]) | ||
expect(integration.sync_subscriptions).to eq(create_args[:sync_subscriptions]) | ||
expect(integration.organization_id).to eq(organization.id) | ||
end | ||
|
||
it 'returns an integration in result object' do | ||
result = service_call | ||
|
||
expect(result.integration).to be_a(Integrations::HubspotIntegration) | ||
end | ||
|
||
it 'calls Integrations::Aggregator::SendPrivateAppTokenJob' do | ||
service_call | ||
|
||
integration = Integrations::HubspotIntegration.order(:created_at).last | ||
expect(Integrations::Aggregator::SendPrivateAppTokenJob).to have_received(:perform_later).with(integration:) | ||
end | ||
end | ||
|
||
context 'with validation error' do | ||
let(:name) { nil } | ||
|
||
it 'returns an error' do | ||
result = service_call | ||
|
||
aggregate_failures do | ||
expect(result).not_to be_success | ||
expect(result.error).to be_a(BaseService::ValidationFailure) | ||
expect(result.error.messages[:name]).to eq(['value_is_mandatory']) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.