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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +14 -0
  6. data/Gemfile.lock +64 -0
  7. data/README.rdoc +44 -0
  8. data/Rakefile +32 -0
  9. data/fellowshipone_api.gemspec +25 -0
  10. data/lib/api/api_object.rb +141 -0
  11. data/lib/api/communication.rb +59 -0
  12. data/lib/api/contribution.rb +97 -0
  13. data/lib/api/contribution_list.rb +57 -0
  14. data/lib/api/fund.rb +40 -0
  15. data/lib/api/fund_list.rb +53 -0
  16. data/lib/api/household.rb +50 -0
  17. data/lib/api/household_list.rb +78 -0
  18. data/lib/api/member_household_list.rb +73 -0
  19. data/lib/api/mergeable_household_list.rb +79 -0
  20. data/lib/api/mergeable_person_list.rb +110 -0
  21. data/lib/api/person.rb +169 -0
  22. data/lib/api/person_list.rb +85 -0
  23. data/lib/api/search.rb +55 -0
  24. data/lib/auto_load.rb +17 -0
  25. data/lib/common.rb +76 -0
  26. data/lib/exceptions.rb +5 -0
  27. data/lib/fellowship_one.rb +54 -0
  28. data/lib/oauth_monkey_patch.rb +13 -0
  29. data/lib/readers/api_reader.rb +34 -0
  30. data/lib/readers/communication_reader.rb +19 -0
  31. data/lib/readers/contribution_list_reader.rb +37 -0
  32. data/lib/readers/contribution_reader.rb +18 -0
  33. data/lib/readers/fund_list_reader.rb +13 -0
  34. data/lib/readers/fund_reader.rb +14 -0
  35. data/lib/readers/household_list_reader.rb +62 -0
  36. data/lib/readers/household_reader.rb +18 -0
  37. data/lib/readers/member_household_list_reader.rb +27 -0
  38. data/lib/readers/person_list_reader.rb +37 -0
  39. data/lib/readers/person_reader.rb +20 -0
  40. data/lib/writers/api_writer.rb +64 -0
  41. data/lib/writers/communication_writer.rb +27 -0
  42. data/lib/writers/contribution_writer.rb +28 -0
  43. data/lib/writers/household_writer.rb +30 -0
  44. data/lib/writers/person_writer.rb +64 -0
  45. data/spec/api/person_spec.rb +21 -0
  46. data/spec/functional/fellowship_one_spec.rb +17 -0
  47. data/spec/readers/api_reader_spec.rb +7 -0
  48. data/spec/spec_helper.rb +47 -0
  49. data/spec/vcr_setup.rb +5 -0
  50. data/spec/writers/api_writer_spec.rb +7 -0
  51. 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,5 @@
1
+ module FellowshipOneExceptions
2
+ class UnableToConnectToFellowshipOne < StandardError; end
3
+ class UnknownErrorConnectingToFellowshipOne < StandardError; end
4
+ class FellowshipOneResponseError < StandardError; end
5
+ end
@@ -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