metaforce-beta 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +22 -0
  7. data/README.md +193 -0
  8. data/Rakefile +10 -0
  9. data/bin/metaforce +14 -0
  10. data/examples/example.rb +52 -0
  11. data/lib/metaforce.rb +34 -0
  12. data/lib/metaforce/abstract_client.rb +86 -0
  13. data/lib/metaforce/cli.rb +130 -0
  14. data/lib/metaforce/client.rb +31 -0
  15. data/lib/metaforce/config.rb +100 -0
  16. data/lib/metaforce/job.rb +203 -0
  17. data/lib/metaforce/job/crud.rb +13 -0
  18. data/lib/metaforce/job/deploy.rb +87 -0
  19. data/lib/metaforce/job/retrieve.rb +102 -0
  20. data/lib/metaforce/login.rb +39 -0
  21. data/lib/metaforce/manifest.rb +106 -0
  22. data/lib/metaforce/metadata/client.rb +18 -0
  23. data/lib/metaforce/metadata/client/crud.rb +86 -0
  24. data/lib/metaforce/metadata/client/file.rb +113 -0
  25. data/lib/metaforce/reporters.rb +2 -0
  26. data/lib/metaforce/reporters/base_reporter.rb +56 -0
  27. data/lib/metaforce/reporters/deploy_reporter.rb +69 -0
  28. data/lib/metaforce/reporters/retrieve_reporter.rb +11 -0
  29. data/lib/metaforce/services/client.rb +84 -0
  30. data/lib/metaforce/version.rb +3 -0
  31. data/metaforce.gemspec +34 -0
  32. data/spec/fixtures/package.xml +17 -0
  33. data/spec/fixtures/payload.zip +0 -0
  34. data/spec/fixtures/requests/check_deploy_status/done.xml +33 -0
  35. data/spec/fixtures/requests/check_deploy_status/error.xml +26 -0
  36. data/spec/fixtures/requests/check_retrieve_status/success.xml +37 -0
  37. data/spec/fixtures/requests/check_status/done.xml +19 -0
  38. data/spec/fixtures/requests/check_status/not_done.xml +19 -0
  39. data/spec/fixtures/requests/create/in_progress.xml +12 -0
  40. data/spec/fixtures/requests/delete/in_progress.xml +12 -0
  41. data/spec/fixtures/requests/deploy/in_progress.xml +13 -0
  42. data/spec/fixtures/requests/describe_layout/success.xml +15 -0
  43. data/spec/fixtures/requests/describe_metadata/success.xml +230 -0
  44. data/spec/fixtures/requests/foo/invalid_session.xml +15 -0
  45. data/spec/fixtures/requests/list_metadata/no_result.xml +6 -0
  46. data/spec/fixtures/requests/list_metadata/objects.xml +33 -0
  47. data/spec/fixtures/requests/login/failure.xml +15 -0
  48. data/spec/fixtures/requests/login/success.xml +39 -0
  49. data/spec/fixtures/requests/retrieve/in_progress.xml +12 -0
  50. data/spec/fixtures/requests/send_email/success.xml +1 -0
  51. data/spec/fixtures/requests/update/in_progress.xml +12 -0
  52. data/spec/lib/cli_spec.rb +42 -0
  53. data/spec/lib/client_spec.rb +39 -0
  54. data/spec/lib/config_spec.rb +12 -0
  55. data/spec/lib/job/deploy_spec.rb +54 -0
  56. data/spec/lib/job/retrieve_spec.rb +28 -0
  57. data/spec/lib/job_spec.rb +111 -0
  58. data/spec/lib/login_spec.rb +18 -0
  59. data/spec/lib/manifest_spec.rb +35 -0
  60. data/spec/lib/metadata/client_spec.rb +135 -0
  61. data/spec/lib/metaforce_spec.rb +42 -0
  62. data/spec/lib/reporters/base_reporter_spec.rb +79 -0
  63. data/spec/lib/reporters/deploy_reporter_spec.rb +124 -0
  64. data/spec/lib/reporters/retrieve_reporter_spec.rb +14 -0
  65. data/spec/lib/services/client_spec.rb +37 -0
  66. data/spec/spec_helper.rb +37 -0
  67. data/spec/support/client.rb +39 -0
  68. data/wsdl/23.0/metadata.xml +3520 -0
  69. data/wsdl/23.0/partner.xml +3190 -0
  70. data/wsdl/26.0/metadata.xml +4750 -0
  71. data/wsdl/26.0/partner.xml +3340 -0
  72. data/wsdl/34.0/metadata.xml +7981 -0
  73. data/wsdl/34.0/partner.xml +5398 -0
  74. data/wsdl/35.0/metadata.xml +8183 -0
  75. data/wsdl/35.0/partner.xml +5755 -0
  76. data/wsdl/40.0/metadata.xml +29052 -0
  77. data/wsdl/40.0/partner.xml +7642 -0
  78. metadata +327 -0
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <soapenv:Body>
4
+ <retrieveResponse>
5
+ <result>
6
+ <done>false</done>
7
+ <id>04sU0000000WkdIIAS</id>
8
+ <state>InProgress</state>
9
+ </result>
10
+ </retrieveResponse>
11
+ </soapenv:Body>
12
+ </soapenv:Envelope>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com"><soapenv:Body><sendEmailResponse><result><success>true</success></result></sendEmailResponse></soapenv:Body></soapenv:Envelope>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <soapenv:Body>
4
+ <updateResponse>
5
+ <result>
6
+ <done>false</done>
7
+ <id>04sU0000000WNWoIAO</id>
8
+ <state>InProgress</state>
9
+ </result>
10
+ </updateResponse>
11
+ </soapenv:Body>
12
+ </soapenv:Envelope>
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'metaforce/cli'
3
+
4
+ describe Metaforce::CLI do
5
+ before do
6
+ Metaforce::Client.any_instance.stub(:deploy).and_return(double('deploy job').as_null_object)
7
+ Metaforce::Client.any_instance.stub(:retrieve).and_return(double('retrieve job').as_null_object)
8
+ subject.stub(:config).and_return(nil)
9
+ end
10
+
11
+ describe 'credentials' do
12
+ # let(:output) { capture(:stdout) { subject.deploy('./path') } }
13
+
14
+ # context 'when supplied credentials from the command line' do
15
+ # let(:options) { indifferent_hash(:username => 'foo', :password => 'bar', :security_token => 'token', :deploy_options => {}) }
16
+
17
+ # it 'uses supplied credentials from command line' do
18
+ # subject.options = options
19
+ # Metaforce.should_receive(:new).with(indifferent_hash(options).slice(:username, :password, :security_token)).and_call_original
20
+ # # output.should include('Deploying: ./path')
21
+ # expect { subject.deploy('./path') }.to output('Deploying: ./path {}').to_stdout
22
+ # end
23
+ # end
24
+
25
+ context 'when supplied credentials from config file' do
26
+ let(:options) { indifferent_hash(:environment => 'production', :deploy_options => {}) }
27
+ let(:config) { { 'username' => 'foo', 'password' => 'bar', 'security_token' => 'token' } }
28
+
29
+ it 'uses credentials from the config file' do
30
+ subject.options = options
31
+ subject.stub(:config).and_return('production' => config)
32
+ Metaforce.should_receive(:new).with(indifferent_hash(config)).and_call_original
33
+ # output.should include('Deploying: ./path')
34
+ expect { subject.deploy('./path') }.to output("Deploying: ./path {}\n").to_stdout
35
+ end
36
+ end
37
+ end
38
+
39
+ def indifferent_hash(hash)
40
+ Thor::CoreExt::HashWithIndifferentAccess.new(hash)
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce::Client do
4
+ let(:options) { { session_id: 'foobar' } }
5
+ let(:client) { described_class.new(options) }
6
+
7
+ describe '.metadata' do
8
+ subject { client.metadata }
9
+ it { should be_a Metaforce::Metadata::Client }
10
+ end
11
+
12
+ describe '.services' do
13
+ subject { client.services }
14
+ it { should be_a Metaforce::Services::Client }
15
+ end
16
+
17
+ describe '.method_missing' do
18
+ [:services, :metadata].each do |type|
19
+ context "when the #{type} client responds to method" do
20
+ it 'proxies to the method' do
21
+ # client.send(type).should_receive(:respond_to?).with(:foobar, false).and_return(true)
22
+ client.send(type).should_receive(:foobar)
23
+ client.foobar
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'when neither client responds to method' do
29
+ it 'raises an exception' do
30
+ expect { client.foobar }.to raise_error NoMethodError
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '.inspect' do
36
+ subject { client.inspect }
37
+ it { should eq '#<Metaforce::Client @options={:session_id=>"foobar"}>' }
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce do
4
+ describe '.configuration' do
5
+ subject { Metaforce.configuration }
6
+ it { should set_default(:api_version).to('26.0') }
7
+ it { should set_default(:host).to('login.salesforce.com') }
8
+ it { should set_default(:endpoint).to('https://login.salesforce.com/services/Soap/u/26.0') }
9
+ it { should set_default(:partner_wsdl).to(File.expand_path('../../../wsdl/26.0/partner.xml', __FILE__)) }
10
+ it { should set_default(:metadata_wsdl).to(File.expand_path('../../../wsdl/26.0/metadata.xml', __FILE__)) }
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce::Job::Deploy do
4
+ let(:client) { double('metadata client') }
5
+ let(:path) { File.expand_path('../../../fixtures/payload.zip', __FILE__) }
6
+ let(:job) { described_class.new client, path }
7
+
8
+ describe '.perform' do
9
+ subject { job.perform }
10
+
11
+ context 'when the path is a file' do
12
+ before do
13
+ client.should_receive(:_deploy).with(/^UEsDBA.*/, {}).and_return(Hashie::Mash.new(id: '1234'))
14
+ client.should_receive(:status).at_least(1).times.and_return(Hashie::Mash.new(done: true, state: 'Completed'))
15
+ end
16
+
17
+ it { should eq job }
18
+ its(:id) { should eq '1234' }
19
+ end
20
+
21
+ context 'when the path is a directory' do
22
+ before do
23
+ client.should_receive(:_deploy).with(/^UEsDBB.*/, {}).and_return(Hashie::Mash.new(id: '1234'))
24
+ client.should_receive(:status).at_least(1).times.and_return(Hashie::Mash.new(done: true, state: 'Completed'))
25
+ end
26
+
27
+ let(:path) { File.expand_path('../../../fixtures', __FILE__) }
28
+ it { should eq job }
29
+ its(:id) { should eq '1234' }
30
+ end
31
+ end
32
+
33
+ describe '.result' do
34
+ let(:response) { Hashie::Mash.new(success: true) }
35
+
36
+ before do
37
+ client.should_receive(:status).with(job.id, :deploy).and_return(response)
38
+ end
39
+
40
+ subject { job.result }
41
+ it { should eq response }
42
+ end
43
+
44
+ describe '.success?' do
45
+ let(:response) { Hashie::Mash.new(success: true) }
46
+
47
+ before do
48
+ client.should_receive(:status).with(job.id, :deploy).and_return(response)
49
+ end
50
+
51
+ subject { job.success? }
52
+ it { should be_truthy }
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce::Job::Retrieve do
4
+ let(:client) { double('metadata client') }
5
+ let(:job) { described_class.new client }
6
+
7
+ describe '.result' do
8
+ let(:response) { Hashie::Mash.new(success: true) }
9
+
10
+ before do
11
+ client.should_receive(:status).with(job.id, :retrieve).and_return(response)
12
+ end
13
+
14
+ subject { job.result }
15
+ it { should eq response }
16
+ end
17
+
18
+ describe '.zip_file' do
19
+ let(:response) { Hashie::Mash.new(success: true, zip_file: 'foobar') }
20
+
21
+ before do
22
+ client.should_receive(:status).with(job.id, :retrieve).and_return(response)
23
+ end
24
+
25
+ subject { job.zip_file.bytes }
26
+ it { should eq [126, 138, 27, 106] }
27
+ end
28
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce::Job do
4
+ let(:client) { double('client') }
5
+ let(:job) { described_class.new(client) }
6
+
7
+ describe '.perform' do
8
+ it 'starts a heart beat' do
9
+ job.should_receive(:start_heart_beat)
10
+ job.perform
11
+ end
12
+ end
13
+
14
+ describe '.started?' do
15
+ subject { job.started? }
16
+
17
+ context 'when .perform has been called and an @id has been set' do
18
+ before do
19
+ job.instance_variable_set(:@id, '1234')
20
+ end
21
+
22
+ it { should be_truthy }
23
+ end
24
+
25
+ context 'when .perform has not been called and no @id has been set' do
26
+ it { should be_falsey }
27
+ end
28
+ end
29
+
30
+ describe '.on_complete' do
31
+ it 'allows the user to register an on_complete callback' do
32
+ client.should_receive(:status).at_least(1).times.and_return(Hashie::Mash.new(done: true, state: 'Completed'))
33
+ called = false
34
+ block = lambda { |job| called = true }
35
+ job.on_complete &block
36
+ job.perform
37
+ expect(called).to be_truthy
38
+ end
39
+ end
40
+
41
+ describe '.on_error' do
42
+ it 'allows the user to register an on_error callback' do
43
+ client.should_receive(:status).at_least(1).times.and_return(Hashie::Mash.new(done: true, state: 'Error'))
44
+ called = false
45
+ block = lambda { |job| called = true }
46
+ job.on_error &block
47
+ job.perform
48
+ expect(called).to be_truthy
49
+ end
50
+ end
51
+
52
+ describe '.status' do
53
+ before do
54
+ client.should_receive(:status)
55
+ end
56
+
57
+ subject { job.status }
58
+ it { should be_nil }
59
+ end
60
+
61
+ describe '.done?' do
62
+ subject { job.done? }
63
+
64
+ context 'when done' do
65
+ before do
66
+ client.should_receive(:status).and_return(Hashie::Mash.new(done: true))
67
+ end
68
+
69
+ it { should be_truthy }
70
+ end
71
+
72
+ context 'when not done' do
73
+ before do
74
+ client.should_receive(:status).and_return(Hashie::Mash.new(done: false))
75
+ end
76
+
77
+ it { should be_falsey }
78
+ end
79
+ end
80
+
81
+ describe '.state' do
82
+ subject { job.state }
83
+
84
+ context 'when done' do
85
+ before do
86
+ client.should_receive(:status).and_return(Hashie::Mash.new(done: true, state: 'Completed'))
87
+ end
88
+
89
+ it { should eq 'Completed' }
90
+ end
91
+
92
+ context 'when not done' do
93
+ before do
94
+ client.should_receive(:status).once.and_return(Hashie::Mash.new(done: false))
95
+ end
96
+
97
+ it { should be_falsey }
98
+ end
99
+ end
100
+
101
+ %w[Queued InProgress Completed Error].each do |state|
102
+ describe ".#{state.underscore}?" do
103
+ before do
104
+ client.should_receive(:status).and_return(Hashie::Mash.new(done: true, state: state))
105
+ end
106
+
107
+ subject { job.send(:"#{state.underscore}?") }
108
+ it { should be_truthy }
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce::Login do
4
+ let(:klass) { described_class.new('foo', 'bar', 'whizbang') }
5
+
6
+ describe '.login' do
7
+ before do
8
+ savon.expects(:login).with(:username => 'foo', :password => 'barwhizbang').returns(:success)
9
+ end
10
+
11
+ subject { klass.login }
12
+ it { should be_a Hash }
13
+ its([:session_id]) { should eq '00DU0000000Ilbh!AQoAQHVcube9Z6CRlbR9Eg8ZxpJlrJ6X8QDbnokfyVZItFKzJsLH' \
14
+ 'IRGiqhzJkYsNYRkd3UVA9.s82sbjEbZGUqP3mG6TP_P8' }
15
+ its([:metadata_server_url]) { should eq 'https://na12-api.salesforce.com/services/Soap/m/23.0/00DU0000000Albh' }
16
+ its([:server_url]) { should eq 'https://na12-api.salesforce.com/services/Soap/u/23.0/00DU0000000Ilbh' }
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe Metaforce::Manifest do
4
+ let(:package_xml) { File.open(File.join(File.dirname(__FILE__), '../fixtures/package.xml'), 'r').read }
5
+ let(:package_hash) do
6
+ { :apex_class => ['TestClass', 'AnotherClass'],
7
+ :apex_component => ['Component'],
8
+ :static_resource => ['Assets'] }
9
+ end
10
+ let(:package) { package_xml }
11
+ let(:manifest) { described_class.new(package) }
12
+
13
+ describe '.to_xml' do
14
+ let(:package) { package_hash }
15
+ subject { manifest.to_xml }
16
+ it { should eq package_xml }
17
+ end
18
+
19
+ describe '.to_hash' do
20
+ let(:package) { package_xml }
21
+ subject { manifest.to_hash }
22
+ it { should eq package_hash }
23
+ end
24
+
25
+ describe '.to_package' do
26
+ subject { manifest.to_package }
27
+ it do
28
+ should eq [
29
+ { :members => ['TestClass', 'AnotherClass'], :name => 'ApexClass' },
30
+ { :members => ['Component'], :name => 'ApexComponent' },
31
+ { :members => ['Assets'], :name => 'StaticResource' }
32
+ ]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metaforce::Metadata::Client do
4
+ let(:client) { described_class.new(:session_id => 'foobar', :metadata_server_url => 'https://na12-api.salesforce.com/services/Soap/u/23.0/00DU0000000Ilbh') }
5
+
6
+ it_behaves_like 'a client'
7
+
8
+ describe '.list_metadata' do
9
+ context 'with a single symbol' do
10
+ before do
11
+ savon.expects(:list_metadata).with(:queries => [{:type => 'ApexClass'}]).returns(:objects)
12
+ end
13
+
14
+ subject { client.list_metadata(:apex_class) }
15
+ it { should be_an Array }
16
+ end
17
+
18
+ context 'with a single string' do
19
+ before do
20
+ savon.expects(:list_metadata).with(:queries => [{:type => 'ApexClass'}]).returns(:objects)
21
+ end
22
+
23
+ subject { client.list_metadata('ApexClass') }
24
+ it { should be_an Array }
25
+ end
26
+ end
27
+
28
+ describe '.describe' do
29
+ context 'with no version' do
30
+ before do
31
+ savon.expects(:describe_metadata).with(nil).returns(:success)
32
+ end
33
+
34
+ subject { client.describe }
35
+ it { should be_a Hash }
36
+ end
37
+
38
+ context 'with a version' do
39
+ before do
40
+ savon.expects(:describe_metadata).with(:api_version => '18.0').returns(:success)
41
+ end
42
+
43
+ subject { client.describe('18.0') }
44
+ it { should be_a Hash }
45
+ end
46
+ end
47
+
48
+ describe '.status' do
49
+ context 'with a single id' do
50
+ before do
51
+ savon.expects(:check_status).with(:ids => ['1234']).returns(:done)
52
+ end
53
+
54
+ subject { client.status '1234' }
55
+ it { should be_a Hash }
56
+ end
57
+ end
58
+
59
+ describe '._deploy' do
60
+ before do
61
+ savon.expects(:deploy).with(:zip_file => 'foobar', :deploy_options => {}).returns(:in_progress)
62
+ end
63
+
64
+ subject { client._deploy('foobar') }
65
+ it { should be_a Hash }
66
+ end
67
+
68
+ describe '.deploy' do
69
+ subject { client.deploy File.expand_path('../../path/to/zip') }
70
+ it { should be_a Metaforce::Job::Deploy }
71
+ end
72
+
73
+ describe '._retrieve' do
74
+ let(:options) { double('options') }
75
+
76
+ before do
77
+ savon.expects(:retrieve).with(:retrieve_request => options).returns(:in_progress)
78
+ end
79
+
80
+ subject { client._retrieve(options) }
81
+ it { should be_a Hash }
82
+ end
83
+
84
+ describe '.retrieve' do
85
+ subject { client.retrieve }
86
+ it { should be_a Metaforce::Job::Retrieve }
87
+ end
88
+
89
+ describe '.retrieve_unpackaged' do
90
+ let(:manifest) { Metaforce::Manifest.new(:custom_object => ['Account']) }
91
+ subject { client.retrieve_unpackaged(manifest) }
92
+ it { should be_a Metaforce::Job::Retrieve }
93
+ end
94
+
95
+ describe '._create' do
96
+ before do
97
+ savon.expects(:create).with(:metadata => [{:full_name => 'component', :label => 'test', :content => "Zm9vYmFy\n"}], :attributes! => {'ins0:metadata' => {'xsi:type' => 'ins0:ApexComponent'}}).returns(:in_progress)
98
+ end
99
+
100
+ subject { client._create(:apex_component, :full_name => 'component', :label => 'test', :content => 'foobar') }
101
+ it { should be_a Hash }
102
+ end
103
+
104
+ describe '._delete' do
105
+ context 'with a single name' do
106
+ before do
107
+ savon.expects(:delete).with(:metadata => [{:full_name => 'component'}], :attributes! => {'ins0:metadata' => {'xsi:type' => 'ins0:ApexComponent'}}).returns(:in_progress)
108
+ end
109
+
110
+ subject { client._delete(:apex_component, 'component') }
111
+ it { should be_a Hash }
112
+ end
113
+
114
+ context 'with multiple' do
115
+ before do
116
+ savon.expects(:delete).with(:metadata => [{:full_name => 'component1'}, {:full_name => 'component2'}], :attributes! => {'ins0:metadata' => {'xsi:type' => 'ins0:ApexComponent'}}).returns(:in_progress)
117
+ end
118
+
119
+ subject { client._delete(:apex_component, 'component1', 'component2') }
120
+ it { should be_a Hash }
121
+ end
122
+ end
123
+
124
+ describe '._update' do
125
+ before do
126
+ savon.expects(:update).with(:metadata => {:current_name => 'old_component', :metadata => [{:full_name => 'component', :label => 'test', :content => "Zm9vYmFy\n"}], :attributes! => {:metadata => {'xsi:type' => 'ins0:ApexComponent'}}}).returns(:in_progress)
127
+ end
128
+
129
+ subject { client._update(:apex_component, 'old_component', :full_name => 'component', :label => 'test', :content => 'foobar') }
130
+ it { should be_a Hash }
131
+ end
132
+ end
133
+
134
+
135
+