metaforce 0.2.0.alpha

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 (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