mirror of https://github.com/docusealco/docuseal
				
				
				
			
						commit
						a317c030a1
					
				| @ -0,0 +1,43 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | require 'faraday' | ||||||
|  | 
 | ||||||
|  | class ExportService | ||||||
|  |   attr_reader :error_message | ||||||
|  | 
 | ||||||
|  |   def initialize | ||||||
|  |     @error_message = nil | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   protected | ||||||
|  | 
 | ||||||
|  |   def api_connection | ||||||
|  |     @api_connection ||= Faraday.new(url: ExportLocation.default_location.api_base_url) do |faraday| | ||||||
|  |       faraday.request :json | ||||||
|  |       faraday.response :json | ||||||
|  |       faraday.adapter Faraday.default_adapter | ||||||
|  |     end | ||||||
|  |   rescue StandardError => e | ||||||
|  |     Rails.logger.error("Failed to create API connection: #{e.message}") | ||||||
|  |     Rollbar.error(e) if defined?(Rollbar) | ||||||
|  |     nil | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def post_to_api(data, endpoint, extra_params = nil) | ||||||
|  |     connection = api_connection | ||||||
|  |     return nil unless connection | ||||||
|  | 
 | ||||||
|  |     connection.post(endpoint) do |req| | ||||||
|  |       data = data.merge(extra_params) if extra_params.present? && data.is_a?(Hash) | ||||||
|  |       req.body = data.is_a?(String) ? data : data.to_json | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def export_location | ||||||
|  |     @export_location ||= ExportLocation.default_location | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def set_error(message) | ||||||
|  |     @error_message = message | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,47 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class ExportSubmissionService < ExportService | ||||||
|  |   attr_reader :submission | ||||||
|  | 
 | ||||||
|  |   def initialize(submission) | ||||||
|  |     super() | ||||||
|  |     @submission = submission | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def call | ||||||
|  |     unless export_location&.submissions_endpoint.present? | ||||||
|  |       set_error('Export failed: Submission export endpoint is not configured.') | ||||||
|  |       return false | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     payload = build_payload | ||||||
|  |     response = post_to_api(payload, export_location.submissions_endpoint, export_location.extra_params) | ||||||
|  | 
 | ||||||
|  |     if response&.success? | ||||||
|  |       true | ||||||
|  |     else | ||||||
|  |       set_error("Failed to export submission ##{submission.id} events.") | ||||||
|  |       false | ||||||
|  |     end | ||||||
|  |   rescue Faraday::Error => e | ||||||
|  |     Rails.logger.error("Failed to export submission Faraday: #{e.message}") | ||||||
|  |     Rollbar.error("Failed to export submission: #{e.message}") if defined?(Rollbar) | ||||||
|  |     set_error("Network error occurred during export: #{e.message}") | ||||||
|  |     false | ||||||
|  |   rescue StandardError => e | ||||||
|  |     Rails.logger.error("Failed to export submission: #{e.message}") | ||||||
|  |     Rollbar.error(e) if defined?(Rollbar) | ||||||
|  |     set_error("An unexpected error occurred during export: #{e.message}") | ||||||
|  |     false | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   private | ||||||
|  | 
 | ||||||
|  |   def build_payload | ||||||
|  |     { | ||||||
|  |       submission_id: submission.id, | ||||||
|  |       template_name: submission.template&.name, | ||||||
|  |       events: submission.submission_events.order(updated_at: :desc).limit(1) | ||||||
|  |     } | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class ExportTemplateService < ExportService | ||||||
|  |   def initialize(data) | ||||||
|  |     super() | ||||||
|  |     @data = data | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def call | ||||||
|  |     response = post_to_api(@data, export_location.templates_endpoint, export_location.extra_params) | ||||||
|  | 
 | ||||||
|  |     if response&.success? | ||||||
|  |       Rails.logger.info("Successfully exported template #{@data[:template][:name]} to #{export_location.name}") | ||||||
|  |       true | ||||||
|  |     else | ||||||
|  |       Rails.logger.error("Failed to export template to third party: #{response&.status}") | ||||||
|  |       Rollbar.error("#{export_location.name} template export API error: #{response&.status}") if defined?(Rollbar) | ||||||
|  |       set_error("Failed to export template to third party") | ||||||
|  |       false | ||||||
|  |     end | ||||||
|  |   rescue Faraday::Error => e | ||||||
|  |     Rails.logger.error("Failed to export template Faraday: #{e.message}") | ||||||
|  |     Rollbar.error("Failed to export template: #{e.message}") if defined?(Rollbar) | ||||||
|  |     set_error("Network error occurred during template export: #{e.message}") | ||||||
|  |     false | ||||||
|  |   rescue StandardError => e | ||||||
|  |     Rails.logger.error("Failed to export template: #{e.message}") | ||||||
|  |     Rollbar.error(e) if defined?(Rollbar) | ||||||
|  |     set_error("An unexpected error occurred during template export: #{e.message}") | ||||||
|  |     false | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | class AddSubmissionsEndpointToExportLocations < ActiveRecord::Migration[8.0] | ||||||
|  |   def change | ||||||
|  |     add_column :export_locations, :submissions_endpoint, :string | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | FactoryBot.define do | ||||||
|  |   factory :export_location do | ||||||
|  |     name { Faker::Company.name } | ||||||
|  |     api_base_url { 'https://api.example.com' } | ||||||
|  |     default_location { false } | ||||||
|  |     extra_params { {} } | ||||||
|  |     templates_endpoint { '/templates' } | ||||||
|  |     submissions_endpoint { nil } | ||||||
|  |     authorization_token { nil } | ||||||
|  | 
 | ||||||
|  |     trait :with_submissions_endpoint do | ||||||
|  |       submissions_endpoint { '/submissions' } | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     trait :with_authorization_token do | ||||||
|  |       authorization_token { SecureRandom.hex(32) } | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     trait :default do | ||||||
|  |       default_location { true } | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     trait :with_extra_params do | ||||||
|  |       extra_params { { 'api_key' => 'test_key', 'version' => '1.0' } } | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,200 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | RSpec.describe ExportSubmissionService do | ||||||
|  |   let(:account) { create(:account) } | ||||||
|  |   let(:user) { create(:user, account: account) } | ||||||
|  |   let(:template) { create(:template, account: account, author: user) } | ||||||
|  |   let(:submission) { create(:submission, template: template, account: account) } | ||||||
|  |   let(:export_location) { create(:export_location, :with_submissions_endpoint) } | ||||||
|  |   let(:service) { described_class.new(submission) } | ||||||
|  |   let(:faraday_connection) { instance_double(Faraday::Connection) } | ||||||
|  |   let(:faraday_response) { instance_double(Faraday::Response) } | ||||||
|  | 
 | ||||||
|  |   before do | ||||||
|  |     allow(ExportLocation).to receive(:default_location).and_return(export_location) | ||||||
|  |     allow(Faraday).to receive(:new).and_return(faraday_connection) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#call' do | ||||||
|  |     context 'when export location is not configured' do | ||||||
|  |       before do | ||||||
|  |         allow(ExportLocation).to receive(:default_location).and_return(nil) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('Export failed: Submission export endpoint is not configured.') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when export location has no submissions endpoint' do | ||||||
|  |       before do | ||||||
|  |         allow(export_location).to receive(:submissions_endpoint).and_return(nil) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('Export failed: Submission export endpoint is not configured.') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when export location is properly configured' do | ||||||
|  |       let(:request_double) { double('request', body: nil) } | ||||||
|  | 
 | ||||||
|  |       before do | ||||||
|  |         allow(request_double).to receive(:body=) | ||||||
|  |         allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       context 'when API request succeeds' do | ||||||
|  |         before do | ||||||
|  |           allow(faraday_response).to receive(:success?).and_return(true) | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         it 'returns true' do | ||||||
|  |           expect(service.call).to be true | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         it 'makes API call with correct endpoint' do | ||||||
|  |           expect(faraday_connection).to receive(:post).with(export_location.submissions_endpoint) | ||||||
|  |           service.call | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       context 'when API request fails' do | ||||||
|  |         before do | ||||||
|  |           allow(faraday_response).to receive(:success?).and_return(false) | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         it 'returns false and sets error message' do | ||||||
|  |           expect(service.call).to be false | ||||||
|  |           expect(service.error_message).to eq("Failed to export submission ##{submission.id} events.") | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       context 'when API response is nil' do | ||||||
|  |         before do | ||||||
|  |           allow(faraday_connection).to receive(:post).and_return(nil) | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         it 'returns false and sets error message' do | ||||||
|  |           expect(service.call).to be false | ||||||
|  |           expect(service.error_message).to eq("Failed to export submission ##{submission.id} events.") | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when Faraday error occurs' do | ||||||
|  |       before do | ||||||
|  |         allow(faraday_connection).to receive(:post).and_raise(Faraday::ConnectionFailed.new('Connection failed')) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets network error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('Network error occurred during export: Connection failed') | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'logs the error' do | ||||||
|  |         expect(Rails.logger).to receive(:error).with('Failed to export submission Faraday: Connection failed') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'reports to Rollbar if available' do | ||||||
|  |         stub_const('Rollbar', double) | ||||||
|  |         expect(Rollbar).to receive(:error).with('Failed to export submission: Connection failed') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when other standard error occurs' do | ||||||
|  |       before do | ||||||
|  |         allow(ExportLocation).to receive(:default_location).and_raise(StandardError.new('Database error')) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets generic error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('An unexpected error occurred during export: Database error') | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'logs the error' do | ||||||
|  |         expect(Rails.logger).to receive(:error).with('Failed to export submission: Database error') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'reports to Rollbar if available' do | ||||||
|  |         stub_const('Rollbar', double) | ||||||
|  |         error = StandardError.new('Database error') | ||||||
|  |         allow(ExportLocation).to receive(:default_location).and_raise(error) | ||||||
|  |         expect(Rollbar).to receive(:error).with(error) | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe 'payload building' do | ||||||
|  |     let(:request_double) { double('request', body: nil) } | ||||||
|  | 
 | ||||||
|  |     before do | ||||||
|  |       allow(request_double).to receive(:body=) | ||||||
|  |       allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response) | ||||||
|  |       allow(faraday_response).to receive(:success?).and_return(true) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'includes submission_id in payload' do | ||||||
|  |       expect(request_double).to receive(:body=) do |body| | ||||||
|  |         expect(JSON.parse(body)).to include('submission_id' => submission.id) | ||||||
|  |       end | ||||||
|  |       service.call | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'includes template_name in payload' do | ||||||
|  |       expect(request_double).to receive(:body=) do |body| | ||||||
|  |         expect(JSON.parse(body)).to include('template_name' => submission.template.name) | ||||||
|  |       end | ||||||
|  |       service.call | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'includes recent events in payload' do | ||||||
|  |       expect(request_double).to receive(:body=) do |body| | ||||||
|  |         parsed_body = JSON.parse(body) | ||||||
|  |         expect(parsed_body).to have_key('events') | ||||||
|  |       end | ||||||
|  |       service.call | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when template is nil' do | ||||||
|  |       before do | ||||||
|  |         allow(submission).to receive(:template).and_return(nil) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'includes nil template_name in payload' do | ||||||
|  |         expect(request_double).to receive(:body=) do |body| | ||||||
|  |           expect(JSON.parse(body)).to include('template_name' => nil) | ||||||
|  |         end | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe 'extra_params handling' do | ||||||
|  |     let(:extra_params) { { 'api_key' => 'test_key', 'version' => '1.0' } } | ||||||
|  |     let(:request_double) { double('request', body: nil) } | ||||||
|  | 
 | ||||||
|  |     before do | ||||||
|  |       allow(export_location).to receive(:extra_params).and_return(extra_params) | ||||||
|  |       allow(request_double).to receive(:body=) | ||||||
|  |       allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response) | ||||||
|  |       allow(faraday_response).to receive(:success?).and_return(true) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'merges extra_params into the payload' do | ||||||
|  |       expect(request_double).to receive(:body=) do |body| | ||||||
|  |         parsed_body = JSON.parse(body) | ||||||
|  |         expect(parsed_body).to include('api_key' => 'test_key', 'version' => '1.0') | ||||||
|  |       end | ||||||
|  |       service.call | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,158 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | RSpec.describe ExportTemplateService do | ||||||
|  |   let(:export_location) { create(:export_location, :default) } | ||||||
|  |   let(:data) { { template: { name: 'Test Template' } } } | ||||||
|  |   let(:service) { described_class.new(data) } | ||||||
|  |   let(:faraday_connection) { instance_double(Faraday::Connection) } | ||||||
|  |   let(:faraday_response) { instance_double(Faraday::Response) } | ||||||
|  | 
 | ||||||
|  |   before do | ||||||
|  |     allow(ExportLocation).to receive(:default_location).and_return(export_location) | ||||||
|  |     allow(Faraday).to receive(:new).and_return(faraday_connection) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#call' do | ||||||
|  |     let(:request_double) { double('request', body: nil) } | ||||||
|  | 
 | ||||||
|  |     before do | ||||||
|  |       allow(request_double).to receive(:body=) | ||||||
|  |       allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when API request succeeds' do | ||||||
|  |       before do | ||||||
|  |         allow(faraday_response).to receive(:success?).and_return(true) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns true' do | ||||||
|  |         expect(service.call).to be true | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'makes API call with correct endpoint' do | ||||||
|  |         expect(faraday_connection).to receive(:post).with(export_location.templates_endpoint) | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'logs success message' do | ||||||
|  |         expect(Rails.logger).to receive(:info).with("Successfully exported template Test Template to #{export_location.name}") | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when API request fails' do | ||||||
|  |       before do | ||||||
|  |         allow(faraday_response).to receive(:success?).and_return(false) | ||||||
|  |         allow(faraday_response).to receive(:status).and_return(422) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('Failed to export template to third party') | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'logs error message' do | ||||||
|  |         expect(Rails.logger).to receive(:error).with('Failed to export template to third party: 422') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'reports to Rollbar if available' do | ||||||
|  |         stub_const('Rollbar', double) | ||||||
|  |         expect(Rollbar).to receive(:error).with("#{export_location.name} template export API error: 422") | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when API response is nil' do | ||||||
|  |       before do | ||||||
|  |         allow(faraday_connection).to receive(:post).and_return(nil) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('Failed to export template to third party') | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when Faraday error occurs' do | ||||||
|  |       before do | ||||||
|  |         allow(faraday_connection).to receive(:post).and_raise(Faraday::ConnectionFailed.new('Connection failed')) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets network error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('Network error occurred during template export: Connection failed') | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'logs the error' do | ||||||
|  |         expect(Rails.logger).to receive(:error).with('Failed to export template Faraday: Connection failed') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'reports to Rollbar if available' do | ||||||
|  |         stub_const('Rollbar', double) | ||||||
|  |         expect(Rollbar).to receive(:error).with('Failed to export template: Connection failed') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when other standard error occurs' do | ||||||
|  |       before do | ||||||
|  |         allow(ExportLocation).to receive(:default_location).and_raise(StandardError.new('Database error')) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'returns false and sets generic error message' do | ||||||
|  |         expect(service.call).to be false | ||||||
|  |         expect(service.error_message).to eq('An unexpected error occurred during template export: Database error') | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'logs the error' do | ||||||
|  |         expect(Rails.logger).to receive(:error).with('Failed to export template: Database error') | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'reports to Rollbar if available' do | ||||||
|  |         stub_const('Rollbar', double) | ||||||
|  |         error = StandardError.new('Database error') | ||||||
|  |         allow(ExportLocation).to receive(:default_location).and_raise(error) | ||||||
|  |         expect(Rollbar).to receive(:error).with(error) | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe 'data handling' do | ||||||
|  |     let(:request_double) { double('request', body: nil) } | ||||||
|  | 
 | ||||||
|  |     before do | ||||||
|  |       allow(request_double).to receive(:body=) | ||||||
|  |       allow(faraday_connection).to receive(:post).and_yield(request_double).and_return(faraday_response) | ||||||
|  |       allow(faraday_response).to receive(:success?).and_return(true) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'sends the data in the request body' do | ||||||
|  |       expect(request_double).to receive(:body=) do |body| | ||||||
|  |         expect(JSON.parse(body)).to eq(data.deep_stringify_keys) | ||||||
|  |       end | ||||||
|  |       service.call | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when extra_params are provided' do | ||||||
|  |       let(:extra_params) { { 'api_key' => 'test_key', 'version' => '1.0' } } | ||||||
|  | 
 | ||||||
|  |       before do | ||||||
|  |         allow(export_location).to receive(:extra_params).and_return(extra_params) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       it 'merges extra_params into the data' do | ||||||
|  |         expect(request_double).to receive(:body=) do |body| | ||||||
|  |           parsed_body = JSON.parse(body) | ||||||
|  |           expect(parsed_body).to include('api_key' => 'test_key', 'version' => '1.0') | ||||||
|  |         end | ||||||
|  |         service.call | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
					Loading…
					
					
				
		Reference in new issue