blue_state_digital 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +2 -0
- data/Guardfile +5 -0
- data/LICENSE +24 -0
- data/README.md +123 -0
- data/Rakefile +11 -0
- data/blue_state_digital.gemspec +37 -0
- data/lib/blue_state_digital.rb +32 -0
- data/lib/blue_state_digital/address.rb +28 -0
- data/lib/blue_state_digital/api_data_model.rb +31 -0
- data/lib/blue_state_digital/collection_resource.rb +14 -0
- data/lib/blue_state_digital/connection.rb +119 -0
- data/lib/blue_state_digital/constituent.rb +178 -0
- data/lib/blue_state_digital/constituent_group.rb +151 -0
- data/lib/blue_state_digital/contribution.rb +73 -0
- data/lib/blue_state_digital/dataset.rb +139 -0
- data/lib/blue_state_digital/dataset_map.rb +138 -0
- data/lib/blue_state_digital/email.rb +22 -0
- data/lib/blue_state_digital/email_unsubscribe.rb +11 -0
- data/lib/blue_state_digital/error_middleware.rb +29 -0
- data/lib/blue_state_digital/event.rb +46 -0
- data/lib/blue_state_digital/event_rsvp.rb +17 -0
- data/lib/blue_state_digital/event_type.rb +19 -0
- data/lib/blue_state_digital/phone.rb +20 -0
- data/lib/blue_state_digital/version.rb +3 -0
- data/spec/blue_state_digital/address_spec.rb +25 -0
- data/spec/blue_state_digital/api_data_model_spec.rb +13 -0
- data/spec/blue_state_digital/connection_spec.rb +153 -0
- data/spec/blue_state_digital/constituent_group_spec.rb +269 -0
- data/spec/blue_state_digital/constituent_spec.rb +422 -0
- data/spec/blue_state_digital/contribution_spec.rb +132 -0
- data/spec/blue_state_digital/dataset_map_spec.rb +137 -0
- data/spec/blue_state_digital/dataset_spec.rb +177 -0
- data/spec/blue_state_digital/email_spec.rb +16 -0
- data/spec/blue_state_digital/error_middleware_spec.rb +15 -0
- data/spec/blue_state_digital/event_rsvp_spec.rb +17 -0
- data/spec/blue_state_digital/event_spec.rb +70 -0
- data/spec/blue_state_digital/event_type_spec.rb +51 -0
- data/spec/blue_state_digital/phone_spec.rb +16 -0
- data/spec/fixtures/multiple_event_types.json +234 -0
- data/spec/fixtures/single_event_type.json +117 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/matchers/fields.rb +23 -0
- metadata +334 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
module BlueStateDigital
|
2
|
+
class Constituent < ApiDataModel
|
3
|
+
FIELDS = [:id, :firstname, :lastname, :is_banned, :create_dt, :ext_id, :birth_dt, :gender,
|
4
|
+
:emails, :addresses, :phones, :groups, :is_new]
|
5
|
+
attr_accessor *FIELDS
|
6
|
+
attr_accessor :group_ids
|
7
|
+
|
8
|
+
def initialize(attrs = {})
|
9
|
+
super(attrs)
|
10
|
+
self.group_ids = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def save
|
14
|
+
xml = connection.perform_request '/cons/set_constituent_data', {}, "POST", self.to_xml
|
15
|
+
doc = Nokogiri::XML(xml)
|
16
|
+
record = doc.xpath('//cons').first
|
17
|
+
if record
|
18
|
+
self.id = record[:id]
|
19
|
+
self.is_new = record[:is_new]
|
20
|
+
else
|
21
|
+
raise "Set constituent data failed with message: #{xml}"
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_new?
|
27
|
+
is_new == "1"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_xml
|
31
|
+
builder = Builder::XmlMarkup.new
|
32
|
+
builder.instruct! :xml, version: '1.0', encoding: 'utf-8'
|
33
|
+
builder.api do |api|
|
34
|
+
cons_attrs = {}
|
35
|
+
cons_attrs[:id] = self.id unless self.id.blank?
|
36
|
+
unless self.ext_id.blank?
|
37
|
+
cons_attrs[:ext_id] = self.ext_id.id unless self.ext_id.id.blank?
|
38
|
+
cons_attrs[:ext_type] = self.ext_id.type unless self.ext_id.type.blank?
|
39
|
+
end
|
40
|
+
|
41
|
+
api.cons(cons_attrs) do |cons|
|
42
|
+
cons.firstname(self.firstname) unless self.firstname.blank?
|
43
|
+
cons.lastname(self.lastname) unless self.lastname.blank?
|
44
|
+
cons.is_banned(self.is_banned) unless self.is_banned.blank?
|
45
|
+
cons.create_dt(self.create_dt) unless self.create_dt.blank?
|
46
|
+
cons.birth_dt(self.birth_dt) unless self.birth_dt.blank?
|
47
|
+
cons.gender(self.gender) unless self.gender.blank?
|
48
|
+
|
49
|
+
unless self.emails.blank?
|
50
|
+
self.emails.each {|email| build_constituent_email(email, cons) }
|
51
|
+
end
|
52
|
+
unless self.addresses.blank?
|
53
|
+
self.addresses.each {|address| build_constituent_address(address, cons) }
|
54
|
+
end
|
55
|
+
unless self.phones.blank?
|
56
|
+
self.phones.each {|phone| build_constituent_phone(phone, cons) }
|
57
|
+
end
|
58
|
+
unless self.groups.blank?
|
59
|
+
self.groups.each {|group| build_constituent_group(group, cons) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
|
68
|
+
def build_constituent_group(group, cons)
|
69
|
+
cons.cons_group({ id: group })
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_constituent_email(email, cons)
|
73
|
+
cons.cons_email do |cons_email|
|
74
|
+
email.to_hash.each do |key, value|
|
75
|
+
cons_email.__send__(key, value) unless value.blank?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def build_constituent_phone(phone, cons)
|
81
|
+
cons.cons_phone do |cons_phone|
|
82
|
+
phone.to_hash.each do |key, value|
|
83
|
+
cons_phone.__send__(key, value) unless value.blank?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_constituent_address(address, cons)
|
89
|
+
cons.cons_addr do |cons_addr|
|
90
|
+
address.to_hash.each do |key, value|
|
91
|
+
cons_addr.__send__(key, value) unless value.blank?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Constituents < CollectionResource
|
98
|
+
def get_constituents_by_email email, bundles= [ 'cons_group' ]
|
99
|
+
get_constituents "email=#{email}", bundles
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_constituents_by_id(cons_ids, bundles = ['cons_group'])
|
103
|
+
cons_ids_concat = cons_ids.is_a?(Array) ? cons_ids.join(',') : cons_ids.to_s
|
104
|
+
|
105
|
+
from_response(connection.perform_request('/cons/get_constituents_by_id', {:cons_ids => cons_ids_concat, :bundles=> bundles.join(',')}, "GET"))
|
106
|
+
end
|
107
|
+
|
108
|
+
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") )
|
110
|
+
|
111
|
+
from_response(result)
|
112
|
+
end
|
113
|
+
|
114
|
+
def delete_constituents_by_id(cons_ids)
|
115
|
+
cons_ids_concat = cons_ids.is_a?(Array) ? cons_ids.join(',') : cons_ids.to_s
|
116
|
+
connection.perform_request('/cons/delete_constituents_by_id', {:cons_ids => cons_ids_concat}, "POST")
|
117
|
+
end
|
118
|
+
|
119
|
+
def from_response(string)
|
120
|
+
parsed_result = Crack::XML.parse(string)
|
121
|
+
if parsed_result["api"].present?
|
122
|
+
result = []
|
123
|
+
if parsed_result["api"]["cons"].is_a?(Array)
|
124
|
+
parsed_result["api"]["cons"].each do |cons_group|
|
125
|
+
result << from_hash(cons_group)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
result << from_hash(parsed_result["api"]["cons"])
|
129
|
+
end
|
130
|
+
return result
|
131
|
+
else
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def from_hash(hash)
|
137
|
+
attrs = {}
|
138
|
+
Constituent::FIELDS.each do | field |
|
139
|
+
attrs[field] = hash[field.to_s] if hash[field.to_s].present?
|
140
|
+
end
|
141
|
+
cons = Constituent.new(attrs)
|
142
|
+
cons.connection = connection
|
143
|
+
if hash['cons_group'].present?
|
144
|
+
if hash['cons_group'].is_a?(Array)
|
145
|
+
cons.group_ids = hash['cons_group'].collect{|g| g["id"]}
|
146
|
+
else
|
147
|
+
cons.group_ids << hash['cons_group']["id"]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
if hash['cons_addr'].present?
|
152
|
+
if hash['cons_addr'].is_a?(Array)
|
153
|
+
cons.addresses = hash['cons_addr'].collect {|addr_hash| BlueStateDigital::Address.new addr_hash}
|
154
|
+
else
|
155
|
+
cons.addresses = [BlueStateDigital::Address.new(hash['cons_addr'])]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
if hash['cons_email'].present?
|
160
|
+
if hash['cons_email'].is_a?(Array)
|
161
|
+
cons.emails = hash['cons_email'].collect {|email_hash| BlueStateDigital::Email.new email_hash}
|
162
|
+
else
|
163
|
+
cons.emails = [BlueStateDigital::Email.new(hash['cons_email'])]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
if hash['cons_phone'].present?
|
168
|
+
if hash['cons_phone'].is_a?(Array)
|
169
|
+
cons.phones = hash['cons_phone'].collect {|phone_hash| BlueStateDigital::Phone.new phone_hash}
|
170
|
+
else
|
171
|
+
cons.phones = [BlueStateDigital::Phone.new(hash['cons_phone'])]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
cons
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module BlueStateDigital
|
2
|
+
class ConstituentGroup < ApiDataModel
|
3
|
+
FIELDS = [:id, :name, :slug, :description, :group_type, :create_dt]
|
4
|
+
attr_accessor *FIELDS
|
5
|
+
|
6
|
+
def to_xml
|
7
|
+
builder = Builder::XmlMarkup.new
|
8
|
+
builder.instruct! :xml, version: '1.0', encoding: 'utf-8'
|
9
|
+
builder.api do |api|
|
10
|
+
api.cons_group do |cons_group|
|
11
|
+
cons_group.name(@name) unless @name.nil?
|
12
|
+
cons_group.slug(@slug) unless @slug.nil?
|
13
|
+
cons_group.description(@description) unless @description.nil?
|
14
|
+
cons_group.group_type(@group_type) unless @group_type.nil?
|
15
|
+
cons_group.create_dt(@create_dt) unless @create_dt.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ConstituentGroups < CollectionResource
|
22
|
+
CONSTITUENTS_BATCH_SIZE = 100
|
23
|
+
|
24
|
+
def add_cons_ids_to_group(cons_group_id, cons_ids, options = {wait_for_result: true})
|
25
|
+
add_or_remove_cons_ids_from_group(:add, cons_group_id, cons_ids, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_cons_ids_from_group(cons_group_id, cons_ids, options = {wait_for_result: true})
|
29
|
+
add_or_remove_cons_ids_from_group(:remove, cons_group_id, cons_ids, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def create(attrs = {})
|
33
|
+
cons_group = ConstituentGroup.new(attrs)
|
34
|
+
|
35
|
+
xml = connection.perform_request '/cons_group/add_constituent_groups', {}, "POST", cons_group.to_xml
|
36
|
+
doc = Nokogiri::XML(xml)
|
37
|
+
group = doc.xpath('//cons_group')
|
38
|
+
|
39
|
+
cons_group.id = group.first[:id]
|
40
|
+
cons_group
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_or_create(attr = {})
|
44
|
+
group = get_constituent_group_by_slug(attr[:slug])
|
45
|
+
if group
|
46
|
+
return group
|
47
|
+
else
|
48
|
+
return create(attr)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def list_constituent_groups
|
53
|
+
from_response(connection.perform_request '/cons_group/list_constituent_groups', {}, "GET")
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_constituent_group(id)
|
57
|
+
from_response(connection.perform_request '/cons_group/get_constituent_group', {cons_group_id: id}, "GET")
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_by_id(id)
|
61
|
+
get_constituent_group(id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_constituent_groups(group_ids)
|
65
|
+
group_ids_concat = group_ids.is_a?(Array) ? group_ids.join(',') : group_ids.to_s
|
66
|
+
connection.wait_for_deferred_result( connection.perform_request( '/cons_group/delete_constituent_groups', {cons_group_ids: group_ids_concat}, "POST") )
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_constituent_group_by_name( name )
|
70
|
+
name = name.slice(0..254)
|
71
|
+
from_response( connection.perform_request '/cons_group/get_constituent_group_by_name', {name: name}, "GET" )
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_constituent_group_by_slug( slug )
|
75
|
+
slug = slug.slice(0..31)
|
76
|
+
from_response( connection.perform_request '/cons_group/get_constituent_group_by_slug', {slug: slug}, "GET" )
|
77
|
+
end
|
78
|
+
|
79
|
+
def rename_group(id, new_name)
|
80
|
+
connection.perform_request '/cons_group/rename_group', {cons_group_id: id, new_name: new_name}, "POST"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Warning: this is an expensive, potentially dangerous operation! You should almost always use rename group instead.
|
84
|
+
def replace_constituent_group!(old_group_id, new_group_attrs)
|
85
|
+
# first, check to see if this group exists.
|
86
|
+
group = get_constituent_group(old_group_id)
|
87
|
+
raise "Group being renamed does not exist!" if group.nil?
|
88
|
+
|
89
|
+
cons_ids = get_cons_ids_for_group(old_group_id)
|
90
|
+
delete_constituent_groups(old_group_id)
|
91
|
+
|
92
|
+
new_group = find_or_create(new_group_attrs)
|
93
|
+
add_cons_ids_to_group(new_group.id, cons_ids)
|
94
|
+
|
95
|
+
new_group
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_cons_ids_for_group(cons_group_id)
|
99
|
+
response = connection.wait_for_deferred_result( connection.perform_request '/cons_group/get_cons_ids_for_group', {cons_group_id: cons_group_id}, "GET" )
|
100
|
+
response.split("\n")
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def from_response(string)
|
107
|
+
parsed_result = Crack::XML.parse(string)
|
108
|
+
if parsed_result["api"].present?
|
109
|
+
if parsed_result["api"]["cons_group"].is_a?(Array)
|
110
|
+
results = []
|
111
|
+
parsed_result["api"]["cons_group"].each do |cons_group|
|
112
|
+
results << from_hash(cons_group)
|
113
|
+
end
|
114
|
+
return results
|
115
|
+
else
|
116
|
+
return from_hash(parsed_result["api"]["cons_group"])
|
117
|
+
end
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def from_hash(hash)
|
124
|
+
attrs = {}
|
125
|
+
ConstituentGroup::FIELDS.each do | field |
|
126
|
+
attrs[field] = hash[field.to_s] if hash[field.to_s].present?
|
127
|
+
end
|
128
|
+
ConstituentGroup.new(attrs)
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_or_remove_cons_ids_from_group(operation, cons_group_id, cons_ids, options)
|
132
|
+
method = case operation
|
133
|
+
when :add
|
134
|
+
'add_cons_ids_to_group'
|
135
|
+
when :remove
|
136
|
+
'remove_cons_ids_from_group'
|
137
|
+
end
|
138
|
+
cons_ids = cons_ids.is_a?(Array) ? cons_ids : [cons_ids]
|
139
|
+
|
140
|
+
cons_ids.in_groups_of(CONSTITUENTS_BATCH_SIZE, false) do |batched_cons_ids|
|
141
|
+
cons_ids_concat = batched_cons_ids.join(',')
|
142
|
+
post_params = { cons_group_id: cons_group_id, cons_ids: cons_ids_concat }
|
143
|
+
if options[:wait_for_result]
|
144
|
+
connection.wait_for_deferred_result( connection.perform_request "/cons_group/#{method}", post_params, "POST" )
|
145
|
+
else
|
146
|
+
connection.perform_request "/cons_group/#{method}", post_params, "POST"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module BlueStateDigital
|
2
|
+
class Contribution < ApiDataModel
|
3
|
+
#TODO are we sure we want to raise an exception on save?
|
4
|
+
class ContributionExternalIdMissingException < StandardError
|
5
|
+
def initialize
|
6
|
+
super("Missing GUID for ID")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
class ContributionSaveFailureException < StandardError
|
10
|
+
def initialize(msg)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
class ContributionSaveValidationException < StandardError
|
15
|
+
def initialize(validation_errors)
|
16
|
+
error_messages = validation_errors.map do |id,msgs|
|
17
|
+
"Error for Contribution(ID: #{id}): #{msgs.join(', ')}. "
|
18
|
+
end
|
19
|
+
super(error_messages.join(', '))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
FIELDS = [
|
24
|
+
:external_id,
|
25
|
+
:prefix,:firstname,:middlename,:lastname,:suffix,
|
26
|
+
:transaction_dt,:transaction_amt,:cc_type_cd,:gateway_transaction_id,
|
27
|
+
:contribution_page_id,:stg_contribution_recurring_id,:contribution_page_slug,
|
28
|
+
:outreach_page_id,:source,:opt_compliance,
|
29
|
+
:addr1,:addr2,:city,:state_cd,:zip,:country,
|
30
|
+
:phone,:email,
|
31
|
+
:employer,:occupation,
|
32
|
+
:custom_fields
|
33
|
+
]
|
34
|
+
attr_accessor *FIELDS
|
35
|
+
|
36
|
+
def as_json(options={})
|
37
|
+
fields_to_exclude = []
|
38
|
+
fields_to_exclude << :contribution_page_id if contribution_page_id.nil?
|
39
|
+
fields_to_exclude << :contribution_page_slug if contribution_page_slug.nil?
|
40
|
+
super(options.merge({except: fields_to_exclude}))
|
41
|
+
end
|
42
|
+
|
43
|
+
def save
|
44
|
+
begin
|
45
|
+
if connection
|
46
|
+
response = connection.perform_request(
|
47
|
+
'/contribution/add_external_contribution',
|
48
|
+
{accept: 'application/json'},
|
49
|
+
'POST',
|
50
|
+
[self].to_json
|
51
|
+
)
|
52
|
+
begin
|
53
|
+
response = JSON.parse(response)
|
54
|
+
rescue
|
55
|
+
raise ContributionSaveFailureException.new(response)
|
56
|
+
end
|
57
|
+
if(response['summary']['missing_ids']>0)
|
58
|
+
raise ContributionExternalIdMissingException.new
|
59
|
+
elsif(response['summary']['failures']>0)
|
60
|
+
raise ContributionSaveValidationException.new(response['errors'])
|
61
|
+
end
|
62
|
+
else
|
63
|
+
raise NoConnectionException.new
|
64
|
+
end
|
65
|
+
#TODO shouldn't we be returning true or false and set the errors as in ActiveModel?
|
66
|
+
true
|
67
|
+
rescue => e
|
68
|
+
raise e
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module BlueStateDigital
|
2
|
+
class Dataset < ApiDataModel
|
3
|
+
|
4
|
+
extend ActiveModel::Naming
|
5
|
+
include ActiveModel::Validations
|
6
|
+
|
7
|
+
UPLOAD_ENDPOINT = '/cons/upload_dataset'
|
8
|
+
DELETE_ENDPOINT = '/cons/delete_dataset'
|
9
|
+
|
10
|
+
FIELDS = [
|
11
|
+
:dataset_id,
|
12
|
+
:map_type,
|
13
|
+
:slug,
|
14
|
+
:rows
|
15
|
+
]
|
16
|
+
attr_accessor *FIELDS
|
17
|
+
attr_reader :data,:data_header,:errors
|
18
|
+
|
19
|
+
validates_presence_of :map_type, :slug
|
20
|
+
validate :data_must_have_header
|
21
|
+
|
22
|
+
def initialize(args={})
|
23
|
+
super
|
24
|
+
@data = []
|
25
|
+
@data_header = nil
|
26
|
+
@errors = ActiveModel::Errors.new(self)
|
27
|
+
end
|
28
|
+
def data
|
29
|
+
@data
|
30
|
+
end
|
31
|
+
def add_data_row(row)
|
32
|
+
@data.push row
|
33
|
+
end
|
34
|
+
def add_data_header(header_row)
|
35
|
+
@data_header = header_row
|
36
|
+
end
|
37
|
+
def save
|
38
|
+
#errors.add(:data, "cannot be blank") if @data.blank?
|
39
|
+
if valid?
|
40
|
+
if connection
|
41
|
+
response = connection.perform_request_raw(
|
42
|
+
UPLOAD_ENDPOINT,
|
43
|
+
{api_ver: 2, slug: slug, map_type: map_type, content_type: 'text/csv', accept: 'application/json'},
|
44
|
+
"PUT",
|
45
|
+
csv_payload
|
46
|
+
)
|
47
|
+
if(response.status == 202)
|
48
|
+
true
|
49
|
+
else
|
50
|
+
errors.add(:web_service,"#{response.body}")
|
51
|
+
false
|
52
|
+
end
|
53
|
+
else
|
54
|
+
errors.add(:connection,"is missing")
|
55
|
+
false
|
56
|
+
end
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def delete
|
64
|
+
if dataset_id.nil?
|
65
|
+
errors.add(:dataset_id,"is missing")
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
if connection
|
69
|
+
response = connection.perform_request_raw(
|
70
|
+
DELETE_ENDPOINT,
|
71
|
+
{api_ver: 2},
|
72
|
+
"POST",
|
73
|
+
{dataset_id: dataset_id}.to_json
|
74
|
+
)
|
75
|
+
if(response.status==200)
|
76
|
+
true
|
77
|
+
else
|
78
|
+
errors.add(:web_service,"#{response.body}")
|
79
|
+
false
|
80
|
+
end
|
81
|
+
else
|
82
|
+
errors.add(:connection,"is missing")
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def read_attribute_for_validation(attr)
|
88
|
+
send(attr)
|
89
|
+
end
|
90
|
+
def self.human_attribute_name(attr, options = {})
|
91
|
+
attr
|
92
|
+
end
|
93
|
+
def self.lookup_ancestors
|
94
|
+
[self]
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def csv_payload
|
100
|
+
csv_string = CSV.generate do |csv|
|
101
|
+
csv << (@data_header||[])
|
102
|
+
@data.each do |row|
|
103
|
+
csv << row
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def data_must_have_header
|
109
|
+
errors.add(:data_header, "is missing") if !@data.blank? && @data_header.nil?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Datasets < CollectionResource
|
114
|
+
|
115
|
+
FETCH_ENDPOINT = '/cons/list_datasets'
|
116
|
+
|
117
|
+
def get_datasets
|
118
|
+
if connection
|
119
|
+
response = connection.perform_request FETCH_ENDPOINT, { api_ver: 2 }, "GET"
|
120
|
+
|
121
|
+
# TODO: Should check response's status code
|
122
|
+
begin
|
123
|
+
parsed_response = JSON.parse(response)
|
124
|
+
|
125
|
+
data = parsed_response['data']
|
126
|
+
if(data)
|
127
|
+
data.map {|dataset| Dataset.new(dataset) }
|
128
|
+
else
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
rescue Exception => e
|
132
|
+
raise FetchFailureException.new(e)
|
133
|
+
end
|
134
|
+
else
|
135
|
+
raise NoConnectionException.new
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|