activeforce 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/Gemfile.lock +128 -0
- data/LICENSE.txt +20 -0
- data/README.md +112 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/activeforce.gemspec +151 -0
- data/app/models/salesforce/account.rb +4 -0
- data/app/models/salesforce/activity_history.rb +4 -0
- data/app/models/salesforce/approval.rb +4 -0
- data/app/models/salesforce/campaign.rb +4 -0
- data/app/models/salesforce/campaign_feed.rb +4 -0
- data/app/models/salesforce/campaign_member.rb +4 -0
- data/app/models/salesforce/case.rb +4 -0
- data/app/models/salesforce/case_comment.rb +4 -0
- data/app/models/salesforce/case_contact_role.rb +4 -0
- data/app/models/salesforce/case_feed.rb +4 -0
- data/app/models/salesforce/case_history.rb +4 -0
- data/app/models/salesforce/case_share.rb +4 -0
- data/app/models/salesforce/case_solution.rb +4 -0
- data/app/models/salesforce/case_status.rb +4 -0
- data/app/models/salesforce/case_team_member.rb +4 -0
- data/app/models/salesforce/community.rb +4 -0
- data/app/models/salesforce/contact.rb +4 -0
- data/app/models/salesforce/contact_feed.rb +4 -0
- data/app/models/salesforce/contact_history.rb +4 -0
- data/app/models/salesforce/contract.rb +4 -0
- data/app/models/salesforce/document.rb +4 -0
- data/app/models/salesforce/event.rb +4 -0
- data/app/models/salesforce/feed_item.rb +4 -0
- data/app/models/salesforce/group.rb +4 -0
- data/app/models/salesforce/group_member.rb +4 -0
- data/app/models/salesforce/idea.rb +4 -0
- data/app/models/salesforce/lead.rb +4 -0
- data/app/models/salesforce/lead_status.rb +4 -0
- data/app/models/salesforce/name.rb +4 -0
- data/app/models/salesforce/note.rb +4 -0
- data/app/models/salesforce/open_activity.rb +4 -0
- data/app/models/salesforce/opportunity.rb +4 -0
- data/app/models/salesforce/organization.rb +4 -0
- data/app/models/salesforce/partner.rb +4 -0
- data/app/models/salesforce/period.rb +4 -0
- data/app/models/salesforce/product2.rb +4 -0
- data/app/models/salesforce/product2_feed.rb +4 -0
- data/app/models/salesforce/profile.rb +4 -0
- data/app/models/salesforce/quote.rb +4 -0
- data/app/models/salesforce/solution.rb +4 -0
- data/app/models/salesforce/task.rb +4 -0
- data/app/models/salesforce/task_feed.rb +4 -0
- data/app/models/salesforce/task_priority.rb +4 -0
- data/app/models/salesforce/task_status.rb +4 -0
- data/app/models/salesforce/user.rb +4 -0
- data/app/models/salesforce/user_role.rb +4 -0
- data/app/models/salesforce/vote.rb +4 -0
- data/lib/activeforce.rb +29 -0
- data/lib/salesforce/attributes.rb +13 -0
- data/lib/salesforce/authentication.rb +25 -0
- data/lib/salesforce/base.rb +240 -0
- data/lib/salesforce/bulk/batch.rb +77 -0
- data/lib/salesforce/bulk/job.rb +103 -0
- data/lib/salesforce/bulk/operations.rb +25 -0
- data/lib/salesforce/bulk/update_job.rb +24 -0
- data/lib/salesforce/column.rb +98 -0
- data/lib/salesforce/columns.rb +60 -0
- data/lib/salesforce/config.rb +110 -0
- data/lib/salesforce/connection.rb +33 -0
- data/lib/salesforce/connection/async.rb +36 -0
- data/lib/salesforce/connection/conversion.rb +37 -0
- data/lib/salesforce/connection/http_methods.rb +72 -0
- data/lib/salesforce/connection/rest_api.rb +52 -0
- data/lib/salesforce/connection/soap_api.rb +74 -0
- data/lib/salesforce/engine.rb +6 -0
- data/lib/salesforce/errors.rb +33 -0
- data/test/salesforce/authentication_test.rb +52 -0
- data/test/salesforce/base_test.rb +440 -0
- data/test/salesforce/bulk/batch_test.rb +79 -0
- data/test/salesforce/bulk/update_job_test.rb +125 -0
- data/test/salesforce/column_test.rb +115 -0
- data/test/salesforce/config_test.rb +163 -0
- data/test/salesforce/connection/async_test.rb +138 -0
- data/test/salesforce/connection/http_methods_test.rb +242 -0
- data/test/salesforce/connection/rest_api_test.rb +61 -0
- data/test/salesforce/connection/soap_api_test.rb +148 -0
- data/test/salesforce/connection_test.rb +148 -0
- data/test/test_helper.rb +79 -0
- 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,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
|
+
|