metaforce 0.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +7 -0
  2. data/.rvmrc +55 -0
  3. data/Gemfile +10 -0
  4. data/Guardfile +9 -0
  5. data/README.md +59 -0
  6. data/Rakefile +6 -0
  7. data/lib/metaforce.rb +4 -0
  8. data/lib/metaforce/api.rb +3 -0
  9. data/lib/metaforce/api/metadata.rb +117 -0
  10. data/lib/metaforce/api/services.rb +33 -0
  11. data/lib/metaforce/api/transaction.rb +53 -0
  12. data/lib/metaforce/config.rb +24 -0
  13. data/lib/metaforce/manifest.rb +313 -0
  14. data/lib/metaforce/version.rb +3 -0
  15. data/metaforce.gemspec +28 -0
  16. data/spec/.gitignore +1 -0
  17. data/spec/fixtures/package.xml +17 -0
  18. data/spec/fixtures/requests/check_deploy_status/done.xml +33 -0
  19. data/spec/fixtures/requests/check_deploy_status/error.xml +26 -0
  20. data/spec/fixtures/requests/check_status/done.xml +19 -0
  21. data/spec/fixtures/requests/check_status/not_done.xml +19 -0
  22. data/spec/fixtures/requests/deploy/in_progress.xml +13 -0
  23. data/spec/fixtures/requests/describe_metadata/success.xml +230 -0
  24. data/spec/fixtures/requests/list_metadata/objects.xml +33 -0
  25. data/spec/fixtures/requests/login/failure.xml +15 -0
  26. data/spec/fixtures/requests/login/success.xml +39 -0
  27. data/spec/fixtures/sample/src/classes/TestClass.cls +2 -0
  28. data/spec/fixtures/sample/src/classes/TestClass.cls-meta.xml +5 -0
  29. data/spec/fixtures/sample/src/package.xml +8 -0
  30. data/spec/lib/api/metadata_spec.rb +139 -0
  31. data/spec/lib/api/services_spec.rb +24 -0
  32. data/spec/lib/api/transaction_spec.rb +62 -0
  33. data/spec/lib/config_spec.rb +53 -0
  34. data/spec/lib/manifest_spec.rb +181 -0
  35. data/spec/spec_helper.rb +11 -0
  36. data/wsdl/23.0/metadata.xml +3520 -0
  37. data/wsdl/23.0/partner.xml +3190 -0
  38. metadata +167 -0
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
+ <soapenv:Body>
4
+ <loginResponse>
5
+ <result>
6
+ <metadataServerUrl>https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh</metadataServerUrl>
7
+ <passwordExpired>false</passwordExpired>
8
+ <sandbox>false</sandbox>
9
+ <serverUrl>https://na12-api.salesforce.com/services/Soap/u/23.0/00DU0000000Ilbh</serverUrl>
10
+ <sessionId>00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLHIRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8</sessionId>
11
+ <userId>005U0000000FGpcIAG</userId>
12
+ <userInfo>
13
+ <accessibilityMode>false</accessibilityMode>
14
+ <currencySymbol>$</currencySymbol>
15
+ <orgAttachmentFileSizeLimit>5242880</orgAttachmentFileSizeLimit>
16
+ <orgDefaultCurrencyIsoCode>USD</orgDefaultCurrencyIsoCode>
17
+ <orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
18
+ <orgHasPersonAccounts>false</orgHasPersonAccounts>
19
+ <organizationId>00DU0000000AlbhMAC</organizationId>
20
+ <organizationMultiCurrency>false</organizationMultiCurrency>
21
+ <organizationName>Foo Org</organizationName>
22
+ <profileId>00eU0000000LiuNIAQ</profileId>
23
+ <roleId>00EU0000000x4OtMAK</roleId>
24
+ <sessionSecondsValid>7200</sessionSecondsValid>
25
+ <userDefaultCurrencyIsoCode xsi:nil="true"/>
26
+ <userEmail>foo@bar.com</userEmail>
27
+ <userFullName>Foo Bar</userFullName>
28
+ <userId>005U0000000EApcIAG</userId>
29
+ <userLanguage>en_US</userLanguage>
30
+ <userLocale>en_US</userLocale>
31
+ <userName>foo@bar.com</userName>
32
+ <userTimeZone>America/Los_Angeles</userTimeZone>
33
+ <userType>Standard</userType>
34
+ <userUiSkin>Theme3</userUiSkin>
35
+ </userInfo>
36
+ </result>
37
+ </loginResponse>
38
+ </soapenv:Body>
39
+ </soapenv:Envelope>
@@ -0,0 +1,2 @@
1
+ public class TestClass {
2
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>23.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0"?>
2
+ <Package xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <types>
4
+ <members>TestClass</members>
5
+ <name>ApexClass</name>
6
+ </types>
7
+ <version>23.0</version>
8
+ </Package>
@@ -0,0 +1,139 @@
1
+ require "spec_helper"
2
+ require "tempfile"
3
+
4
+ describe Metaforce::Metadata::Client do
5
+
6
+ before(:each) do
7
+ savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
8
+ end
9
+
10
+ let(:client) do
11
+ Metaforce::Metadata::Client.new(:username => 'valid', :password => 'password')
12
+ end
13
+
14
+ describe ".list" do
15
+ context "when given valid information" do
16
+
17
+ it "returns an array" do
18
+ savon.expects(:list_metadata).with(:queries => [ :type => "ApexClass"]).returns(:objects)
19
+ client.list(:type => "ApexClass").should be_an(Array)
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ describe ".describe" do
26
+ context "when given valid information" do
27
+
28
+ it "returns a hash" do
29
+ savon.expects(:describe_metadata).returns(:success)
30
+ client.describe.should be_a(Hash)
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ describe ".status" do
37
+ context "when given an invalid id" do
38
+
39
+ it "raises an" do
40
+ expect { client.status("badId") }.to raise_error
41
+ end
42
+
43
+ end
44
+ context "when given an id of a result that has completed" do
45
+
46
+ it "returns a hash and the :done key contains true" do
47
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
48
+ status = client.status("04sU0000000WNWoIAO")
49
+ status.should be_a(Hash)
50
+ status[:done].should eq(true)
51
+ end
52
+
53
+ end
54
+ context "when given an id of a result that has not completed" do
55
+
56
+ it "returns a hash and the :done key contains false" do
57
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAo" ]).returns(:not_done)
58
+ status = client.status("04sU0000000WNWoIAo")
59
+ status.should be_a(Hash)
60
+ status[:done].should eq(false)
61
+ end
62
+
63
+ end
64
+ context "when given and id of a deploy that has completed" do
65
+
66
+ it "returns a hash" do
67
+ savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
68
+ status = client.status("04sU0000000WNWoIAO", :deploy)
69
+ status.should be_a(Hash)
70
+ end
71
+
72
+ end
73
+ end
74
+
75
+ describe ".done?" do
76
+ context "when given an id of a result that has completed" do
77
+
78
+ it "returns true" do
79
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
80
+ client.done?("04sU0000000WNWoIAO").should eq(true)
81
+ end
82
+
83
+ end
84
+ context "when given an id of a result that has not completed" do
85
+
86
+ it "returns false" do
87
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAo" ]).returns(:not_done)
88
+ client.done?("04sU0000000WNWoIAo").should eq(false)
89
+ end
90
+
91
+ end
92
+ end
93
+
94
+ describe ".deploy" do
95
+
96
+ before(:each) do
97
+ Metaforce::Metadata::Client.any_instance.stubs(:create_deploy_file).returns('')
98
+ end
99
+
100
+ context "when given a directory to deploy" do
101
+
102
+ it "deploys the directory and returns a transaction" do
103
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
104
+ deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
105
+ deployment.should be_a(Metaforce::Transaction)
106
+ deployment.id.should eq("04sU0000000WNWoIAO")
107
+ end
108
+
109
+ end
110
+
111
+ # context "when given a file" do
112
+
113
+ # it "deploys the file and returns the id of the result" do
114
+ # path = Tempfile.new('zipfile').tap { |f| f.write('h'); f.close }.path
115
+ # savon.expects(:deploy).with(:zip_file => "aA==\n", :deploy_options => {}).returns(:in_progress)
116
+ # id = client.deploy(File.open(path))
117
+ # id.should eq("04sU0000000WNWoIAO")
118
+ # end
119
+
120
+ # end
121
+
122
+ it "allows deploy options to be configured via a hash" do
123
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => { :run_all_tests => true }).returns(:in_progress)
124
+ expect {
125
+ client.deploy('', { :run_all_tests => true })
126
+ }.to_not raise_error
127
+ end
128
+
129
+ it "allows deploy options to be configured via a block" do
130
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => { :run_all_tests => true }).returns(:in_progress)
131
+ expect {
132
+ client.deploy('') do |options|
133
+ options.run_all_tests = true
134
+ end
135
+ }.to_not raise_error
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe Metaforce::Services::Client do
4
+ describe ".login" do
5
+ context "when given invalid credentials" do
6
+
7
+ it "raises an error" do
8
+ savon.expects(:login).with(:username => 'invalid', :password => 'password').returns(:failure)
9
+ expect { Metaforce::Services::Client.new(:username => 'invalid', :password => 'password') }.to raise_error
10
+ end
11
+
12
+ end
13
+ context "when given valid credentials" do
14
+
15
+ it "returns a hash containing the session id and metadata server url" do
16
+ savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
17
+ session = Metaforce::Services::Client.new(:username => 'valid', :password => 'password').session
18
+ session.should eq({ :session_id => "00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLHIRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8",
19
+ :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh" })
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,62 @@
1
+ require "spec_helper"
2
+
3
+ describe Metaforce::Transaction do
4
+
5
+ before(:each) do
6
+ savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
7
+ Metaforce::Metadata::Client.any_instance.stubs(:create_deploy_file).returns('')
8
+ end
9
+
10
+ let(:client) do
11
+ Metaforce::Metadata::Client.new(:username => 'valid', :password => 'password')
12
+ end
13
+
14
+ describe ".done?" do
15
+
16
+ it "allows you to check if the transaction has completed" do
17
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
18
+ deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
19
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
20
+ deployment.done?.should eq(true)
21
+ end
22
+
23
+ it "doesn't send a request if it has already completed" do
24
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
25
+ deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
26
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
27
+ deployment.done?.should eq(true)
28
+ expect { deployment.done?.should eq(true) }.to_not raise_error
29
+ end
30
+
31
+ end
32
+
33
+ describe ".result" do
34
+
35
+ it "raises an error if .done? hasn't been called" do
36
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
37
+ deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
38
+ expect { deployment.result }.to raise_error
39
+ end
40
+
41
+ it "allows you to check the transaction result" do
42
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
43
+ deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
44
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
45
+ deployment.done?.should eq(true)
46
+ savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
47
+ deployment.result.should be_a(Hash)
48
+ end
49
+
50
+ it "doesn't send a request if it has already retrieved the result" do
51
+ savon.expects(:deploy).with(:zip_file => '', :deploy_options => {}).returns(:in_progress)
52
+ deployment = client.deploy(File.expand_path('../../../fixtures/sample', __FILE__))
53
+ savon.expects(:check_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
54
+ deployment.done?.should eq(true)
55
+ savon.expects(:check_deploy_status).with(:ids => [ "04sU0000000WNWoIAO" ]).returns(:done)
56
+ deployment.result.should be_a(Hash)
57
+ expect { deployment.result }.to_not raise_error
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+
3
+ describe Metaforce do
4
+ describe ".configuration" do
5
+
6
+ it "sets the default api version to 23.0" do
7
+ Metaforce.configuration.api_version.should eq("23.0")
8
+ end
9
+
10
+ it "allows you to configure the api version" do
11
+ Metaforce.configuration.api_version = "21.0"
12
+ Metaforce.configuration.api_version.should eq("21.0")
13
+ end
14
+
15
+ end
16
+
17
+ describe ".configure" do
18
+
19
+ it "allows you to configure the api version" do
20
+ Metaforce.configure do |config|
21
+ config.api_version = "21.0"
22
+ end
23
+ Metaforce.configuration.api_version.should eq("21.0")
24
+ end
25
+
26
+ context "credentials" do
27
+
28
+ it "allows you to set the credentials via the configure block" do
29
+ Metaforce.configure do |config|
30
+ config.api_version = "23.0"
31
+ config.username = "valid"
32
+ config.password = "password"
33
+ end
34
+ savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
35
+ session = Metaforce::Services::Client.new.session
36
+ session.should eq({ :session_id => "00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLHIRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8",
37
+ :metadata_server_url => "https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh" })
38
+ end
39
+
40
+ it "allows you to set the credentials via the configure block" do
41
+ Metaforce.configure do |config|
42
+ config.api_version = "23.0"
43
+ config.username = "valid"
44
+ config.password = "password"
45
+ end
46
+ savon.expects(:login).with(:username => 'valid', :password => 'password').returns(:success)
47
+ expect { Metaforce::Metadata::Client.new }.to_not raise_error
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,181 @@
1
+ require "spec_helper"
2
+
3
+ describe Metaforce::Manifest do
4
+
5
+ before(:all) do
6
+ @package_xml = File.open(File.join(File.dirname(__FILE__), '../fixtures/package.xml'), 'r').read
7
+ end
8
+
9
+ before(:each) do
10
+ @package_hash = {
11
+ :apex_class => ['TestClass', 'AnotherClass'],
12
+ :apex_component => ['Component'],
13
+ :static_resource => ['Assets']
14
+ }
15
+ end
16
+
17
+ describe ".new" do
18
+ context "when given a hash" do
19
+ describe ".to_xml" do
20
+
21
+ it "returns a string containing xml" do
22
+ package = Metaforce::Manifest.new(@package_hash)
23
+ response = package.to_xml
24
+ response.should eq(@package_xml)
25
+ end
26
+
27
+ end
28
+ end
29
+ context "when given a string" do
30
+ describe ".to_hash" do
31
+
32
+ it "returns the xml content as a hash" do
33
+ package = Metaforce::Manifest.new(@package_xml)
34
+ response = package.to_hash
35
+ response.should eq(@package_hash)
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+
42
+ describe ".add" do
43
+
44
+ it "can add additional components" do
45
+ package = Metaforce::Manifest.new(@package_hash)
46
+ package.add(:apex_class, 'AdditionalClass')
47
+ response = package.to_hash
48
+ @package_hash = {
49
+ :apex_class => ['TestClass', 'AnotherClass', 'AdditionalClass'],
50
+ :apex_component => ['Component'],
51
+ :static_resource => ['Assets']
52
+ }
53
+ response.should eq(@package_hash)
54
+ end
55
+
56
+ it "can add additional components from an array" do
57
+ package = Metaforce::Manifest.new(@package_hash)
58
+ package.add(:apex_class, ['class1', 'class2'])
59
+ response = package.to_hash
60
+ @package_hash = {
61
+ :apex_class => ['TestClass', 'AnotherClass', 'class1', 'class2'],
62
+ :apex_component => ['Component'],
63
+ :static_resource => ['Assets']
64
+ }
65
+ response.should eq(@package_hash)
66
+ end
67
+
68
+ it "strips directories and extensions" do
69
+ package = Metaforce::Manifest.new(@package_hash)
70
+ package.add(:apex_class, 'src/classes/AdditionalClass.cls')
71
+ response = package.to_hash
72
+ @package_hash = {
73
+ :apex_class => ['TestClass', 'AnotherClass', 'AdditionalClass'],
74
+ :apex_component => ['Component'],
75
+ :static_resource => ['Assets']
76
+ }
77
+ response.should eq(@package_hash)
78
+ end
79
+
80
+ end
81
+
82
+ describe ".remove" do
83
+
84
+ it "can remove components" do
85
+ package = Metaforce::Manifest.new(@package_hash)
86
+ package.remove(:apex_class, 'TestClass')
87
+ response = package.to_hash
88
+ @package_hash = {
89
+ :apex_class => ['AnotherClass'],
90
+ :apex_component => ['Component'],
91
+ :static_resource => ['Assets']
92
+ }
93
+ response.should eq(@package_hash)
94
+ end
95
+
96
+ it "can remove components in an array" do
97
+ package = Metaforce::Manifest.new(@package_hash)
98
+ package.remove(:apex_class, ['TestClass', 'AnotherClass'])
99
+ response = package.to_hash
100
+ @package_hash = {
101
+ :apex_component => ['Component'],
102
+ :static_resource => ['Assets']
103
+ }
104
+ response.should eq(@package_hash)
105
+ end
106
+
107
+ it "strips directories and extensions" do
108
+ package = Metaforce::Manifest.new(@package_hash)
109
+ package.remove(:apex_class, 'src/classes/TestClass.cls')
110
+ response = package.to_hash
111
+ @package_hash = {
112
+ :apex_class => ['AnotherClass'],
113
+ :apex_component => ['Component'],
114
+ :static_resource => ['Assets']
115
+ }
116
+ response.should eq(@package_hash)
117
+ end
118
+
119
+ end
120
+
121
+ describe ".only" do
122
+
123
+ it "filters the components based on a list of files" do
124
+ package = Metaforce::Manifest.new(@package_hash)
125
+ package.only(['classes/TestClass'])
126
+ response = package.to_hash
127
+ @package_hash = {
128
+ :apex_class => ['TestClass']
129
+ }
130
+ response.should eq(@package_hash)
131
+ end
132
+
133
+ it "ignores file extensions" do
134
+ package = Metaforce::Manifest.new(@package_hash)
135
+ package.only(['classes/TestClass.cls', 'components/Component.component'])
136
+ response = package.to_hash
137
+ @package_hash = {
138
+ :apex_class => ['TestClass'],
139
+ :apex_component => ['Component']
140
+ }
141
+ response.should eq(@package_hash)
142
+ end
143
+
144
+ it "strips any leading directories" do
145
+ package = Metaforce::Manifest.new(@package_hash)
146
+ package.only(['src/classes/TestClass.cls', 'src/components/Component.component'])
147
+ response = package.to_hash
148
+ @package_hash = {
149
+ :apex_class => ['TestClass'],
150
+ :apex_component => ['Component']
151
+ }
152
+ response.should eq(@package_hash)
153
+ end
154
+
155
+ it "ignores case of folder" do
156
+ package = Metaforce::Manifest.new(@package_hash)
157
+ package.only(['src/Classes/TestClass.cls', 'src/Components/Component.component'])
158
+ response = package.to_hash
159
+ @package_hash = {
160
+ :apex_class => ['TestClass'],
161
+ :apex_component => ['Component']
162
+ }
163
+ response.should eq(@package_hash)
164
+ end
165
+
166
+ end
167
+
168
+ describe ".to_package" do
169
+
170
+ it "returns a in package format" do
171
+ package = Metaforce::Manifest.new(@package_hash)
172
+ response = package.to_package
173
+ response.should eq([
174
+ { :members => ["TestClass", "AnotherClass"], :name => "ApexClass" },
175
+ { :members => ["Component"], :name => "ApexComponent" },
176
+ { :members => ["Assets"], :name => "StaticResource" }
177
+ ])
178
+ end
179
+
180
+ end
181
+ end