fellowshipone-api 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 +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +64 -0
- data/README.rdoc +44 -0
- data/Rakefile +32 -0
- data/fellowshipone_api.gemspec +25 -0
- data/lib/api/api_object.rb +141 -0
- data/lib/api/communication.rb +59 -0
- data/lib/api/contribution.rb +97 -0
- data/lib/api/contribution_list.rb +57 -0
- data/lib/api/fund.rb +40 -0
- data/lib/api/fund_list.rb +53 -0
- data/lib/api/household.rb +50 -0
- data/lib/api/household_list.rb +78 -0
- data/lib/api/member_household_list.rb +73 -0
- data/lib/api/mergeable_household_list.rb +79 -0
- data/lib/api/mergeable_person_list.rb +110 -0
- data/lib/api/person.rb +169 -0
- data/lib/api/person_list.rb +85 -0
- data/lib/api/search.rb +55 -0
- data/lib/auto_load.rb +17 -0
- data/lib/common.rb +76 -0
- data/lib/exceptions.rb +5 -0
- data/lib/fellowship_one.rb +54 -0
- data/lib/oauth_monkey_patch.rb +13 -0
- data/lib/readers/api_reader.rb +34 -0
- data/lib/readers/communication_reader.rb +19 -0
- data/lib/readers/contribution_list_reader.rb +37 -0
- data/lib/readers/contribution_reader.rb +18 -0
- data/lib/readers/fund_list_reader.rb +13 -0
- data/lib/readers/fund_reader.rb +14 -0
- data/lib/readers/household_list_reader.rb +62 -0
- data/lib/readers/household_reader.rb +18 -0
- data/lib/readers/member_household_list_reader.rb +27 -0
- data/lib/readers/person_list_reader.rb +37 -0
- data/lib/readers/person_reader.rb +20 -0
- data/lib/writers/api_writer.rb +64 -0
- data/lib/writers/communication_writer.rb +27 -0
- data/lib/writers/contribution_writer.rb +28 -0
- data/lib/writers/household_writer.rb +30 -0
- data/lib/writers/person_writer.rb +64 -0
- data/spec/api/person_spec.rb +21 -0
- data/spec/functional/fellowship_one_spec.rb +17 -0
- data/spec/readers/api_reader_spec.rb +7 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/vcr_setup.rb +5 -0
- data/spec/writers/api_writer_spec.rb +7 -0
- metadata +129 -0
data/lib/api/person.rb
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
module FellowshipOne
|
2
|
+
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
PersonAddress = Struct.new(:is_primary?, :street, :street2, :city, :state, :postal_code)
|
6
|
+
PersonCommunication = Struct.new(:is_phone?, :is_mobile?, :is_email?, :type, :value, :preferred?)
|
7
|
+
|
8
|
+
class Person < ApiObject
|
9
|
+
attr_accessor :addresses,
|
10
|
+
:communications
|
11
|
+
|
12
|
+
f1_attr_accessor :id,
|
13
|
+
:uri,
|
14
|
+
:image_uri,
|
15
|
+
:old_id,
|
16
|
+
:i_code,
|
17
|
+
:household_id,
|
18
|
+
:old_household_id,
|
19
|
+
:title,
|
20
|
+
:salutation,
|
21
|
+
:prefix,
|
22
|
+
:first_name,
|
23
|
+
:last_name,
|
24
|
+
:suffix,
|
25
|
+
:middle_name,
|
26
|
+
:goes_by_name,
|
27
|
+
:former_name,
|
28
|
+
:gender,
|
29
|
+
:date_of_birth,
|
30
|
+
:marital_status,
|
31
|
+
:household_member_type,
|
32
|
+
:is_authorized,
|
33
|
+
:status,
|
34
|
+
:occupation,
|
35
|
+
:employer,
|
36
|
+
:school,
|
37
|
+
:denomination,
|
38
|
+
:former_church,
|
39
|
+
:bar_code,
|
40
|
+
:member_envelope_code,
|
41
|
+
:default_tag_comment,
|
42
|
+
:weblink,
|
43
|
+
:solicit,
|
44
|
+
:thank,
|
45
|
+
:first_record,
|
46
|
+
:last_match_date,
|
47
|
+
:created_date,
|
48
|
+
:last_updated_date
|
49
|
+
|
50
|
+
# Loads the user by the specified ID.
|
51
|
+
#
|
52
|
+
# @param person_id The ID of the person to load.
|
53
|
+
#
|
54
|
+
# Returns a new Person object.
|
55
|
+
def self.load_by_id(person_id)
|
56
|
+
reader = PersonReader.new(person_id)
|
57
|
+
self.new(reader)
|
58
|
+
rescue
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
# Constructor.
|
64
|
+
#
|
65
|
+
# @param reader (optional) The object that has the data. This can be a PersonReader or Hash object.
|
66
|
+
# @param options (optional) Options for including more information.
|
67
|
+
def initialize(reader = nil, options = {})
|
68
|
+
@writer_object = PersonWriter
|
69
|
+
if reader.is_a?(PersonReader)
|
70
|
+
initialize_from_json_object(reader.load_feed['person'])
|
71
|
+
elsif reader.is_a?(Hash)
|
72
|
+
initialize_from_json_object(reader)
|
73
|
+
else # new empty
|
74
|
+
reader = PersonReader.new
|
75
|
+
initialize_from_json_object(reader.load_new['person'])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def is_head_of_household?
|
81
|
+
(!self.household_member_type.nil? and self.household_member_type['name'].downcase == 'head')
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def family_role
|
86
|
+
begin
|
87
|
+
self.household_member_type['name']
|
88
|
+
rescue
|
89
|
+
'Other'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def is_child?
|
95
|
+
begin
|
96
|
+
self.household_member_type['name'].downcase == 'child'
|
97
|
+
rescue
|
98
|
+
false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def addresses
|
104
|
+
return nil if @addresses.nil?
|
105
|
+
return @addresses_cache unless @addresses_cache.nil?
|
106
|
+
|
107
|
+
@addresses_cache = []
|
108
|
+
@addresses['address'].each do |addr|
|
109
|
+
@addresses_cache << PersonAddress.new(
|
110
|
+
addr['addressType']['name'].downcase == 'primary',
|
111
|
+
addr['address1'],
|
112
|
+
addr['address2'],
|
113
|
+
addr['city'],
|
114
|
+
addr['stProvince'],
|
115
|
+
addr['postalCode']
|
116
|
+
)
|
117
|
+
end
|
118
|
+
@addresses_cache
|
119
|
+
end
|
120
|
+
|
121
|
+
def email_addresses
|
122
|
+
self.communications.collect { |c| c[:is_email?] ? c[:value] : nil }.compact
|
123
|
+
end
|
124
|
+
|
125
|
+
def all_numbers
|
126
|
+
self.communications.collect { |c| (c[:is_phone?] or c[:is_mobile?]) ? c[:value] : nil }.compact
|
127
|
+
end
|
128
|
+
|
129
|
+
def phone_numbers
|
130
|
+
self.communications.collect { |c| c[:is_phone?] ? c[:value] : nil }.compact
|
131
|
+
end
|
132
|
+
|
133
|
+
def mobile_numbers
|
134
|
+
self.communications.collect { |c| c[:is_mobile?] ? c[:value] : nil }.compact
|
135
|
+
end
|
136
|
+
|
137
|
+
def communications
|
138
|
+
return nil if @communications.nil?
|
139
|
+
return @communications_cache unless @communications_cache.nil?
|
140
|
+
|
141
|
+
@communications_cache = []
|
142
|
+
@communications['communication'].each do |comm|
|
143
|
+
@communications_cache << PersonCommunication.new(
|
144
|
+
comm['communicationType']['name'].to_s.downcase.include?('phone'),
|
145
|
+
comm['communicationType']['name'].to_s.downcase.include?('mobile'),
|
146
|
+
comm['communicationType']['name'].to_s.downcase.include?('email'),
|
147
|
+
comm['communicationType']['name'],
|
148
|
+
comm['communicationValue'],
|
149
|
+
comm['preferred'].to_s.downcase == 'true'
|
150
|
+
)
|
151
|
+
end
|
152
|
+
@communications_cache
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def _field_map
|
157
|
+
{:id => '@id',
|
158
|
+
:uri => '@uri',
|
159
|
+
:imageUri => '@imageURI',
|
160
|
+
:oldId => '@oldID',
|
161
|
+
:iCode => '@iCode',
|
162
|
+
:householdId => '@householdID',
|
163
|
+
:oldHouseholdId => '@oldHouseholdID'}
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module FellowshipOne
|
2
|
+
|
3
|
+
class PersonList
|
4
|
+
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :count, :page_number, :total_records, :additional_pages
|
8
|
+
|
9
|
+
|
10
|
+
# Constructor.
|
11
|
+
#
|
12
|
+
# @param options A hash of options for loading the list.
|
13
|
+
#
|
14
|
+
# Options:
|
15
|
+
# :page - (optional) The page number to get.
|
16
|
+
# :reader - (optional) The Reader to use to load the data.
|
17
|
+
def initialize(options)
|
18
|
+
#options[:page] ||= 1
|
19
|
+
reader = options[:reader] || FellowshipOne::PersonListReader.new(options)
|
20
|
+
@json_data = reader.load_feed
|
21
|
+
|
22
|
+
@count = @json_data['@count'].to_i
|
23
|
+
@page_number = @json_data['@pageNumber'].to_i
|
24
|
+
@total_records = @json_data['@totalRecords'].to_i
|
25
|
+
@additional_pages = @json_data['@additionalPages'].to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# All the people in the list.
|
30
|
+
#
|
31
|
+
# @return array of names (first last).
|
32
|
+
def all_names
|
33
|
+
return [] unless @json_data['person']
|
34
|
+
@json_data['person'].collect { |person| [person['firstName'], person['lastName']].join(' ') }
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :names, :all_names
|
38
|
+
|
39
|
+
|
40
|
+
# Get the specified person.
|
41
|
+
#
|
42
|
+
# @param index The index of the person to get.
|
43
|
+
#
|
44
|
+
# @return [Person]
|
45
|
+
def [](index)
|
46
|
+
Person.new( @json_data['person'][index] ) if @json_data['person'] and @json_data['person'][index]
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# This method is needed for Enumerable.
|
51
|
+
def each &block
|
52
|
+
@json_data['person'].each{ |person| yield( Person.new(person) )}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Alias the count method
|
56
|
+
alias :size :count
|
57
|
+
|
58
|
+
# Checks if the list is empty.
|
59
|
+
#
|
60
|
+
# @return True on empty, false otherwise.
|
61
|
+
def empty?
|
62
|
+
#@json_data['person'].empty?
|
63
|
+
self.count == 0 ? true : false
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# Get all the people ids in the list.
|
68
|
+
#
|
69
|
+
# @return An array of people ids.
|
70
|
+
def ids
|
71
|
+
(@json_data['person'].collect { |person| person['@id'] }).uniq
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Access to the raw JSON data. This method is needed for merging lists.
|
76
|
+
#
|
77
|
+
# @returns Raw JSON data.
|
78
|
+
def raw_data
|
79
|
+
@json_data
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
data/lib/api/search.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module FellowshipOne
|
2
|
+
|
3
|
+
class Search
|
4
|
+
|
5
|
+
def self.search_for_contributions_by_date(start_date, end_date, page=1, per_page=500)
|
6
|
+
options = {:start_date => start_date, :end_date => end_date, :page => page, :per_page => per_page}
|
7
|
+
reader = FellowshipOne::ContributionListReader.new(options)
|
8
|
+
ContributionList.new({:reader => reader})
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.search_for_contributions_by_household_id(household_id, page=1, per_page=500)
|
12
|
+
options = {:household_id => household_id, :page => page, :per_page => per_page}
|
13
|
+
reader = FellowshipOne::ContributionListReader.new(options)
|
14
|
+
ContributionList.new({:reader => reader})
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.search_for_contributions_by_individual_id(individual_id, page=1, per_page=500)
|
18
|
+
options = {:individual_id => individual_id, :page => page, :per_page => per_page}
|
19
|
+
reader = FellowshipOne::ContributionListReader.new(options)
|
20
|
+
ContributionList.new({:reader => reader})
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.search_for_person_by_name(query, page=1)
|
24
|
+
options = {:page => page, :search_for => query}
|
25
|
+
reader = FellowshipOne::PersonListReader.new(options)
|
26
|
+
PersonList.new({:reader => reader})
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.search_for_person_by_household_id(query, page=1)
|
30
|
+
options = {:page => page, :hsdid => query}
|
31
|
+
reader = FellowshipOne::PersonListReader.new(options)
|
32
|
+
PersonList.new({:reader => reader})
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.search_for_person_by_communication(query, page=1)
|
36
|
+
options = {:page => page, :communication => query}
|
37
|
+
reader = FellowshipOne::PersonListReader.new(options)
|
38
|
+
PersonList.new({:reader => reader})
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.search_for_person_created_on_or_after(created_date, page=1)
|
42
|
+
options = {:page => page, :created_date => created_date}
|
43
|
+
reader = FellowshipOne::PersonListReader.new(options)
|
44
|
+
PersonList.new({:reader => reader})
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.search_for_household_by_name(name, page=1)
|
48
|
+
options = {:page => page, :search_for => name}
|
49
|
+
reader = FellowshipOne::HouseholdListReader.new(options)
|
50
|
+
HouseholdList.new({:reader => reader})
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/lib/auto_load.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module FellowshipOne
|
2
|
+
|
3
|
+
require FELLOWSHIPONE_LIB_DIR + '/exceptions.rb'
|
4
|
+
|
5
|
+
api_path = FELLOWSHIPONE_LIB_DIR + '/api/'
|
6
|
+
require api_path + 'api_object.rb'
|
7
|
+
Dir["#{api_path}/*.rb"].each { |f| require(f) }
|
8
|
+
|
9
|
+
readers_path = FELLOWSHIPONE_LIB_DIR + '/readers/'
|
10
|
+
require readers_path + 'api_reader.rb'
|
11
|
+
Dir["#{readers_path}/*.rb"].each { |f| require(f) }
|
12
|
+
|
13
|
+
writers_path = FELLOWSHIPONE_LIB_DIR + '/writers/'
|
14
|
+
require writers_path + 'api_writer.rb'
|
15
|
+
Dir["#{writers_path}/*.rb"].each { |f| require(f) }
|
16
|
+
|
17
|
+
end
|
data/lib/common.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module FellowshipOne
|
2
|
+
require 'json'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
def self.api_request(method, path, params = {}, body = '')
|
6
|
+
# response = case method
|
7
|
+
# when :post
|
8
|
+
# Typhoeus::Request.post(url, {:headers => self._build_api_headers, :body => body})
|
9
|
+
# when :get
|
10
|
+
# Typhoeus::Request.get(url, {:headers => self._build_api_headers, :params => params})
|
11
|
+
# when :put
|
12
|
+
# Typhoeus::Request.put(url, {:headers => self._build_api_headers, :body => body})
|
13
|
+
# when :delete
|
14
|
+
# Typhoeus::Request.delete(url, {:headers => self._build_api_headers, :params => params})
|
15
|
+
# end
|
16
|
+
|
17
|
+
response = self._oauth_request_get(method, path, params, body)
|
18
|
+
|
19
|
+
unless response.success?
|
20
|
+
puts response.inspect
|
21
|
+
if response.code > 0
|
22
|
+
raise FellowshipOneExceptions::UnableToConnectToFellowshipOne.new(response.body)
|
23
|
+
else
|
24
|
+
begin
|
25
|
+
error_messages = JSON.parse(response.body)['error_message']
|
26
|
+
rescue
|
27
|
+
response_code_desc = response.headers.partition("\r\n")[0].sub(/^\S+/, '') rescue nil
|
28
|
+
raise FellowshipOneExceptions::UnknownErrorConnectingToFellowshipOne.new("Unknown error when connecting to FellowshipOne API. #{response_code_desc}")
|
29
|
+
else
|
30
|
+
raise FellowshipOneExceptions::FellowshipOneResponseError.new(error_messages)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
response
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def self._oauth_request_get(method, path, params, body)
|
40
|
+
consumer_env = FellowshipOne::Api.is_production ? '' : '.staging'
|
41
|
+
base_url = "https://#{FellowshipOne::Api.church_code}#{consumer_env}.fellowshiponeapi.com"
|
42
|
+
url = base_url + path
|
43
|
+
if method == :get
|
44
|
+
url_params = params.collect { |k, v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&')
|
45
|
+
url_params = nil if url_params.empty?
|
46
|
+
url = [url, url_params].compact.join('?')
|
47
|
+
params = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
consumer = OAuth::Consumer.new(FellowshipOne::Api.consumer_key,
|
51
|
+
FellowshipOne::Api.consumer_secret,
|
52
|
+
:site => base_url,
|
53
|
+
:http_method => method)
|
54
|
+
access_token = OAuth::AccessToken.new(consumer, FellowshipOne::Api.api_token, FellowshipOne::Api.api_secret)
|
55
|
+
|
56
|
+
options = {:params => params, :method => method, :body => body}
|
57
|
+
oauth_params = {:consumer => consumer, :token => access_token}
|
58
|
+
hydra = Typhoeus::Hydra.new
|
59
|
+
req = Typhoeus::Request.new(url, options)
|
60
|
+
|
61
|
+
# {'Content-Type' => 'application/vnd.fellowshiponeapi.com.people.people.v2+json'}
|
62
|
+
# req.headers.merge!({'Content-Type' => 'application/json'})
|
63
|
+
req.options[:headers].merge!({'Content-Type' => 'application/json'})
|
64
|
+
oauth_helper = OAuth::Client::Helper.new(req, oauth_params.merge(:request_uri => url))
|
65
|
+
|
66
|
+
#req.headers.merge!({"Authorization" => oauth_helper.header})
|
67
|
+
req.options[:headers].merge!({"Authorization" => oauth_helper.header}) # Signs the request
|
68
|
+
|
69
|
+
hydra.queue(req)
|
70
|
+
hydra.run
|
71
|
+
|
72
|
+
req.response
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
data/lib/exceptions.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
require 'yaml'
|
3
|
+
require 'typhoeus'
|
4
|
+
require 'oauth/request_proxy/typhoeus_request'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
# The path to the lib directory.
|
8
|
+
FELLOWSHIPONE_LIB_DIR = File.dirname(__FILE__)
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/auto_load.rb'
|
11
|
+
|
12
|
+
require File.dirname(__FILE__) + '/common.rb'
|
13
|
+
|
14
|
+
require File.dirname(__FILE__) + '/oauth_monkey_patch'
|
15
|
+
|
16
|
+
module FellowshipOne
|
17
|
+
|
18
|
+
class Api
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_reader :church_code, :consumer_key, :consumer_secret, :api_token, :api_secret, :is_production
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.establish_connection(church_code, consumer_key, consumer_secret, callback_url, production = true)
|
25
|
+
if church_code.nil? or consumer_key.nil? or consumer_secret.nil?
|
26
|
+
raise FellowshipOneExceptions::UnableToConnectToFellowshipOne.new('Church code, Consumer Key and Consumer Secret cannot be nil.')
|
27
|
+
end
|
28
|
+
|
29
|
+
consumer_env = production ? '' : '.staging' # Yes, blank is production
|
30
|
+
|
31
|
+
puts "F1 URL: https://#{church_code}#{consumer_env}.fellowshiponeapi.com"
|
32
|
+
|
33
|
+
consumer = OAuth::Consumer.new(consumer_key, consumer_secret,
|
34
|
+
:site => "https://#{church_code}#{consumer_env}.fellowshiponeapi.com",
|
35
|
+
:request_token_path => '/v1/Tokens/RequestToken',
|
36
|
+
:authorize_path => '/v1/PortalUser/Login',
|
37
|
+
:access_token_path => '/v1/Tokens/AccessToken')
|
38
|
+
|
39
|
+
consumer.get_request_token(:oauth_callback => callback_url)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.connect(church_code, consumer_key, consumer_secret, oauth_token, oauth_secret, production = true)
|
43
|
+
raise FellowshipOneExceptions::UnableToConnectToFellowshipOne.new('Church Code, Token and Secret cannot be nil.') if oauth_token.nil? or oauth_secret.nil?
|
44
|
+
@church_code = church_code
|
45
|
+
@consumer_key = consumer_key
|
46
|
+
@consumer_secret = consumer_secret
|
47
|
+
@api_token = oauth_token
|
48
|
+
@api_secret = oauth_secret
|
49
|
+
@is_production = production
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|