blue_state_digital 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/README.md +17 -4
- data/lib/blue_state_digital.rb +2 -1
- data/lib/blue_state_digital/connection.rb +27 -3
- data/lib/blue_state_digital/constituent.rb +16 -6
- data/lib/blue_state_digital/error_middleware.rb +5 -2
- data/lib/blue_state_digital/event.rb +3 -2
- data/lib/blue_state_digital/signup_form.rb +93 -0
- data/lib/blue_state_digital/version.rb +1 -1
- data/spec/blue_state_digital/connection_spec.rb +50 -0
- data/spec/blue_state_digital/constituent_group_spec.rb +1 -1
- data/spec/blue_state_digital/constituent_spec.rb +8 -6
- data/spec/blue_state_digital/event_spec.rb +9 -8
- data/spec/blue_state_digital/signup_form_spec.rb +103 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4c617aba30c10bd08e560236ecebebdaf6a6698a9e55fbb5c17beb2284aedd5e
|
4
|
+
data.tar.gz: d1529a6282f4c39dabd7177999409f58a741573d400c2d3a4c4bc285c8b89630
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7f79e29f4c914a2e3fcccb809b866eae767ea97552131d739bcdb7f6981b88f9520574047165c3abcedad8d21ef0115609c2d3d74de88bc4519b28a790cc0ba
|
7
|
+
data.tar.gz: 11483f192126b7ea23da98263cb4ef074bde678dd43b8fc8bc05dfe5fbdd887a6feb850c6beed20ea5db72ad3a7f77a4f831ccfe35f8e5315236e2791d0e60c0
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.5.1
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -11,25 +11,38 @@ gem blue_state_digital
|
|
11
11
|
Configuration:
|
12
12
|
|
13
13
|
```ruby
|
14
|
-
connection = BlueStateDigital::Connection.new(host:'foo.com' api_id: 'bar', api_secret: 'magic_secret')
|
14
|
+
connection = BlueStateDigital::Connection.new(host:'foo.com', api_id: 'bar', api_secret: 'magic_secret')
|
15
15
|
cons = BlueStateDigital::Constituent.new({firstname: 'George', lastname: 'Washington', emails: [{ email: 'george@washington.com'}]}.merge({connection: connection}))
|
16
16
|
cons.save
|
17
|
-
cons.
|
17
|
+
cons.id # created constituent ID
|
18
18
|
```
|
19
19
|
|
20
20
|
Use the event machine adapter:
|
21
21
|
|
22
22
|
```ruby
|
23
|
-
connection = BlueStateDigital::Connection.new(host:'foo.com' api_id: 'bar', api_secret: 'magic_secret', adapter: :em_synchrony)
|
23
|
+
connection = BlueStateDigital::Connection.new(host:'foo.com', api_id: 'bar', api_secret: 'magic_secret', adapter: :em_synchrony)
|
24
24
|
```
|
25
25
|
|
26
26
|
### Unsubscribes
|
27
27
|
|
28
28
|
```ruby
|
29
|
-
connection = BlueStateDigital::Connection.new(host:'foo.com' api_id: 'bar', api_secret: 'magic_secret')
|
29
|
+
connection = BlueStateDigital::Connection.new(host:'foo.com', api_id: 'bar', api_secret: 'magic_secret')
|
30
30
|
unsub = BlueStateDigital::EmailUnsubscribe.new({email: 'george@washington.com', reason: 'tea in the harbor'}.merge({connection: connection}))
|
31
31
|
unsub.unsubscribe! # raises on error, returns true on success.
|
32
|
+
```
|
33
|
+
|
34
|
+
### Signup Forms
|
32
35
|
|
36
|
+
```ruby
|
37
|
+
connection = BlueStateDigital::Connection.new(host:'foo.com', api_id: 'bar', api_secret: 'magic_secret')
|
38
|
+
signup_form = BlueStateDigital::SignupForm.clone(clone_from_id: 3, slug: 'foo', name: 'my new form', public_title: 'Sign Here For Puppies', connection: connection)
|
39
|
+
signup_form.set_cons_group(2345)
|
40
|
+
fields = signup_form.form_fields # returns a list of SignupFormField
|
41
|
+
|
42
|
+
# The keys of the field_data hash should match the labels of the signup form's fields
|
43
|
+
signup_form.process_signup(field_data: {'First Name' => 'George', 'Last Name' => 'Washington', 'Email Address' => 'george@example.com', 'A Custom Field' => 'some custom data'},
|
44
|
+
email_opt_in: true, source: 'foo', subsource: 'bar')
|
45
|
+
```
|
33
46
|
|
34
47
|
### Dataset integration
|
35
48
|
|
data/lib/blue_state_digital.rb
CHANGED
@@ -25,8 +25,9 @@ require "blue_state_digital/dataset"
|
|
25
25
|
require "blue_state_digital/dataset_map"
|
26
26
|
require "blue_state_digital/error_middleware"
|
27
27
|
require "blue_state_digital/email_unsubscribe"
|
28
|
+
require "blue_state_digital/signup_form"
|
28
29
|
|
29
30
|
|
30
31
|
I18n.enforce_available_locales = false
|
31
32
|
|
32
|
-
Faraday::Response.register_middleware :error_middleware => lambda { BlueStateDigital::ErrorMiddleware }
|
33
|
+
Faraday::Response.register_middleware :error_middleware => lambda { BlueStateDigital::ErrorMiddleware }
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module BlueStateDigital
|
2
|
+
class DeferredResultTimeout < StandardError ; end
|
3
|
+
|
2
4
|
class Connection
|
3
5
|
API_VERSION = 2
|
4
6
|
API_BASE = '/page/api'
|
@@ -6,9 +8,13 @@ module BlueStateDigital
|
|
6
8
|
|
7
9
|
attr_reader :constituents, :constituent_groups, :datasets, :dataset_maps
|
8
10
|
|
11
|
+
attr_accessor :instrumentation
|
12
|
+
|
9
13
|
def initialize(params = {})
|
10
14
|
@api_id = params[:api_id]
|
11
15
|
@api_secret = params[:api_secret]
|
16
|
+
self.instrumentation = params[:instrumentation]
|
17
|
+
|
12
18
|
@client = Faraday.new(:url => "https://#{params[:host]}/") do |faraday|
|
13
19
|
faraday.request :url_encoded # form-encode POST params
|
14
20
|
if defined?(Rails) && Rails.env.development?
|
@@ -26,7 +32,16 @@ module BlueStateDigital
|
|
26
32
|
|
27
33
|
def perform_request_raw(call, params = {}, method = "GET", body = nil)
|
28
34
|
path = API_BASE + call
|
29
|
-
|
35
|
+
|
36
|
+
# instrumentation is a Proc that is called for each request that is performed.
|
37
|
+
if self.instrumentation
|
38
|
+
stats = {}
|
39
|
+
stats[:path] = path
|
40
|
+
stats[:api_id] = @api_id
|
41
|
+
self.instrumentation.call(stats)
|
42
|
+
end
|
43
|
+
|
44
|
+
resp = if method == "POST" || method == "PUT"
|
30
45
|
@client.send(method.downcase.to_sym) do |req|
|
31
46
|
content_type = params.delete(:content_type) || 'application/x-www-form-urlencoded'
|
32
47
|
accept = params.delete(:accept) || 'text/xml'
|
@@ -39,6 +54,8 @@ module BlueStateDigital
|
|
39
54
|
else
|
40
55
|
@client.get(path, extended_params(path, params))
|
41
56
|
end
|
57
|
+
|
58
|
+
resp
|
42
59
|
end
|
43
60
|
|
44
61
|
def perform_graph_request(call, params, method = 'POST')
|
@@ -106,11 +123,18 @@ module BlueStateDigital
|
|
106
123
|
end
|
107
124
|
end
|
108
125
|
|
109
|
-
def wait_for_deferred_result(deferred_id)
|
126
|
+
def wait_for_deferred_result(deferred_id, timeout = 600)
|
110
127
|
result = nil
|
128
|
+
time_waiting = 0
|
111
129
|
while result.nil? || (result.respond_to?(:length) && result.length == 0)
|
112
130
|
result = retrieve_results(deferred_id)
|
113
|
-
|
131
|
+
if result.nil? || (result.respond_to?(:length) && result.length == 0)
|
132
|
+
time_waiting = time_waiting + 2
|
133
|
+
if time_waiting > timeout
|
134
|
+
raise BlueStateDigital::DeferredResultTimeout.new("exceeded timeout #{timeout} seconds waiting for #{deferred_id}")
|
135
|
+
end
|
136
|
+
sleep(2)
|
137
|
+
end
|
114
138
|
end
|
115
139
|
result
|
116
140
|
end
|
@@ -11,14 +11,14 @@ module BlueStateDigital
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def save
|
14
|
-
xml = connection.perform_request '/cons/
|
14
|
+
xml = connection.wait_for_deferred_result( connection.perform_request '/cons/upsert_constituent_data', {}, "POST", self.to_xml )
|
15
15
|
doc = Nokogiri::XML(xml)
|
16
|
-
record =
|
16
|
+
record = doc.xpath('//cons').first
|
17
17
|
if record
|
18
18
|
self.id = record[:id]
|
19
19
|
self.is_new = record[:is_new]
|
20
20
|
else
|
21
|
-
raise "
|
21
|
+
raise "upsert_constituent_data failed with message: #{xml}"
|
22
22
|
end
|
23
23
|
self
|
24
24
|
end
|
@@ -96,17 +96,19 @@ module BlueStateDigital
|
|
96
96
|
|
97
97
|
class Constituents < CollectionResource
|
98
98
|
def get_constituents_by_email email, bundles= [ 'cons_group' ]
|
99
|
-
|
99
|
+
result = connection.perform_request('/cons/get_constituents_by_email', filter_parameters({emails: email, :bundles=> bundles.join(',')}), "GET")
|
100
|
+
|
101
|
+
from_response(result)
|
100
102
|
end
|
101
103
|
|
102
104
|
def get_constituents_by_id(cons_ids, bundles = ['cons_group'])
|
103
105
|
cons_ids_concat = cons_ids.is_a?(Array) ? cons_ids.join(',') : cons_ids.to_s
|
104
106
|
|
105
|
-
from_response(connection.perform_request('/cons/get_constituents_by_id', {:cons_ids => cons_ids_concat, :bundles=> bundles.join(',')}, "GET"))
|
107
|
+
from_response(connection.perform_request('/cons/get_constituents_by_id', filter_parameters({:cons_ids => cons_ids_concat, :bundles=> bundles.join(',')}), "GET"))
|
106
108
|
end
|
107
109
|
|
108
110
|
def get_constituents(filter, bundles = [ 'cons_group' ])
|
109
|
-
result = connection.wait_for_deferred_result( connection.perform_request('/cons/get_constituents', {:filter => filter, :bundles=> bundles.join(',')}, "GET") )
|
111
|
+
result = connection.wait_for_deferred_result( connection.perform_request('/cons/get_constituents', filter_parameters({:filter => filter, :bundles=> bundles.join(',')}), "GET") )
|
110
112
|
|
111
113
|
from_response(result)
|
112
114
|
end
|
@@ -174,5 +176,13 @@ module BlueStateDigital
|
|
174
176
|
|
175
177
|
cons
|
176
178
|
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def filter_parameters(params)
|
183
|
+
params.delete_if do |key, value|
|
184
|
+
value.blank?
|
185
|
+
end
|
186
|
+
end
|
177
187
|
end
|
178
188
|
end
|
@@ -2,6 +2,7 @@ module BlueStateDigital
|
|
2
2
|
class Unauthorized < ::Faraday::Error::ClientError ; end
|
3
3
|
class ResourceDoesNotExist < ::Faraday::Error::ClientError ; end
|
4
4
|
class EmailNotFound < ::Faraday::Error::ClientError ; end
|
5
|
+
class XmlErrorResponse < ::Faraday::Error::ClientError ; end
|
5
6
|
|
6
7
|
class ErrorMiddleware < ::Faraday::Response::RaiseError
|
7
8
|
def on_complete(env)
|
@@ -11,7 +12,9 @@ module BlueStateDigital
|
|
11
12
|
when 403
|
12
13
|
raise BlueStateDigital::Unauthorized, response_values(env).to_s
|
13
14
|
when 409
|
14
|
-
if env.body =~
|
15
|
+
if env.body =~ /<error>/
|
16
|
+
raise BlueStateDigital::XmlErrorResponse, env.body
|
17
|
+
elsif env.body =~ /does not exist/
|
15
18
|
raise BlueStateDigital::ResourceDoesNotExist, response_values(env).to_s
|
16
19
|
elsif env.body =~ /Email not found/
|
17
20
|
raise BlueStateDigital::EmailNotFound, response_values(env).to_s
|
@@ -26,4 +29,4 @@ module BlueStateDigital
|
|
26
29
|
end
|
27
30
|
end
|
28
31
|
end
|
29
|
-
end
|
32
|
+
end
|
@@ -10,7 +10,8 @@ module BlueStateDigital
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
FIELDS = [:event_id_obfuscated, :event_type_id, :creator_cons_id, :name, :description, :venue_name, :venue_country,
|
13
|
+
FIELDS = [:event_id_obfuscated, :event_type_id, :creator_cons_id, :name, :description, :venue_name, :venue_country,
|
14
|
+
:venue_zip, :venue_city, :venue_state_cd, :start_date, :end_date, :local_timezone]
|
14
15
|
attr_accessor *FIELDS
|
15
16
|
|
16
17
|
def save
|
@@ -37,7 +38,7 @@ module BlueStateDigital
|
|
37
38
|
end
|
38
39
|
|
39
40
|
duration_in_minutes = ((end_date - start_date) / 60).to_i
|
40
|
-
day_attrs = { start_datetime_system: start_date.strftime('%Y-%m-%d %H:%M:%S
|
41
|
+
day_attrs = { start_datetime_system: start_date.strftime('%Y-%m-%d %H:%M:%S'), duration: duration_in_minutes }
|
41
42
|
event_attrs[:days] = [ day_attrs ]
|
42
43
|
|
43
44
|
event_attrs.to_json
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module BlueStateDigital
|
2
|
+
class SignupFormField < ApiDataModel
|
3
|
+
FIELDS = [:id, :format, :label, :description, :is_required, :is_custom_field, :cons_field_id, :create_dt]
|
4
|
+
attr_accessor *FIELDS
|
5
|
+
|
6
|
+
# Takes a <signup_form_field> block already processed by Nokogiri
|
7
|
+
def self.from_xml(xml_record)
|
8
|
+
SignupFormField.new(id: xml_record[:id],
|
9
|
+
format: xml_record.xpath('format').text,
|
10
|
+
label: xml_record.xpath('label').text,
|
11
|
+
description: xml_record.xpath('description').text,
|
12
|
+
is_required: xml_record.xpath('is_required').text == '1',
|
13
|
+
is_custom_field: xml_record.xpath('is_custom_field').text == '1',
|
14
|
+
cons_field_id: xml_record.xpath('cons_field_id').text.to_i,
|
15
|
+
create_dt: xml_record.xpath('create_dt').text)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SignupForm < ApiDataModel
|
20
|
+
FIELDS = [:id, :name, :slug, :public_title, :create_dt]
|
21
|
+
attr_accessor *FIELDS
|
22
|
+
|
23
|
+
def self.clone(clone_from_id:, slug:, name:, public_title:, connection:)
|
24
|
+
params = {signup_form_id: clone_from_id, title: public_title, signup_form_name: name, slug: slug}
|
25
|
+
xml_response = connection.perform_request '/signup/clone_form', {}, 'POST', params
|
26
|
+
|
27
|
+
doc = Nokogiri::XML(xml_response)
|
28
|
+
record = doc.xpath('//signup_form').first
|
29
|
+
if record
|
30
|
+
id = record.xpath('id').text.to_i
|
31
|
+
SignupForm.new(id: id, name: name, slug: slug, public_title: public_title, connection: connection)
|
32
|
+
else
|
33
|
+
raise "Cloning signup form failed with message: #{xml_response}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# field_data is a hash mapping field labels to field values, like {'First Name' => 'Alice'}
|
38
|
+
def process_signup(field_data:, email_opt_in: false, source: nil, subsource: nil)
|
39
|
+
# Construct the XML to send
|
40
|
+
builder = Builder::XmlMarkup.new
|
41
|
+
builder.instruct! :xml, version: '1.0', encoding: 'utf-8'
|
42
|
+
xml_body = builder.api do |api|
|
43
|
+
api.signup_form(id: self.id) do |form|
|
44
|
+
form_fields.each do |field|
|
45
|
+
form.signup_form_field(field_data[field.label], id: field.id)
|
46
|
+
end
|
47
|
+
unless email_opt_in.nil?
|
48
|
+
form.email_opt_in(email_opt_in ? '1' : '0')
|
49
|
+
end
|
50
|
+
form.source(source) unless source.nil?
|
51
|
+
form.subsource(subsource) unless subsource.nil?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Post it to the endpoint
|
56
|
+
begin
|
57
|
+
response = connection.perform_request_raw '/signup/process_signup', {}, 'POST', xml_body
|
58
|
+
if response.status >= 200 && response.status < 300
|
59
|
+
return true
|
60
|
+
else
|
61
|
+
raise "process signup failed with message: #{response.body}"
|
62
|
+
end
|
63
|
+
rescue BlueStateDigital::XmlErrorResponse => err
|
64
|
+
begin
|
65
|
+
errors = {}
|
66
|
+
error_xmls = Nokogiri::XML(err.message).xpath('//error')
|
67
|
+
error_xmls.each do |error_xml|
|
68
|
+
field = form_fields.select{|field| field.id.to_s == error_xml.xpath('signup_form_field_id').text}.first
|
69
|
+
errors[field.label] = error_xml.xpath('description').text
|
70
|
+
raise "process_signup failed with errors: #{errors}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_cons_group(cons_group_id)
|
77
|
+
connection.perform_request('/signup/set_cons_group', {signup_form_id: self.id}, 'POST', {cons_group_id: cons_group_id})
|
78
|
+
end
|
79
|
+
|
80
|
+
def form_fields
|
81
|
+
if @_form_fields.nil?
|
82
|
+
xml_response = connection.perform_request '/signup/list_form_fields', {signup_form_id: id}, 'GET', nil
|
83
|
+
doc = Nokogiri::XML(xml_response)
|
84
|
+
|
85
|
+
@_form_fields = []
|
86
|
+
doc.xpath('//signup_form_field').each do |form_field_record|
|
87
|
+
@_form_fields << SignupFormField.from_xml(form_field_record)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
@_form_fields
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -27,6 +27,46 @@ describe BlueStateDigital::Connection do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
describe '#perform_request_raw' do
|
31
|
+
let(:api_call) { '/somemethod' }
|
32
|
+
let(:timestamp) { Time.now }
|
33
|
+
let(:api_ts) { timestamp.utc.to_i.to_s }
|
34
|
+
let(:api_mac) { connection.compute_hmac("/page/api#{api_call}", api_ts, { api_ver: '2', api_id: api_id, api_ts: api_ts }) }
|
35
|
+
|
36
|
+
describe 'instrumentation' do
|
37
|
+
before(:each) do
|
38
|
+
Timecop.freeze(timestamp) do
|
39
|
+
stub_url = "https://#{api_host}/page/api/somemethod?api_id=#{api_id}&api_mac=#{api_mac}&api_ts=#{api_ts}&api_ver=2"
|
40
|
+
stub_request(:post, stub_url).with do |request|
|
41
|
+
expect(request.body).to eq("a=b")
|
42
|
+
expect(request.headers['Accept']).to eq('text/xml')
|
43
|
+
expect(request.headers['Content-Type']).to eq('application/x-www-form-urlencoded')
|
44
|
+
true
|
45
|
+
end.to_return(body: "body")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should not instrument anything if instrumentation is nil' do
|
50
|
+
expect(connection.instrumentation).to be_nil
|
51
|
+
connection.perform_request(api_call, params = {}, method = "POST", body = "a=b")
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with instrumentation' do
|
55
|
+
let(:instrumentation) do
|
56
|
+
Proc.new do |stats|
|
57
|
+
stats[:path]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
let(:connection) { BlueStateDigital::Connection.new({host: api_host, api_id: api_id, api_secret: api_secret, instrumentation: instrumentation})}
|
61
|
+
|
62
|
+
it 'should call if set to proc' do
|
63
|
+
expect(instrumentation).to receive(:call).with({path: '/page/api/somemethod', :api_id=>"sfrazer"})
|
64
|
+
connection.perform_request(api_call, params = {}, method = "POST", body = "a=b")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
30
70
|
describe "#perform_request" do
|
31
71
|
context 'POST' do
|
32
72
|
it "should perform POST request" do
|
@@ -142,6 +182,16 @@ describe BlueStateDigital::Connection do
|
|
142
182
|
expect(connection).to receive(:perform_request).and_return("foo")
|
143
183
|
expect(connection.get_deferred_results("deferred_id")).to eq("foo")
|
144
184
|
end
|
185
|
+
|
186
|
+
it 'should raise if timeout occurs' do
|
187
|
+
expect(connection).to receive(:perform_request).and_return(nil)
|
188
|
+
expect { connection.wait_for_deferred_result("deferred_id", 1) }.to raise_error(BlueStateDigital::DeferredResultTimeout)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should not raise if successful' do
|
192
|
+
expect(connection).to receive(:perform_request).and_return("foo")
|
193
|
+
expect(connection.wait_for_deferred_result("deferred_id")).to eq("foo")
|
194
|
+
end
|
145
195
|
end
|
146
196
|
|
147
197
|
describe "#compute_hmac" do
|
@@ -226,7 +226,7 @@ xml_string
|
|
226
226
|
expect(connection).to receive(:perform_request).with("/cons_group/#{method}", post_params, "POST").and_return("deferred_id")
|
227
227
|
expect(connection).not_to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET")
|
228
228
|
|
229
|
-
connection.constituent_groups.send(method.to_sym, cons_group_id, cons_ids,
|
229
|
+
connection.constituent_groups.send(method.to_sym, cons_group_id, cons_ids, {wait_for_result: false})
|
230
230
|
end
|
231
231
|
|
232
232
|
it "should #{operation} a single constituent id to a group" do
|
@@ -254,8 +254,7 @@ describe BlueStateDigital::Constituent do
|
|
254
254
|
|
255
255
|
describe ".get_constituents_by_email" do
|
256
256
|
it "should make a filtered constituents query" do
|
257
|
-
expect(connection).to receive(:perform_request).with('/cons/
|
258
|
-
expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(@single_constituent)
|
257
|
+
expect(connection).to receive(:perform_request).with('/cons/get_constituents_by_email', {:emails=>"george@washington.com", :bundles => 'cons_group'}, "GET").and_return(@single_constituent)
|
259
258
|
response = connection.constituents.get_constituents_by_email("george@washington.com").first
|
260
259
|
expect(response.id).to eq("4382")
|
261
260
|
expect(response.firstname).to eq('Bob')
|
@@ -263,8 +262,7 @@ describe BlueStateDigital::Constituent do
|
|
263
262
|
|
264
263
|
it "should return constituents' details based on bundles" do
|
265
264
|
bundles = 'cons_addr'
|
266
|
-
expect(connection).to receive(:perform_request).with('/cons/
|
267
|
-
expect(connection).to receive(:perform_request).with('/get_deferred_results', {deferred_id: "deferred_id"}, "GET").and_return(@constituent_with_addr)
|
265
|
+
expect(connection).to receive(:perform_request).with('/cons/get_constituents_by_email', {:emails=>"george@washington.com", :bundles => bundles}, "GET").and_return(@constituent_with_addr)
|
268
266
|
response = connection.constituents.get_constituents_by_email("george@washington.com", ['cons_addr']).first
|
269
267
|
response.addresses[0].addr1 == "aaa1"
|
270
268
|
response.addresses[0].addr2 == "aaa2"
|
@@ -359,6 +357,7 @@ describe BlueStateDigital::Constituent do
|
|
359
357
|
connection.constituents.delete_constituents_by_id(2)
|
360
358
|
end
|
361
359
|
end
|
360
|
+
|
362
361
|
it "should set constituent data" do
|
363
362
|
timestamp = Time.now.to_i
|
364
363
|
|
@@ -397,7 +396,10 @@ describe BlueStateDigital::Constituent do
|
|
397
396
|
output << "</cons>"
|
398
397
|
output << "</api>"
|
399
398
|
|
400
|
-
|
399
|
+
deferred_result_id = '123'
|
400
|
+
|
401
|
+
expect(connection).to receive(:wait_for_deferred_result).with(deferred_result_id).and_return(output)
|
402
|
+
expect(connection).to receive(:perform_request).with('/cons/upsert_constituent_data', {}, "POST", input) { deferred_result_id }
|
401
403
|
|
402
404
|
cons_data = BlueStateDigital::Constituent.new(data)
|
403
405
|
cons_data.save
|
@@ -419,4 +421,4 @@ describe BlueStateDigital::Constituent do
|
|
419
421
|
expect(cons.to_xml).not_to be_nil
|
420
422
|
end
|
421
423
|
end
|
422
|
-
end
|
424
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe BlueStateDigital::Event do
|
4
|
-
let(:
|
4
|
+
let(:time_zone) { Time.find_zone('America/New_York') }
|
5
|
+
let(:start_date) { time_zone.now.change(usec: 0) }
|
5
6
|
let(:end_date) { start_date + 1.hour }
|
6
|
-
let(:event_attributes) { {event_type_id: '1', creator_cons_id: '2', name: 'event 1', description: 'my event', venue_name: 'home', venue_country: 'US', venue_zip: '10001', venue_city: 'New York', venue_state_cd: 'NY', start_date: start_date, end_date: end_date} }
|
7
|
+
let(:event_attributes) { {event_type_id: '1', creator_cons_id: '2', name: 'event 1', description: 'my event', venue_name: 'home', venue_country: 'US', venue_zip: '10001', venue_city: 'New York', venue_state_cd: 'NY', start_date: start_date, end_date: end_date, local_timezone: 'America/New_York'} }
|
7
8
|
|
8
9
|
describe '#to_json' do
|
9
10
|
it "should serialize event without event_id_obfuscated" do
|
@@ -12,12 +13,12 @@ describe BlueStateDigital::Event do
|
|
12
13
|
event_json = JSON.parse(event.to_json)
|
13
14
|
|
14
15
|
expect(event_json.keys).not_to include(:event_id_obfuscated)
|
15
|
-
[:event_type_id, :creator_cons_id, :name, :description, :venue_name, :venue_country, :venue_zip, :venue_city, :venue_state_cd].each do |direct_attribute|
|
16
|
+
[:event_type_id, :creator_cons_id, :name, :description, :venue_name, :venue_country, :venue_zip, :venue_city, :venue_state_cd, :local_timezone].each do |direct_attribute|
|
16
17
|
expect(event_json[direct_attribute.to_s]).to eq(event_attributes[direct_attribute])
|
17
18
|
end
|
18
19
|
expect(event_json['days'].count).to eq(1)
|
19
|
-
|
20
|
-
expect(
|
20
|
+
start_date_serialized = event_json['days'][0]['start_datetime_system']
|
21
|
+
expect(time_zone.parse(start_date_serialized)).to eq(start_date)
|
21
22
|
expect(event_json['days'][0]['duration']).to eq(60)
|
22
23
|
end
|
23
24
|
|
@@ -27,12 +28,12 @@ describe BlueStateDigital::Event do
|
|
27
28
|
|
28
29
|
event_json = JSON.parse(event.to_json)
|
29
30
|
|
30
|
-
[:event_id_obfuscated, :event_type_id, :creator_cons_id, :name, :description, :venue_name, :venue_country, :venue_zip, :venue_city, :venue_state_cd].each do |direct_attribute|
|
31
|
+
[:event_id_obfuscated, :event_type_id, :creator_cons_id, :name, :description, :venue_name, :venue_country, :venue_zip, :venue_city, :venue_state_cd, :local_timezone].each do |direct_attribute|
|
31
32
|
expect(event_json[direct_attribute.to_s]).to eq(event_attributes[direct_attribute])
|
32
33
|
end
|
33
34
|
expect(event_json['days'].count).to eq(1)
|
34
|
-
|
35
|
-
expect(
|
35
|
+
start_date_serialized = event_json['days'][0]['start_datetime_system']
|
36
|
+
expect(time_zone.parse(start_date_serialized)).to eq(start_date)
|
36
37
|
expect(event_json['days'][0]['duration']).to eq(60)
|
37
38
|
end
|
38
39
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BlueStateDigital::SignupForm do
|
4
|
+
let(:connection) { BlueStateDigital::Connection.new({}) }
|
5
|
+
|
6
|
+
describe '.clone' do
|
7
|
+
it 'should create a new SignupForm' do
|
8
|
+
response = <<-EOF
|
9
|
+
<?xml version="1.0" encoding="utf-8"?>
|
10
|
+
<api>
|
11
|
+
<signup_form>
|
12
|
+
<id>3</id>
|
13
|
+
</signup_form>
|
14
|
+
</api>
|
15
|
+
EOF
|
16
|
+
expect(connection).to receive(:perform_request).with('/signup/clone_form', {}, 'POST',
|
17
|
+
{signup_form_id: 1,
|
18
|
+
title: 'Sign Up Here',
|
19
|
+
signup_form_name: 'Signup Form Foo',
|
20
|
+
slug: 'foo'}).and_return(response)
|
21
|
+
|
22
|
+
form = BlueStateDigital::SignupForm.clone(clone_from_id: 1, slug: 'foo', name: 'Signup Form Foo',
|
23
|
+
public_title: 'Sign Up Here', connection: connection)
|
24
|
+
expect(form.id).to eq(3)
|
25
|
+
expect(form.name).to eq('Signup Form Foo')
|
26
|
+
expect(form.slug).to eq('foo')
|
27
|
+
expect(form.public_title).to eq('Sign Up Here')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#process_signup' do
|
32
|
+
let(:signup_form) { BlueStateDigital::SignupForm.new(id: 3, name: 'A Form', slug: 'asdf', public_title: 'The Best Form', connection: connection) }
|
33
|
+
let(:list_form_fields_response) { <<-EOF
|
34
|
+
<?xml version="1.0" encoding="utf-8"?>
|
35
|
+
<api>
|
36
|
+
<signup_form_field id="83">
|
37
|
+
<format>1</format>
|
38
|
+
<label>First Name</label>
|
39
|
+
<description>First Name</description>
|
40
|
+
<display_order>1</display_order>
|
41
|
+
<is_shown>1</is_shown>
|
42
|
+
<is_required>0</is_required>
|
43
|
+
<break_after>0</break_after>
|
44
|
+
<is_custom_field>0</is_custom_field>
|
45
|
+
<cons_field_id>0</cons_field_id>
|
46
|
+
<create_dt>2010-02-08 18:33:11</create_dt>
|
47
|
+
<extra_def></extra_def>
|
48
|
+
</signup_form_field>
|
49
|
+
<signup_form_field id="84">
|
50
|
+
<format>1</format>
|
51
|
+
<label>Last Name</label>
|
52
|
+
<description>Last Name</description>
|
53
|
+
<display_order>2</display_order>
|
54
|
+
<is_shown>1</is_shown>
|
55
|
+
<is_required>0</is_required>
|
56
|
+
<break_after>0</break_after>
|
57
|
+
<is_custom_field>0</is_custom_field>
|
58
|
+
<cons_field_id>0</cons_field_id>
|
59
|
+
<create_dt>2010-02-08 18:33:11</create_dt>
|
60
|
+
<extra_def></extra_def>
|
61
|
+
</signup_form_field>
|
62
|
+
</api>
|
63
|
+
EOF
|
64
|
+
}
|
65
|
+
|
66
|
+
before :each do
|
67
|
+
allow(connection).to receive(:perform_request).with('/signup/list_form_fields',
|
68
|
+
{signup_form_id: signup_form.id},
|
69
|
+
'GET', nil).and_return(list_form_fields_response)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should call process_signup' do
|
73
|
+
expected_body_readable = <<-EOF
|
74
|
+
<?xml version="1.0" encoding="utf-8"?>
|
75
|
+
<api>
|
76
|
+
<signup_form id="3">
|
77
|
+
<signup_form_field id="83">Susan</signup_form_field>
|
78
|
+
<signup_form_field id="84">Anthony</signup_form_field>
|
79
|
+
<email_opt_in>1</email_opt_in>
|
80
|
+
<source>foo</source>
|
81
|
+
</signup_form>
|
82
|
+
</api>
|
83
|
+
EOF
|
84
|
+
expected_body = expected_body_readable.squish.gsub('> <', '><')
|
85
|
+
|
86
|
+
expect(connection).to receive(:perform_request_raw).with('/signup/process_signup', {}, 'POST', expected_body).and_return(double(body: '', status: 200))
|
87
|
+
|
88
|
+
signup_data = {'First Name' => 'Susan', 'Middle Initial' => 'B', 'Last Name' => 'Anthony'}
|
89
|
+
signup_form.process_signup(field_data: signup_data, email_opt_in: true, source: 'foo')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#set_cons_group' do
|
94
|
+
let(:signup_form) { BlueStateDigital::SignupForm.new(id: 3, name: 'A Form', slug: 'asdf', public_title: 'The Best Form', connection: connection) }
|
95
|
+
let(:cons_group_id) { 123 }
|
96
|
+
|
97
|
+
it 'should call set_cons_group' do
|
98
|
+
expect(connection).to receive(:perform_request).with('/signup/set_cons_group', {signup_form_id: signup_form.id}, 'POST', {cons_group_id: cons_group_id}).and_return('')
|
99
|
+
|
100
|
+
signup_form.set_cons_group(cons_group_id)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blue_state_digital
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Woodhull
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-07-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -271,6 +271,7 @@ files:
|
|
271
271
|
- lib/blue_state_digital/event_rsvp.rb
|
272
272
|
- lib/blue_state_digital/event_type.rb
|
273
273
|
- lib/blue_state_digital/phone.rb
|
274
|
+
- lib/blue_state_digital/signup_form.rb
|
274
275
|
- lib/blue_state_digital/version.rb
|
275
276
|
- spec/blue_state_digital/address_spec.rb
|
276
277
|
- spec/blue_state_digital/api_data_model_spec.rb
|
@@ -286,6 +287,7 @@ files:
|
|
286
287
|
- spec/blue_state_digital/event_spec.rb
|
287
288
|
- spec/blue_state_digital/event_type_spec.rb
|
288
289
|
- spec/blue_state_digital/phone_spec.rb
|
290
|
+
- spec/blue_state_digital/signup_form_spec.rb
|
289
291
|
- spec/fixtures/multiple_event_types.json
|
290
292
|
- spec/fixtures/single_event_type.json
|
291
293
|
- spec/spec_helper.rb
|
@@ -309,7 +311,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
309
311
|
version: '0'
|
310
312
|
requirements: []
|
311
313
|
rubyforge_project: blue_state_digital
|
312
|
-
rubygems_version: 2.
|
314
|
+
rubygems_version: 2.7.6
|
313
315
|
signing_key:
|
314
316
|
specification_version: 4
|
315
317
|
summary: Simple wrapper for Blue State Digital.
|
@@ -328,6 +330,7 @@ test_files:
|
|
328
330
|
- spec/blue_state_digital/event_spec.rb
|
329
331
|
- spec/blue_state_digital/event_type_spec.rb
|
330
332
|
- spec/blue_state_digital/phone_spec.rb
|
333
|
+
- spec/blue_state_digital/signup_form_spec.rb
|
331
334
|
- spec/fixtures/multiple_event_types.json
|
332
335
|
- spec/fixtures/single_event_type.json
|
333
336
|
- spec/spec_helper.rb
|