activeforce 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
+