diff --git a/example.rb b/example.rb index 456173e..ce237bf 100644 --- a/example.rb +++ b/example.rb @@ -3,9 +3,11 @@ require 'campact_user_service' require 'faraday/detailed_logger' +service_host = ENV['SERVICE_HOST'] || 'weact-adapter.staging.campact.de' + def instrument_connection_with_extended_logging(client) default_options = { - ssl: { verify: true }, + ssl: { verify: false }, headers: { 'Accept' => "application/json;q=0.1", 'Accept-Charset' => "utf-8", @@ -29,7 +31,8 @@ def instrument_connection_with_extended_logging(client) # Pick which API to connect to # 1 for session # 2 for user -puts "Which user service are you going to use?\n\t1) session\n\t2) user" +# 3 for prefill forms +puts "Which user service are you going to use?\n\t1) session\n\t2) user\n\t3) prefill forms" option = gets.chomp # Get TOTP credentials @@ -57,17 +60,25 @@ def instrument_connection_with_extended_logging(client) token, 'campact-staging-session', { - host: 'weact-adapter.staging.campact.de', + host: service_host, topt_authorization: {user: username, secret: secret} } ) when '2' puts "I'll need a user account ID. In practice I won't need this here because it can be derived through the session token" - external_account_id = gets.chomp + account_id = gets.chomp account = CampactUserService.account( - external_account_id, + account_id, { - host: 'weact-adapter.staging.campact.de', + host: service_host, + topt_authorization: {user: username, secret: secret} + } + ) +when '3' + puts "I'll need a user account ID. In practice I won't need this here because it can be derived through the session token" + account_id = gets.chomp + prefill_forms = CampactUserService.prefill_forms(account_id, { + host: service_host, topt_authorization: {user: username, secret: secret} } ) diff --git a/lib/campact_user_service.rb b/lib/campact_user_service.rb index e30d4ae..c6d7ca0 100644 --- a/lib/campact_user_service.rb +++ b/lib/campact_user_service.rb @@ -1,6 +1,7 @@ require 'campact_user_service/client' require 'campact_user_service/session' require 'campact_user_service/account' +require 'campact_user_service/prefill_forms' module CampactUserService class << self @@ -9,9 +10,14 @@ def session(session_id, session_cookie_name, options) CampactUserService::Session.new(client, session_id, session_cookie_name) end - def account(external_account_id, options) + def account(account_id, options) client = CampactUserService::Client.new(options) - CampactUserService::Account.new(client, external_account_id) + CampactUserService::Account.new(client, account_id) + end + + def prefill_forms(account_id, options) + client = CampactUserService::Client.new(options) + CampactUserService::PrefillForms.new(client, account_id) end end end diff --git a/lib/campact_user_service/account.rb b/lib/campact_user_service/account.rb index 6cab1e9..f2fed95 100644 --- a/lib/campact_user_service/account.rb +++ b/lib/campact_user_service/account.rb @@ -2,7 +2,7 @@ module CampactUserService class Account attr_reader :client, :user_id - # user_id can either be the user's external_account_id or their email address + # user_id can either be the user's account_id or their email address def initialize(client, user_id) @client = client @user_id = user_id @@ -17,8 +17,13 @@ def subscribed_to_newsletter? subscriptions.any? {|s| s['type'] == 'newsletter' } end + def prefill_undecided? + prefill = account['prefill_forms_state'] + prefill.to_s == 'undecided' + end + def allow_prefill? - prefill = account.dig('preferences', 'prefill_forms') + prefill = account['prefill_forms_state'] prefill.to_s == 'allowed' end diff --git a/lib/campact_user_service/client.rb b/lib/campact_user_service/client.rb index 8410aac..cdb0e2d 100644 --- a/lib/campact_user_service/client.rb +++ b/lib/campact_user_service/client.rb @@ -19,10 +19,11 @@ def initialize(options) @connection = Faraday.new(endpoint, faraday_options) do |faraday| faraday.adapter adapter + faraday.request :json end end - %i(get delete).each do |verb| + %i(get delete patch).each do |verb| define_method("#{verb}_request") do |path, options={}| request(verb, path, options) end @@ -39,6 +40,10 @@ def request(verb, path, options) req.headers['Cookie'] = format_cookies(options[:cookies]) end + if options.key?(:body) + req.body = options[:body] + end + if topt_authorization req.headers['authorization'] = authorization(topt_authorization) end diff --git a/lib/campact_user_service/prefill_forms.rb b/lib/campact_user_service/prefill_forms.rb new file mode 100644 index 0000000..68a3319 --- /dev/null +++ b/lib/campact_user_service/prefill_forms.rb @@ -0,0 +1,24 @@ +require 'cgi' + +module CampactUserService + class PrefillForms + attr_reader :client, :account_id + + def initialize(client, account_id) + @client = client + @account_id = account_id + end + + def update_prefill_forms(prefill_forms_state:, campaign_slug:, **additional_params) + params = { + prefill_forms: { + state: prefill_forms_state, + slug: campaign_slug + }.merge(additional_params) + } + + path = "/v1/prefill_forms?account_id=#{CGI.escape(account_id.to_s)}" + client.patch_request(path, body: params) + end + end +end diff --git a/lib/campact_user_service/session.rb b/lib/campact_user_service/session.rb index a4209d4..0ff2e08 100644 --- a/lib/campact_user_service/session.rb +++ b/lib/campact_user_service/session.rb @@ -8,8 +8,8 @@ def initialize(client, session_id, session_cookie_name) @session_cookie_name = session_cookie_name end - def external_account_id - session["external_account_id"] + def account_id + session["account_id"] end def has_soft_login_session? diff --git a/spec/account_spec.rb b/spec/account_spec.rb index 559d174..4594fa5 100644 --- a/spec/account_spec.rb +++ b/spec/account_spec.rb @@ -165,9 +165,7 @@ it 'should allow prefilling where the user has opted-in' do stub_request(:get, "https://test.com/v1/accounts/#{user_id}") .to_return(body: { - "preferences": { - "prefill_forms": "allowed" - } + "prefill_forms_state": "allowed" }.to_json) expect(subject.allow_prefill?).to be_truthy @@ -176,9 +174,7 @@ it 'should not allow prefilling where the user has not decided' do stub_request(:get, "https://test.com/v1/accounts/#{user_id}") .to_return(body: { - "preferences": { - "prefill_forms": "undecided" - } + "prefill_forms_state": "undecided" }.to_json) expect(subject.allow_prefill?).to be_falsey @@ -187,12 +183,39 @@ it 'should not allow prefilling where the user has opted out' do stub_request(:get, "https://test.com/v1/accounts/#{user_id}") .to_return(body: { - "preferences": { - "prefill_forms": "disallowed" - } + "prefill_forms_state": "forbidden" }.to_json) expect(subject.allow_prefill?).to be_falsey end end + + describe "#prefill_undecided?" do + it 'should be true if the user has not decided' do + stub_request(:get, "https://test.com/v1/accounts/#{user_id}") + .to_return(body: { + "prefill_forms_state": "undecided" + }.to_json) + + expect(subject.prefill_undecided?).to be_truthy + end + + it 'should be false if the user has opted-in' do + stub_request(:get, "https://test.com/v1/accounts/#{user_id}") + .to_return(body: { + "prefill_forms_state": "allowed" + }.to_json) + + expect(subject.prefill_undecided?).to be_falsey + end + + it 'should be false if the user has opted out' do + stub_request(:get, "https://test.com/v1/accounts/#{user_id}") + .to_return(body: { + "prefill_forms_state": "forbidden" + }.to_json) + + expect(subject.prefill_undecided?).to be_falsey + end + end end diff --git a/spec/campact_user_service_spec.rb b/spec/campact_user_service_spec.rb index 5093bc1..be11c5a 100644 --- a/spec/campact_user_service_spec.rb +++ b/spec/campact_user_service_spec.rb @@ -24,11 +24,11 @@ options = { foo: 'bar', foo2: 'bar2' } client = double expect(CampactUserService::Client).to receive(:new).with(options).and_return(client) - external_account_id = '189b6864-d58a-4f49-8370-4ae0b854a40e' + account_id = '189b6864-d58a-4f49-8370-4ae0b854a40e' account_api = double - expect(CampactUserService::Account).to receive(:new).with(client, external_account_id).and_return(account_api) + expect(CampactUserService::Account).to receive(:new).with(client, account_id).and_return(account_api) - account = subject.account(external_account_id, options) + account = subject.account(account_id, options) expect(account).to be(account_api) end diff --git a/spec/client_spec.rb b/spec/client_spec.rb index e40d8cc..c6b6409 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -11,6 +11,10 @@ } let(:faraday_builder) { double(adapter: true) } + before :each do + allow(faraday_builder).to receive(:request).with(:json) + end + it 'should initialize connection from options' do expect(faraday_builder).to receive(:adapter).with(:an_adapter) @@ -87,7 +91,7 @@ subject { CampactUserService::Client.new(host: 'demo.campact.de') } before :each do - expect(Faraday).to receive(:new).and_yield(double(adapter: true)).and_return(connection) + expect(Faraday).to receive(:new).and_yield(double(adapter: true, request: true)).and_return(connection) expect(request_builder).to receive(:options).at_least(:once).and_return(request_builder_options) expect(connection).to receive(:get).and_yield(request_builder).and_return(response) end @@ -167,11 +171,49 @@ subject { CampactUserService::Client.new(host: 'demo.campact.de') } before :each do - expect(Faraday).to receive(:new).and_yield(double(adapter: true)).and_return(connection) + expect(Faraday).to receive(:new).and_yield(double(adapter: true, request: true)).and_return(connection) expect(request_builder).to receive(:options).at_least(:once).and_return(request_builder_options) expect(connection).to receive(:delete).and_yield(request_builder).and_return(response) end include_examples 'error handling' end + + describe '#patch_request' do + let(:connection) { double } + let(:request_builder) { double } + let(:request_builder_options) { double } + let(:response) { double(body: '') } + let(:method_under_test) { :patch_request } + + subject { CampactUserService::Client.new(host: 'demo.campact.de') } + + context 'with faraday stubbed' do + before :each do + expect(Faraday).to receive(:new).and_yield(double(adapter: true, request: true)).and_return(connection) + expect(request_builder).to receive(:options).at_least(:once).and_return(request_builder_options) + expect(connection).to receive(:patch).and_yield(request_builder).and_return(response) + end + + include_examples 'error handling' + end + + context 'with HTTP connection stubbed' do + before(:each) do + WebMock.disable_net_connect! + end + + it 'should send the body included in options' do + stub_patch = + stub_request(:patch, 'https://demo.campact.de/foo/bar') + .with(body: { a_field: 'foo', another_field: 'bar' }.to_json, + headers: { 'Content-Type'=>'application/json' }) + .to_return(status: 204) + + subject.patch_request('/foo/bar', body: { a_field: 'foo', another_field: 'bar' }) + + assert_requested(stub_patch) + end + end + end end diff --git a/spec/prefill_forms_spec.rb b/spec/prefill_forms_spec.rb new file mode 100644 index 0000000..7ab4213 --- /dev/null +++ b/spec/prefill_forms_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe CampactUserService::PrefillForms do + let(:client) { CampactUserService::Client.new(host: 'test.com') } + let(:account_id) { 'abcdef123456fedcba' } + + subject do + CampactUserService::PrefillForms.new(client, account_id) + end + + before(:each) do + WebMock.disable_net_connect! + end + + describe '#update_prefill_forms' do + it 'should update the prefill forms state with just the state and petition slug' do + stub_patch_prefill_forms = stub_request(:patch, "https://test.com/v1/prefill_forms?account_id=#{account_id}") + .with(body: { + prefill_forms: { + state: 'allowed', + slug: 'save-the-forest' + } + }) + .to_return(status: 204) + + result = subject.update_prefill_forms(prefill_forms_state: 'allowed', campaign_slug: 'save-the-forest') + + assert_requested(stub_patch_prefill_forms) + expect(result).to be_truthy + end + + it 'should update the prefill forms state including UTM parameters' do + utm_params = { + utm_campaign: 'a campaign', + utm_content: 'a content', + utm_medium: 'a medium', + utm_source: 'a source', + utm_term: 'a term', + utm_placement: 'a placement', + utm_target: 'a target' + } + stub_patch_prefill_forms = stub_request(:patch, "https://test.com/v1/prefill_forms?account_id=#{account_id}") + .with(body: { + prefill_forms: { + state: 'allowed', + slug: 'save-the-forest' + }.merge(utm_params) + }) + .to_return(status: 204) + + subject.update_prefill_forms(prefill_forms_state: 'allowed', campaign_slug: 'save-the-forest', **utm_params) + + assert_requested(stub_patch_prefill_forms) + end + + it 'should escape special characters in account_id' do + unescaped_account_id = 'user@example.com' + escaped_account_id = 'user%40example.com' + + prefill_forms = CampactUserService::PrefillForms.new(client, unescaped_account_id) + + stub_patch_prefill_forms = stub_request(:patch, "https://test.com/v1/prefill_forms?account_id=#{escaped_account_id}") + .with(body: { + prefill_forms: { + state: 'allowed', + slug: 'save-the-forest' + } + }) + .to_return(status: 204) + + prefill_forms.update_prefill_forms(prefill_forms_state: 'allowed', campaign_slug: 'save-the-forest') + + assert_requested(stub_patch_prefill_forms) + end + end +end diff --git a/spec/session_spec.rb b/spec/session_spec.rb index 5749860..511dc35 100644 --- a/spec/session_spec.rb +++ b/spec/session_spec.rb @@ -7,13 +7,13 @@ subject { CampactUserService::Session.new(client, session_id, session_cookie_name) } - describe '#external_account_id' do + describe '#account_id' do it 'should be present' do stub_request(:get, 'https://test.com/v1/sessions') .with(headers: {'Cookie' => "cus-session=#{session_id};"}) - .to_return(body: {external_account_id: '189b6864-d58a-4f49-8370-4ae0b854a40e'}.to_json) + .to_return(body: {account_id: '189b6864-d58a-4f49-8370-4ae0b854a40e'}.to_json) - expect(subject.external_account_id).to eq('189b6864-d58a-4f49-8370-4ae0b854a40e') + expect(subject.account_id).to eq('189b6864-d58a-4f49-8370-4ae0b854a40e') end end