activeforce 1.5.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 (86) hide show
  1. data/Gemfile +15 -0
  2. data/Gemfile.lock +128 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +112 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/activeforce.gemspec +151 -0
  8. data/app/models/salesforce/account.rb +4 -0
  9. data/app/models/salesforce/activity_history.rb +4 -0
  10. data/app/models/salesforce/approval.rb +4 -0
  11. data/app/models/salesforce/campaign.rb +4 -0
  12. data/app/models/salesforce/campaign_feed.rb +4 -0
  13. data/app/models/salesforce/campaign_member.rb +4 -0
  14. data/app/models/salesforce/case.rb +4 -0
  15. data/app/models/salesforce/case_comment.rb +4 -0
  16. data/app/models/salesforce/case_contact_role.rb +4 -0
  17. data/app/models/salesforce/case_feed.rb +4 -0
  18. data/app/models/salesforce/case_history.rb +4 -0
  19. data/app/models/salesforce/case_share.rb +4 -0
  20. data/app/models/salesforce/case_solution.rb +4 -0
  21. data/app/models/salesforce/case_status.rb +4 -0
  22. data/app/models/salesforce/case_team_member.rb +4 -0
  23. data/app/models/salesforce/community.rb +4 -0
  24. data/app/models/salesforce/contact.rb +4 -0
  25. data/app/models/salesforce/contact_feed.rb +4 -0
  26. data/app/models/salesforce/contact_history.rb +4 -0
  27. data/app/models/salesforce/contract.rb +4 -0
  28. data/app/models/salesforce/document.rb +4 -0
  29. data/app/models/salesforce/event.rb +4 -0
  30. data/app/models/salesforce/feed_item.rb +4 -0
  31. data/app/models/salesforce/group.rb +4 -0
  32. data/app/models/salesforce/group_member.rb +4 -0
  33. data/app/models/salesforce/idea.rb +4 -0
  34. data/app/models/salesforce/lead.rb +4 -0
  35. data/app/models/salesforce/lead_status.rb +4 -0
  36. data/app/models/salesforce/name.rb +4 -0
  37. data/app/models/salesforce/note.rb +4 -0
  38. data/app/models/salesforce/open_activity.rb +4 -0
  39. data/app/models/salesforce/opportunity.rb +4 -0
  40. data/app/models/salesforce/organization.rb +4 -0
  41. data/app/models/salesforce/partner.rb +4 -0
  42. data/app/models/salesforce/period.rb +4 -0
  43. data/app/models/salesforce/product2.rb +4 -0
  44. data/app/models/salesforce/product2_feed.rb +4 -0
  45. data/app/models/salesforce/profile.rb +4 -0
  46. data/app/models/salesforce/quote.rb +4 -0
  47. data/app/models/salesforce/solution.rb +4 -0
  48. data/app/models/salesforce/task.rb +4 -0
  49. data/app/models/salesforce/task_feed.rb +4 -0
  50. data/app/models/salesforce/task_priority.rb +4 -0
  51. data/app/models/salesforce/task_status.rb +4 -0
  52. data/app/models/salesforce/user.rb +4 -0
  53. data/app/models/salesforce/user_role.rb +4 -0
  54. data/app/models/salesforce/vote.rb +4 -0
  55. data/lib/activeforce.rb +29 -0
  56. data/lib/salesforce/attributes.rb +13 -0
  57. data/lib/salesforce/authentication.rb +25 -0
  58. data/lib/salesforce/base.rb +240 -0
  59. data/lib/salesforce/bulk/batch.rb +77 -0
  60. data/lib/salesforce/bulk/job.rb +103 -0
  61. data/lib/salesforce/bulk/operations.rb +25 -0
  62. data/lib/salesforce/bulk/update_job.rb +24 -0
  63. data/lib/salesforce/column.rb +98 -0
  64. data/lib/salesforce/columns.rb +60 -0
  65. data/lib/salesforce/config.rb +110 -0
  66. data/lib/salesforce/connection.rb +33 -0
  67. data/lib/salesforce/connection/async.rb +36 -0
  68. data/lib/salesforce/connection/conversion.rb +37 -0
  69. data/lib/salesforce/connection/http_methods.rb +72 -0
  70. data/lib/salesforce/connection/rest_api.rb +52 -0
  71. data/lib/salesforce/connection/soap_api.rb +74 -0
  72. data/lib/salesforce/engine.rb +6 -0
  73. data/lib/salesforce/errors.rb +33 -0
  74. data/test/salesforce/authentication_test.rb +52 -0
  75. data/test/salesforce/base_test.rb +440 -0
  76. data/test/salesforce/bulk/batch_test.rb +79 -0
  77. data/test/salesforce/bulk/update_job_test.rb +125 -0
  78. data/test/salesforce/column_test.rb +115 -0
  79. data/test/salesforce/config_test.rb +163 -0
  80. data/test/salesforce/connection/async_test.rb +138 -0
  81. data/test/salesforce/connection/http_methods_test.rb +242 -0
  82. data/test/salesforce/connection/rest_api_test.rb +61 -0
  83. data/test/salesforce/connection/soap_api_test.rb +148 -0
  84. data/test/salesforce/connection_test.rb +148 -0
  85. data/test/test_helper.rb +79 -0
  86. metadata +295 -0
@@ -0,0 +1,33 @@
1
+ require 'salesforce/connection/soap_api'
2
+ require 'salesforce/connection/rest_api'
3
+ require 'salesforce/connection/http_methods'
4
+ require 'salesforce/connection/conversion'
5
+ require 'salesforce/connection/async'
6
+
7
+ module Salesforce
8
+ module Connection
9
+ include SoapApi
10
+ include RestApi
11
+ include HttpMethods
12
+ include Conversion
13
+ include Async
14
+
15
+ def self.as_logged_in_user(&block)
16
+ count = 0
17
+ begin
18
+ Salesforce::Authentication.session_id
19
+ block.call
20
+ rescue RestClient::Request::Unauthorized, Savon::SOAP::Fault => e
21
+ if count < 1 && (e.message.downcase.include?("unauthorized") || e.message.downcase.include?("invalid_login"))
22
+ count += 1
23
+ Salesforce::Config.on_login_failure
24
+ Salesforce::Authentication.logout
25
+ retry
26
+ else
27
+ raise e
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ module Salesforce
2
+ module Connection
3
+ module Async
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def async_post(path, body, options = {})
8
+ url = async_api_url(path)
9
+ as_logged_in_user do
10
+ convert_body(RestClient.post(url, body, async_headers(options)), options)
11
+ end
12
+ rescue RestClient::ResourceNotFound, RestClient::BadRequest => e
13
+ convert_error(e, url, options)
14
+ end
15
+
16
+ def async_get(path, options = {})
17
+ url = async_api_url(path)
18
+ as_logged_in_user do
19
+ convert_body RestClient.get(url, async_headers(options)), options
20
+ end
21
+ rescue RestClient::ResourceNotFound, RestClient::BadRequest => e
22
+ convert_error(e, url, options)
23
+ end
24
+
25
+ def async_api_url(path)
26
+ ::Salesforce::Config.async_url + "/" + path
27
+ end
28
+
29
+ def async_headers(options)
30
+ { 'X-SFDC-Session' => Salesforce::Authentication.session_id }.merge(content_type_headers(options))
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ module Salesforce
2
+ module Connection
3
+ module Conversion
4
+ extend ActiveSupport::Concern
5
+ module ClassMethods
6
+
7
+ def convert_body(response, options)
8
+ convert(response.body, options)
9
+ end
10
+
11
+ def convert_error(error, url, options)
12
+ error_message = convert(error.http_body, options)
13
+ error_message = error_message.first if error_message.is_a?(Array)
14
+ error_message = error_message["Error"] if error_message.is_a?(Hash) && error_message["Error"]
15
+ raise InvalidRequest.new(error_message, url)
16
+ end
17
+
18
+ def convert(body, options)
19
+ if options[:format] == :json
20
+ ActiveSupport::JSON.decode(body)
21
+ elsif options[:format] == :xml
22
+ result = Hash.from_xml(body)
23
+ if result.is_a?(Hash) && result.keys.size == 1
24
+ result[result.keys.first].with_indifferent_access
25
+ else
26
+ result.with_indifferent_access
27
+ end
28
+ else
29
+ body
30
+ end
31
+ end
32
+
33
+ end #ClassMethods
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,72 @@
1
+ module Salesforce
2
+ module Connection
3
+ module HttpMethods
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ def headers(options = {})
9
+ {'Authorization' => "OAuth #{::Salesforce::Authentication.session_id}"}.merge(content_type_headers(options))
10
+ end
11
+
12
+ def get(path, options = {})
13
+ http(:get, path, nil, options)
14
+ end
15
+
16
+ def patch(path, body, options = {})
17
+ http(:patch, path, body, options)
18
+ end
19
+
20
+ def post(path, body, options = {})
21
+ http(:post, path, body, options)
22
+ end
23
+
24
+ def delete(path)
25
+ true if http(:delete, path, nil, :format => :xml)
26
+ end
27
+
28
+ def salesforce_url(path)
29
+ if path.include?("services/data")
30
+ ::Salesforce::Config.server_host + path
31
+ else
32
+ ::Salesforce::Config.server_url + "/" + path
33
+ end
34
+ end
35
+
36
+ def http(action, path, body, options)
37
+ as_logged_in_user do
38
+ begin
39
+ response = if body
40
+ RestClient.send(action, salesforce_url(path), body, headers(options))
41
+ else
42
+ RestClient.send(action, salesforce_url(path), headers(options))
43
+ end
44
+
45
+ if response.body.present?
46
+ convert_body(response, options)
47
+ else
48
+ response
49
+ end
50
+ rescue RestClient::ResourceNotFound, RestClient::BadRequest => e
51
+ convert_error(e, salesforce_url(path), options)
52
+ end
53
+ end
54
+ end
55
+
56
+ def content_type_headers(options)
57
+ {}.tap do |hash|
58
+ hash[:content_type] = if options[:content_type]
59
+ options[:content_type]
60
+ elsif options[:format].to_s == 'json'
61
+ "application/json"
62
+ elsif options[:format].to_s == 'xml'
63
+ "application/xml"
64
+ end
65
+ end
66
+ end
67
+
68
+ end #ClassMethods
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,52 @@
1
+ module Salesforce
2
+ module Connection
3
+ module RestApi
4
+ extend ActiveSupport::Concern
5
+ module ClassMethods
6
+
7
+ def supported_objects
8
+ get("sobjects.json", :format => :json)["sobjects"].map { |hash| hash["name"] }
9
+ end
10
+
11
+ def fields(object_name)
12
+ raise ObjectNotSupported.new(object_name) unless supported_objects.include?(object_name)
13
+ get("sobjects/#{object_name}/describe.json", :format => :json)["fields"]
14
+ end
15
+
16
+ def find_object_by_id(object_name, object_id, fields)
17
+ get("sobjects/#{object_name}/#{object_id}.json?fields=#{URI.encode(fields)}", :format => :json)
18
+ end
19
+
20
+ def soql(query_string)
21
+ records = []
22
+ response = get "query.json?q=#{CGI.escape(query_string)}", :format => :json
23
+ records += response["records"].each { |r| r.delete("attributes")}
24
+ while(!response["done"])
25
+ response = get((response["nextRecordsUrl"] + '.json'), :format => :json)
26
+ records += response["records"].each { |r| r.delete("attributes")}
27
+ end
28
+ records
29
+ end
30
+
31
+ def update(object_name, object_id, fields)
32
+ response = patch("sobjects/#{object_name}/#{object_id}.json", fields.to_json, :format => :json)
33
+ response.code == 204
34
+ end
35
+
36
+ def create(object_name, fields)
37
+ result = post("sobjects/#{object_name}.json", fields.to_json, :format => :json)
38
+ if result["success"]
39
+ return result["id"]
40
+ else
41
+ raise RecordInvalid.new(object_name, result["errors"])
42
+ end
43
+ end
44
+
45
+ def destroy(object_name, object_id)
46
+ delete("sobjects/#{object_name}/#{object_id}")
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,74 @@
1
+ module Salesforce
2
+ module Connection
3
+ module SoapApi
4
+ extend ActiveSupport::Concern
5
+
6
+ class Error < RuntimeError; end
7
+
8
+ module ClassMethods
9
+
10
+ def login
11
+ options = {
12
+ :endpoint_url => Config.login_url,
13
+ :body => {
14
+ :username => Config.username,
15
+ :password => Config.password,
16
+ :order! => [ :username, :password ]
17
+ }
18
+ }
19
+
20
+ invoke_soap(:login, options)
21
+ end
22
+
23
+ def convert_lead(lead_converts)
24
+ options = { :body => { :leadConverts => lead_converts } }
25
+
26
+ as_logged_in_user do
27
+ invoke_soap(:convertLead, options)
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def invoke_soap(method_name, options)
34
+ options[:namespace] ||= Config.soap_enterprise_namespace
35
+ options[:endpoint_url] ||= Config.soap_endpoint_url
36
+
37
+ result = soap_client(options).request :wsdl, method_name do |soap|
38
+ soap.body = options[:body]
39
+
40
+ unless method_name.to_sym == :login
41
+ soap.namespaces["xmlns:ns1"] = Config.soap_enterprise_namespace
42
+ soap.header = { "ns1:SessionHeader" => { "ns1:sessionId" => Config.session_id }}
43
+ end
44
+ end
45
+
46
+ result.to_hash[:"#{method_name.to_s.underscore}_response"][:result].tap do |result|
47
+ unless result[:success] || method_name.to_sym == :login
48
+ raise_error(method_name, options, result[:errors])
49
+ end
50
+ end
51
+ end
52
+
53
+ def soap_client(options)
54
+ Savon::Client.new do
55
+ wsdl.namespace = options[:namespace]
56
+ wsdl.endpoint = options[:endpoint_url]
57
+ end
58
+ end
59
+
60
+ def raise_error(method_name, options, errors)
61
+ message = <<-MSG
62
+ \n
63
+ METHOD_NAME: #{method_name}
64
+ OPTIONS: #{options.inspect}
65
+ ERRORS: #{errors.inspect}
66
+ MSG
67
+
68
+ raise Error.new(message)
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,6 @@
1
+ if Object.const_defined?(:Rails)
2
+ module Activeforce
3
+ class Engine < Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,33 @@
1
+ module Salesforce
2
+ class InvalidRequest < StandardError
3
+ attr_accessor :error_code
4
+
5
+ def initialize(error_options, url)
6
+ @error_code = error_options['errorCode']
7
+ super("#{error_options["errorCode"]}: #{error_options["message"]} while accessing #{url}")
8
+ end
9
+ end
10
+
11
+ class ObjectNotSupported < StandardError
12
+ def initialize(object_name)
13
+ super("The #{object_name} is not supported in SalesForce")
14
+ end
15
+ end
16
+
17
+ class RecordInvalid < StandardError
18
+ def initialize(object_name, reason)
19
+ super("#{object_name} failed to be saved. #{reason}")
20
+ end
21
+ end
22
+
23
+ class ColumnNotFound < StandardError
24
+ def initialize(name, table_name)
25
+ super("Column ''#{name}' not found for #{table_name}.")
26
+ end
27
+ end
28
+
29
+
30
+ class InvalidCredentials < StandardError; end
31
+ class ObjectDestroyed < StandardError; end;
32
+ class UnrecognizedColumn < StandardError; end;
33
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ class Salesforce::AuthenticationTest < ActiveSupport::TestCase
4
+ def test_session_id__exists
5
+ Salesforce.configure do
6
+ username "username"
7
+ password "password"
8
+ end
9
+ Salesforce::Authentication.expects(:generate_new_session_id).never
10
+ Salesforce::Config.instance.session_id "existingsessionid"
11
+ assert_equal "existingsessionid", Salesforce::Authentication.session_id
12
+ end
13
+
14
+ def test_session_id__doesnotexist
15
+ Salesforce.configure do
16
+ username "username"
17
+ password "password"
18
+ end
19
+ Salesforce::Authentication.expects(:generate_new_session_id).returns("new_session_id")
20
+ assert_equal "new_session_id", Salesforce::Authentication.session_id
21
+ end
22
+
23
+ def test_session_id__credentials_missing
24
+ Salesforce::Authentication.expects(:generate_new_session_id).never
25
+ assert_raises Salesforce::InvalidCredentials do
26
+ Salesforce::Authentication.session_id
27
+ end
28
+ end
29
+
30
+ def test_logout
31
+ Salesforce::Config.instance.session_id = "session_id"
32
+ Salesforce::Authentication.logout
33
+ assert_equal nil, Salesforce::Config.session_id
34
+ end
35
+
36
+ def test_generate_new_session_id__calls_connection_login
37
+ result = {
38
+ :session_id => "session_id",
39
+ :server_url => "https://cs99-api.salesforce.com/services/Soap/c/22.0/00DQ00000001LRX",
40
+ :user_id => "user_id"
41
+ }
42
+
43
+ Salesforce.connection.expects(:login).returns(result)
44
+
45
+ assert_equal "session_id", Salesforce::Authentication.generate_new_session_id
46
+ assert_equal "https://cs99-api.salesforce.com/services/Soap/c/22.0/00DQ00000001LRX", Salesforce::Config.soap_endpoint_url
47
+ assert_equal "session_id", Salesforce::Config.session_id
48
+ assert_equal "cs99", Salesforce::Config.server_instance
49
+ assert_equal "user_id", Salesforce::Config.user_id
50
+ end
51
+
52
+ end
@@ -0,0 +1,440 @@
1
+ require 'test_helper'
2
+
3
+ class Salesforce::OriginalTable < Salesforce::Base
4
+ end
5
+
6
+ class Salesforce::CustomTable < Salesforce::Base
7
+ self.custom_object = true
8
+ end
9
+
10
+ class Salesforce::BaseTest < ActiveSupport::TestCase
11
+
12
+ setup :clear_columns
13
+
14
+ def test_table_name
15
+ assert_equal "OriginalTable", Salesforce::OriginalTable.table_name
16
+ assert_equal "CustomTable__c", Salesforce::CustomTable.table_name
17
+ Salesforce::CustomTable.custom_table_name = "this_is_your_name"
18
+ assert_equal "this_is_your_name", Salesforce::CustomTable.table_name
19
+ ensure
20
+ Salesforce::CustomTable.custom_table_name = nil
21
+ end
22
+
23
+ def test_columns
24
+ setup_columns_for_custom_table
25
+ obj = Salesforce::CustomTable.new(:name => "tushar", :account => "account")
26
+ assert_equal "tushar", obj.name
27
+ assert_equal "account", obj.account
28
+
29
+ obj = Salesforce::CustomTable.new
30
+ obj.name = "tushar"
31
+ assert_equal "tushar", obj.name
32
+
33
+ obj = Salesforce::CustomTable.new
34
+ obj.Name__c = "tushar"
35
+ obj.Car__c = "Ferrari"
36
+ obj.Id = "newid"
37
+ assert_equal "tushar", obj.name
38
+ assert_equal "Ferrari", obj.car
39
+ assert_equal "newid", obj.id
40
+ end
41
+
42
+ def test_column_attr_createable_columns__new_record
43
+ setup_columns_for_custom_table
44
+ obj = Salesforce::CustomTable.new
45
+ assert_nothing_raised do
46
+ obj.account = "account"
47
+ obj.name = "foo"
48
+ obj.attributes = { :account => 'account2', :name => 'foo'}
49
+ assert_equal 'account2', obj.account
50
+ assert_equal 'foo', obj.name
51
+ end
52
+
53
+ assert_raises ArgumentError do
54
+ obj.car = "car"
55
+ end
56
+
57
+ assert_raises ArgumentError do
58
+ obj.attributes = { :car => 'car2'}
59
+ end
60
+
61
+ assert_raises NoMethodError do
62
+ obj.id = "id"
63
+ end
64
+
65
+ assert_raises NoMethodError do
66
+ obj.attributes = { :id => 'id2'}
67
+ end
68
+ end
69
+
70
+ def test_attributes__creatable_and_updateable
71
+ setup_columns_for_custom_table
72
+ obj = Salesforce::CustomTable.new("Id"=> "id", "Account__c" => "account", "Car__c" => 'car', "Name__c" => 'name')
73
+ assert_equal({"name"=>"name", "account"=>"account", "id"=>"id", "car"=>"car"}, obj.attributes)
74
+ assert_equal({"Account__c"=>"account", "Name__c"=>"name"}, obj.createable_attributes)
75
+ assert_equal({"Car__c"=>"car", "Name__c"=>"name"}, obj.updateable_attributes)
76
+ end
77
+
78
+ def test_column_attr_updateable_columns__existing_record
79
+ setup_columns_for_custom_table
80
+ obj = Salesforce::CustomTable.new("Id" => "id")
81
+
82
+ assert_nothing_raised do
83
+ obj.name = "foo"
84
+ obj.car = "car"
85
+ obj.attributes = { :name => 'foo2', :car => 'car2' }
86
+ assert_equal 'foo2', obj.name
87
+ assert_equal 'car2', obj.car
88
+ end
89
+
90
+ assert_raises ArgumentError do
91
+ obj.account = "account"
92
+ end
93
+
94
+ assert_raises ArgumentError do
95
+ obj.attributes = { :account => 'account2' }
96
+ end
97
+
98
+
99
+
100
+ assert_raises NoMethodError do
101
+ obj.id = "id"
102
+ end
103
+
104
+ assert_raises NoMethodError do
105
+ obj.attributes = { :id => 'id' }
106
+ end
107
+ end
108
+
109
+ def test_save__create
110
+ setup_columns_for_custom_table
111
+ Salesforce.connection.expects(:create).with('CustomTable__c', { 'Account__c' => 'account', "Name__c" => "name"}).returns("newid")
112
+ obj = Salesforce::CustomTable.new(:account => 'account', :name => 'name')
113
+ obj.save!
114
+ assert_equal 'newid', obj.id
115
+ assert_equal false, obj.new_record?
116
+ end
117
+
118
+ def test_save__create_fails
119
+ setup_columns_for_custom_table
120
+ error = Salesforce::RecordInvalid.new('CustomTable__c', 'msg')
121
+ Salesforce.connection.expects(:create).raises(error)
122
+ obj = Salesforce::CustomTable.new(:account => 'account', :name => 'name')
123
+ assert_raises Salesforce::RecordInvalid do
124
+ obj.save!
125
+ end
126
+ assert obj.new_record?
127
+ end
128
+
129
+ def test_save__update
130
+ setup_columns_for_custom_table
131
+ Salesforce.connection.expects(:update).with('CustomTable__c', 'oldid', {'Name__c' => 'newname', 'Car__c' => 'mycar'})
132
+ obj = Salesforce::CustomTable.new(:Account__c => 'account', :name => 'newname', :Id => "oldid", :Car__c => 'mycar')
133
+ assert_equal false, obj.new_record?
134
+ obj.save!
135
+ end
136
+
137
+ def test_update__wrapped_by_with_invalid_column_handling
138
+ Salesforce::OriginalTable.stubs(:columns)
139
+ Salesforce::OriginalTable.expects(:with_invalid_column_handling).returns(:update_result)
140
+ assert_equal :update_result, Salesforce::OriginalTable.new.update
141
+ end
142
+
143
+ def test_save__update_fails
144
+ setup_columns_for_custom_table
145
+ error = Salesforce::RecordInvalid.new('CustomTable__c', 'msg')
146
+ Salesforce.connection.expects(:update).raises(error)
147
+ obj = Salesforce::CustomTable.new(:Account__c => 'account', :name => 'newname', :Id => "oldid", :Car__c => 'mycar')
148
+ assert_raises Salesforce::RecordInvalid do
149
+ obj.save!
150
+ end
151
+ end
152
+
153
+ def test_destroy
154
+ setup_columns_for_custom_table
155
+ Salesforce.connection.expects(:destroy).with('CustomTable__c', 'oldid')
156
+ obj = Salesforce::CustomTable.new(:Account__c => 'account', :name => 'newname', :Id => "oldid")
157
+ obj.destroy
158
+ assert obj.destroyed?
159
+ assert_raises Salesforce::ObjectDestroyed do
160
+ obj.save!
161
+ end
162
+
163
+ assert_raises Salesforce::ObjectDestroyed do
164
+ obj.destroy
165
+ end
166
+
167
+ assert_raises Salesforce::ObjectDestroyed do
168
+ obj.create
169
+ end
170
+
171
+ assert_raises Salesforce::ObjectDestroyed do
172
+ obj.update
173
+ end
174
+ end
175
+
176
+ def test_destroy__new_record
177
+ setup_columns_for_custom_table
178
+ Salesforce.connection.expects(:destroy).never
179
+ obj = Salesforce::CustomTable.new(:Account__c => 'account', :name => 'newname')
180
+ assert obj.new_record?
181
+ obj.destroy
182
+ assert obj.destroyed?
183
+ assert_raises Salesforce::ObjectDestroyed do
184
+ obj.save!
185
+ end
186
+
187
+ assert_raises Salesforce::ObjectDestroyed do
188
+ obj.destroy
189
+ end
190
+
191
+ assert_raises Salesforce::ObjectDestroyed do
192
+ obj.create
193
+ end
194
+
195
+ assert_raises Salesforce::ObjectDestroyed do
196
+ obj.update
197
+ end
198
+ end
199
+
200
+
201
+ def test_find
202
+ Salesforce::OriginalTable.expects(:find_by_id).with("id")
203
+ Salesforce::OriginalTable.find("id")
204
+
205
+ Salesforce::OriginalTable.expects(:find_all)
206
+ Salesforce::OriginalTable.find(:all)
207
+ end
208
+
209
+ def test_find_by_id
210
+ setup_columns_for_original_table
211
+ Salesforce.connection.expects(:find_object_by_id).with("OriginalTable", "id", "Col1,Col2__c").returns({ "Col1" => 'col11', "Col2__c" => 'col21'})
212
+ result = Salesforce::OriginalTable.find_by_id("id")
213
+ assert_equal "col11", result.col1
214
+ assert_equal "col21", result.col2
215
+ end
216
+
217
+ def test_find_by_id__wrapped_by_with_invalid_column_handling
218
+ Salesforce::OriginalTable.expects(:with_invalid_column_handling).returns(:find_by_id_result)
219
+
220
+ assert_equal :find_by_id_result, Salesforce::OriginalTable.find_by_id(anything)
221
+ end
222
+
223
+ def test_find_by_id__columns_not_set
224
+ columns_hash = [{"name" => "Col1", "type" => "id"}, { "name" => "Col2__c", "type" => "string" } ]
225
+ columns = columns_hash.map { |hash| Salesforce::Column.new(hash) }
226
+ Salesforce.connection.expects(:fields).with("CustomTable__c").returns(columns_hash)
227
+ Salesforce.connection.expects(:find_object_by_id).with("CustomTable__c", "id", "Col1,Col2__c").returns("Col1" => 'col11', "Col2__c" => 'col21')
228
+ result = Salesforce::CustomTable.find_by_id("id")
229
+ assert_equal "col11", result.col1
230
+ assert_equal "col21", result.col2
231
+ end
232
+
233
+ def test_find_all
234
+ setup_columns_for_original_table
235
+ Salesforce.connection.expects(:soql).with("SELECT Col1,Col2__c FROM OriginalTable").returns([ { "Col1" => 'col11', "Col2__c" => 'col21'}, { "Col1" => 'col21', "Col2__c" => 'col22'}])
236
+ results = Salesforce::OriginalTable.find(:all)
237
+ assert_equal 2, results.size
238
+ assert_equal "col11", results.first.col1
239
+ assert_equal "col21", results.first.col2
240
+ assert_equal "col21", results.last.col1
241
+ assert_equal "col22", results.last.col2
242
+ end
243
+ def test_find_all__wrapped_by_with_invalid_column_handling
244
+ Salesforce::OriginalTable.expects(:with_invalid_column_handling).returns(:find_all_result)
245
+
246
+ assert_equal :find_all_result, Salesforce::OriginalTable.find_all
247
+ end
248
+
249
+ def test_find_all__with_select
250
+ setup_columns_for_original_table
251
+ Salesforce.connection.expects(:soql).with("SELECT Col2__c FROM OriginalTable").returns([ { "Col2__c" => 'col21'}, { "Col2__c" => 'col22'}])
252
+ results = Salesforce::OriginalTable.find(:all, :select => :col2)
253
+ assert_equal 2, results.size
254
+ assert_equal "col21", results.first.col2
255
+ assert_equal "col22", results.last.col2
256
+ end
257
+
258
+ def test_find_all__with_conditions
259
+ setup_columns_for_original_table
260
+ Salesforce.connection.expects(:soql).with("SELECT Col1,Col2__c FROM OriginalTable WHERE Col2__c >= 2011-11-11").returns([ { "Col2__c" => 'col21'}, { "Col2__c" => 'col22'}])
261
+ results = Salesforce::OriginalTable.find(:all, :conditions => ":col2 >= :date", :date => Date.parse("11/11/2011"))
262
+ assert_equal 2, results.size
263
+ assert_equal "col21", results.first.col2
264
+ assert_equal "col22", results.last.col2
265
+ end
266
+
267
+ def test_query_string
268
+ Salesforce.connection.expects(:fields).with("OriginalTable").returns([
269
+ { "name" => "Col1", "type" => "id" },
270
+ {"name" => "Col2__c", "type" => "string"},
271
+ {"name" => "Col3__c", "type" => "datetime"},
272
+ {"name" => "ZCol4__c", "type" => "date"},
273
+ {"name" => "ACol5__c", "type" => "boolean"}
274
+ ])
275
+
276
+ Salesforce::OriginalTable.columns
277
+
278
+ assert_equal "SELECT ACol5__c,Col1,Col2__c,Col3__c,ZCol4__c FROM OriginalTable", Salesforce::OriginalTable.query_string({})
279
+ assert_equal "SELECT ACol5__c FROM OriginalTable", Salesforce::OriginalTable.query_string({:select => :a_col5})
280
+ assert_equal "SELECT ZCol4__c,Col2__c FROM OriginalTable", Salesforce::OriginalTable.query_string({:select => [ :z_col4, :col2 ]})
281
+
282
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 > Col2__c", Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 > :col2")
283
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 > Col2__c", Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 > :col2")
284
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 > 2011-08-01", Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 > :date",
285
+ :date => Date.parse('08/01/2011') )
286
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 > 2011-08-01 AND ACol5__c = 2011-08-01T09:30:00-07:00",
287
+ Salesforce::OriginalTable.query_string(:select => :a_col5,
288
+ :conditions => ":col1 > :date AND :a_col5 = :time", :date => Date.parse('08/01/2011'), :time => Time.zone.parse("08/01/2011 09:30 AM"))
289
+
290
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 = TRUE AND Col2__c = FALSE AND Col3__c = NULL",
291
+ Salesforce::OriginalTable.query_string(:select => :a_col5,
292
+ :conditions => ":col1 = :true AND :col2 = :false AND :col3 = :nil", :true => true, :false => false, :nil => nil)
293
+
294
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 = 'string'",
295
+ Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 = :string", :string => 'string' )
296
+
297
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 = 'string' ORDER BY Col1",
298
+ Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 = :string", :string => 'string', :order => :col1)
299
+
300
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 = 'string' ORDER BY Col1 ASC, Col3__c DESC",
301
+ Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 = :string",
302
+ :string => 'string' , :order => ":col1 ASC, :col3 DESC")
303
+
304
+ assert_equal "SELECT ACol5__c FROM OriginalTable WHERE Col1 = 'string' GROUP BY Col2__c HAVING Col3__c > 2009-08-01 ORDER BY Col1 ASC, Col3__c DESC",
305
+ Salesforce::OriginalTable.query_string(:select => :a_col5, :conditions => ":col1 = :string", :group_by => :col2, :having => ":col3 > :date",
306
+ :string => 'string', :date => Date.parse('08/01/2009'), :order => ":col1 ASC, :col3 DESC")
307
+
308
+
309
+
310
+ assert_raises Salesforce::ColumnNotFound do
311
+ Salesforce::OriginalTable.query_string({:select => [ :col4 ]})
312
+ end
313
+ end
314
+
315
+ def test_find_all__columns_not_set
316
+ columns_hash = [{"name" => "Col1", "type" => "id"}, { "name" => "Col2__c", "type" => "string" } ]
317
+ columns = columns_hash.map { |hash| Salesforce::Column.new(hash) }
318
+ Salesforce.connection.expects(:fields).with("CustomTable__c").returns(columns_hash)
319
+ Salesforce.connection.expects(:soql).with("SELECT Col1,Col2__c FROM CustomTable__c").returns([ { "Col1" => 'col11', "Col2__c" => 'col21'}, { "Col1" => 'col21', "Col2__c" => 'col22'} ])
320
+ results = Salesforce::CustomTable.find(:all)
321
+ assert_equal 2, results.size
322
+ assert_equal "col11", results.first.col1
323
+ assert_equal "col21", results.first.col2
324
+ assert_equal "col21", results.last.col1
325
+ assert_equal "col22", results.last.col2
326
+ end
327
+
328
+ def test_dynamic_finders
329
+ setup_columns_for_original_table
330
+
331
+ Salesforce::OriginalTable.expects(:find_by_column).with do |col, value|
332
+ col.original_name == "Col1" && value == "col1value"
333
+ end.returns(:find_by_col1_result)
334
+
335
+ assert_equal :find_by_col1_result, Salesforce::OriginalTable.find_by_col1("col1value")
336
+
337
+ Salesforce::OriginalTable.expects(:find_by_column).with do |col, value|
338
+ col.original_name == "Col2__c" && value == "col2value"
339
+ end.returns(:find_by_col2_result)
340
+
341
+ assert_equal :find_by_col2_result, Salesforce::OriginalTable.find_by_col2("col2value")
342
+
343
+ Salesforce::OriginalTable.expects(:find_by_column).never
344
+
345
+ assert_raises NoMethodError do
346
+ Salesforce::OriginalTable.find_by_col3("col1value")
347
+ end
348
+ end
349
+
350
+ def test_find_by_column
351
+ setup_columns_for_original_table
352
+ Salesforce.connection.expects(:soql).with("SELECT Col1,Col2__c FROM OriginalTable WHERE Col2__c='value'").returns([ { "Col1" => 'col11', "Col2__c" => 'col21'}, { "Col1" => 'col21', "Col2__c" => 'col22'} ])
353
+ results = Salesforce::OriginalTable.find_by_column(Salesforce::OriginalTable.columns.all.last, "value")
354
+ assert_equal 2, results.size
355
+ assert_equal "col11", results.first.col1
356
+ assert_equal "col21", results.first.col2
357
+ assert_equal "col21", results.last.col1
358
+ assert_equal "col22", results.last.col2
359
+ end
360
+
361
+ def test_select_values
362
+ setup_columns_for_original_table
363
+ Salesforce::OriginalTable.expects(:query_string).with(:select => [ :col1, :col2 ], :other => :options).returns("sql_query")
364
+ Salesforce.connection.expects(:soql).with("sql_query").returns([ { "Col1" => '123456789012345678', "Col2__c" => 'col21'}, { "Col1" => '123456789012345678', "Col2__c" => 'col22'} ])
365
+ results = Salesforce::OriginalTable.select_values(:select => [ :col1, :col2 ], :other => :options)
366
+ assert_equal 2, results.size
367
+ assert_equal({ :col1 => '123456789012345', :col2 => 'col21'}, results.first)
368
+ assert_equal({ :col1 => '123456789012345', :col2 => 'col22'}, results.last)
369
+ end
370
+
371
+ def test_with_invalid_column_handling__succeeds_the_first_time
372
+ value = Salesforce::OriginalTable.with_invalid_column_handling do
373
+ :foo
374
+ end
375
+
376
+ assert_equal :foo, value
377
+ end
378
+
379
+ def test_with_invalid_column_handling__throws_invalid_fields_error__more_than_once
380
+ assert_raises Salesforce::InvalidRequest do
381
+ Salesforce::OriginalTable.with_invalid_column_handling do
382
+ raise Salesforce::InvalidRequest.new({ 'errorCode' => 'INVALID_FIELD'}, '/foo')
383
+ end
384
+ end
385
+ end
386
+
387
+ def test_with_invalid_column_handling__throws_invalid_fields_error__more_than_once
388
+ error = RuntimeError.new
389
+ Salesforce::OriginalTable.expects(:clear_cached_columns!).never
390
+
391
+ assert_raises error.class do
392
+ Salesforce::OriginalTable.with_invalid_column_handling do
393
+ raise error
394
+ end
395
+ end
396
+ end
397
+
398
+ def test_with_invalid_column_handling__throws_invalid_fields_error__once__then_recovers
399
+ count = 0
400
+
401
+ Salesforce::OriginalTable.expects(:clear_cached_columns!)
402
+ value = Salesforce::OriginalTable.with_invalid_column_handling do
403
+ if count < 1
404
+ count += 1
405
+ raise Salesforce::InvalidRequest.new({ 'errorCode' => 'INVALID_FIELD'}, '/foo')
406
+ end
407
+ :bar
408
+ end
409
+
410
+ assert_equal :bar, value
411
+ end
412
+
413
+ private
414
+
415
+ def clear_columns
416
+ Salesforce::CustomTable.cached_columns = nil
417
+ Salesforce::OriginalTable.cached_columns = nil
418
+ end
419
+
420
+ def setup_columns_for_original_table
421
+ Salesforce.connection.expects(:fields).with("OriginalTable").returns([ { "name" => "Col1", "type" => "id" }, {"name" => "Col2__c", "type" => "id"} ])
422
+ Salesforce::OriginalTable.columns
423
+ end
424
+
425
+ def setup_columns_for_custom_table
426
+ columns_hash = [
427
+ { "name" => "Id", "type" => "id", "createable" => false, "updateable" => false},
428
+ { "name" => "Account__c", "type" => "reference", "createable" => true, "updateable" => false, "custom" => true },
429
+ { "name" => "Car__c", "type" => "reference", "createable" => false, "updateable" => true, "custom" => true},
430
+ { "name" => "Name__c", "type" => "string", "createable" => true, "updateable" => true, "custom" => true}
431
+ ]
432
+
433
+ Salesforce.connection.expects(:fields).with("CustomTable__c").returns(columns_hash).twice
434
+ columns = Salesforce::Columns.new("CustomTable__c")
435
+ assert_equal columns, Salesforce::CustomTable.columns
436
+ end
437
+
438
+
439
+ end
440
+