metaforce-beta 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48b04d4fa24a6113f10acea5b0966fedab064370
4
+ data.tar.gz: b2b3515deac5b9312b15c506eb5f58b461fb414b
5
+ SHA512:
6
+ metadata.gz: 02a9b697f81eae485027dc7a9f914f269da44df68fb1b8b7a03d68b2a2fdc5ba69ae6c395761647b0cad3d8198f392478f3d3f68e206ca017641e6f5459878ff
7
+ data.tar.gz: f951e172ef8f8bd1364e9a830e9248cf93401a6c5aec030510cec0214fc99fae6946eefa0505553f655c943f5f7c5c6f28e1a035efb4bd9a1423fdd4b709993f
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ examples/tmp
6
+ deploy.zip
7
+ retrieve.zip
8
+ test.rb
9
+ .metaforce.yml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - jruby-head
5
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in metaforce.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Eric J. Holmes
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,193 @@
1
+ # Metaforce
2
+
3
+ [![Build Status](https://travis-ci.org/ejholmes/metaforce.png?branch=master)](https://travis-ci.org/ejholmes/metaforce) [![Code Climate](https://codeclimate.com/github/ejholmes/metaforce.png)](https://codeclimate.com/github/ejholmes/metaforce) [![Dependency Status](https://gemnasium.com/ejholmes/metaforce.png)](https://gemnasium.com/ejholmes/metaforce)
4
+
5
+ Metaforce is a Ruby gem for interacting with the Salesforce [Metadata](http://www.salesforce.com/us/developer/docs/api_meta/index.htm)
6
+ and [Services](http://www.salesforce.com/us/developer/docs/api/index.htm) APIs.
7
+
8
+ [Documentation](http://rubydoc.info/gems/metaforce/frames)
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'metaforce'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install metaforce
23
+
24
+ ## Usage
25
+
26
+ ### Initialization
27
+
28
+ #### Username and Password
29
+
30
+ To initialize a new client, you call `Metaforce.new` with a hash that specifies
31
+ the `:username`, `:password`, and `:security_token`.
32
+
33
+ ```ruby
34
+ client = Metaforce.new :username => 'username',
35
+ :password => 'password',
36
+ :security_token => 'security token'
37
+ ```
38
+
39
+ Or you can specify the username, password and security token with environment
40
+ variables:
41
+
42
+ ```bash
43
+ export SALESFORCE_USERNAME="username"
44
+ export SALESFORCE_PASSWORD="password"
45
+ export SALESFORCE_SECURITY_TOKEN="security token"
46
+ ```
47
+
48
+ ```ruby
49
+ client = Metaforce.new
50
+ ```
51
+
52
+ #### Asynchronous Tasks
53
+
54
+ Some calls to the SOAP API's are performed asynchronously (such as deployments),
55
+ meaning the response needs to be polled for. Any call to the SOAP API's that
56
+ are performed asynchronously will return a Metaforce::Job object, which can be used to
57
+ subscribe to `on_complete` and `on_error` callbacks. The Metaforce::Job class
58
+ will poll the status of the asynchronous job in a thread until it completes or
59
+ fails.
60
+
61
+ * * *
62
+
63
+ ### deploy(path, options={})
64
+
65
+ Takes a path (can be a path to a directory, or a zip file), and a set of
66
+ [DeployOptions](http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_deploy.htm#deploy_options)
67
+ and returns a `Metaforce::Job::Deploy`.
68
+
69
+ ```ruby
70
+ client.deploy(File.expand_path('./src'))
71
+ .on_complete { |job| puts "Finished deploy #{job.id}!" }
72
+ .on_error { |job| puts "Something bad happened!" }
73
+ .perform
74
+ #=> #<Metaforce::Job::Deploy @id='1234'>
75
+ ```
76
+
77
+ * * *
78
+
79
+ ### retrieve\_unpackaged(manifest, options={})
80
+
81
+ Takes a manifest (`Metaforce::Manifest` or a path to a package.xml file) and a
82
+ set of [RetrieveOptions](http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_retrieve_request.htm)
83
+ and returns a `Metaforce::Job::Retrieve`.
84
+
85
+ ```ruby
86
+ manifest = Metaforce::Manifest.new(:custom_object => ['Account'])
87
+ client.retrieve_unpackaged(manifest)
88
+ .extract_to('./tmp')
89
+ .perform
90
+ #=> #<Metaforce::Job::Retrieve @id='1234'>
91
+ ```
92
+
93
+ * * *
94
+
95
+ ### create(type, metadata={})
96
+
97
+ Takes a Symbol type and a Hash of [Metadata Attributes](http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_types_list.htm)
98
+ and returns a `Metaforce::Job::CRUD`.
99
+
100
+ ```ruby
101
+ client.create(:apex_page, full_name: 'Foobar', content: 'Hello World!')
102
+ .on_complete { |job| puts "ApexPage created." }
103
+ .perform
104
+ #=> #<Metaforce::Job::CRUD @id='1234'>
105
+ ```
106
+
107
+ * * *
108
+
109
+ ### update(type, current\_name metadata={})
110
+
111
+ Takes a Symbol type, the current `full_name` of the resource, and a Hash of
112
+ [Metadata Attributes](http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_types_list.htm)
113
+ and returns a `Metaforce::Job::CRUD`.
114
+
115
+ ```ruby
116
+ client.update(:apex_page, 'Foobar', content: 'Hello World! Some new content!')
117
+ .on_complete { |job| puts "ApexPage updated." }
118
+ .perform
119
+ #=> #<Metaforce::Job::CRUD @id='1234'>
120
+ ```
121
+
122
+ * * *
123
+
124
+ ### delete(type, \*args)
125
+
126
+ Takes a Symbol type, and the `full_name` of a resource and returns a `Metaforce::Job::CRUD`.
127
+
128
+ ```ruby
129
+ client.delete(:apex_page, 'Foobar')
130
+ .on_complete { |job| puts "ApexPage deleted." }
131
+ .perform
132
+ #=> #<Metaforce::Job::CRUD @id='1234'>
133
+ ```
134
+
135
+ * * *
136
+
137
+ ### send\_email(options={})
138
+
139
+ Sends a [SingleEmailMessage](http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_sendemail.htm) using Salesforce.
140
+
141
+ ```ruby
142
+ client.send_email(
143
+ to_addresses: ['foo@bar.com'],
144
+ subject: 'Hello World',
145
+ plain_text_body: 'Hello World'
146
+ )
147
+ ```
148
+
149
+ If you're using Rails, check out the [metaforce-delivery\_method](https://github.com/ejholmes/metaforce-delivery_method)
150
+ gem, which allows you to use Salesforce as the delivery mechanism for sending
151
+ emails.
152
+
153
+ ## Command-line
154
+
155
+ Metaforce also comes with a handy command line utility that can deploy and retrieve
156
+ code from Salesforce. It also allows you to watch a directory and deploy when
157
+ anything changes.
158
+
159
+ When you deploy, it will also run all tests and provide you with a report,
160
+ similar to rspec.
161
+
162
+ ```bash
163
+ $ metaforce deploy ./src
164
+ Deploying: ./src
165
+ ```
166
+
167
+ ```bash
168
+ $ metaforce watch ./src
169
+ Watching: ./src
170
+ ```
171
+
172
+ ```bash
173
+ $ metaforce retrieve ./src
174
+ Retrieving: ./src/package.xml
175
+
176
+ $ metaforce retrieve ./src/package.xml ./other-location
177
+ Retrieving: ./src/package.xml
178
+ ```
179
+
180
+ ### .metaforce.yml
181
+
182
+ The metaforce command will pull it's configuration from a `.metaforce.yml`
183
+ file, if present. You can provide options for multiple environments, then use
184
+ the `-e` swtich on the command line to use an environment. See the
185
+ [examples](examples) directory for an example.
186
+
187
+ ## Contributing
188
+
189
+ 1. Fork it
190
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
191
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
192
+ 4. Push to the branch (`git push origin my-new-feature`)
193
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default => [:spec]
5
+
6
+ require 'rspec/core/rake_task'
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'metaforce'
5
+
6
+ begin
7
+ require 'metaforce/cli'
8
+ Metaforce::CLI.start
9
+ rescue Interrupt => e
10
+ puts "\nQuitting..."
11
+ exit 1
12
+ rescue SystemExit => e
13
+ exit e.status
14
+ end
@@ -0,0 +1,52 @@
1
+ require 'metaforce'
2
+
3
+ # Run with: `USER=user PASS=pass TOKEN=securitytoken bundle exec ruby example.rb`
4
+
5
+ Metaforce.configuration.log = false
6
+
7
+ client = Metaforce.new :username => ENV['USER'],
8
+ :password => ENV['PASS'],
9
+ :security_token => ENV['TOKEN']
10
+
11
+ Metaforce::Job.disable_threading!
12
+
13
+ # Test sending an email.
14
+ print 'send email to: '; email = STDIN.gets.chomp
15
+ client.send_email(
16
+ to_addresses: [email],
17
+ subject: 'Test',
18
+ plain_text_body: 'Test'
19
+ ) if email
20
+
21
+ # Test retrieve.
22
+ manifest = Metaforce::Manifest.new(:custom_object => ['Account'])
23
+ client.retrieve_unpackaged(manifest)
24
+ .on_complete { |job| puts "Retrieve Completed: #{job.id}."}
25
+ .on_error { |job| puts "Retrieve Failed: #{job.id}."}
26
+ .extract_to('./tmp')
27
+ .perform
28
+
29
+ # Test deployment.
30
+ client.deploy('./tmp')
31
+ .on_complete { |job| puts "Deploy Completed: #{job.id}. #{job.result}"}
32
+ .on_error { |job| puts "Deploy Failed: #{job.id}."}
33
+ .on_poll { |job| puts "Deploy: Polled status for #{job.id}."}
34
+ .perform
35
+
36
+ # Test delete.
37
+ client.delete(:apex_page, 'TestPage')
38
+ .on_complete { |job| puts "Delete Completed: #{job.id}."}
39
+ .on_error { |job| puts "Delete Failed: #{job.id}."}
40
+ .perform
41
+
42
+ # Test create.
43
+ client.create(:apex_page, :full_name => 'TestPage', label: 'Test page', :content => '<apex:page>foobar</apex:page>')
44
+ .on_complete { |job| puts "Create Completed: #{job.id}."}
45
+ .on_error { |job| puts "Create Failed: #{job.id}."}
46
+ .perform
47
+
48
+ # Test update.
49
+ client.update(:apex_page, 'TestPage', :full_name => 'TestPage', :label => 'Test page', :content => '<apex:page>hello world</apex:page>')
50
+ .on_complete { |job| puts "Update Completed: #{job.id}."}
51
+ .on_error { |job| puts "Update Failed: #{job.id}."}
52
+ .perform
@@ -0,0 +1,34 @@
1
+ require 'savon'
2
+ require 'hashie'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+
6
+ require 'metaforce/version'
7
+ require 'metaforce/config'
8
+ require 'metaforce/job'
9
+ require 'metaforce/abstract_client'
10
+ require 'metaforce/services/client'
11
+ require 'metaforce/metadata/client'
12
+
13
+ module Metaforce
14
+ autoload :Manifest, 'metaforce/manifest'
15
+ autoload :Login, 'metaforce/login'
16
+ autoload :Client, 'metaforce/client'
17
+
18
+ class << self
19
+ # Public: Initializes instances of the metadata and services api clients
20
+ # and provides helper methods for deploying and retrieving code.
21
+ def new(*args)
22
+ Client.new(*args)
23
+ end
24
+
25
+ # Performs a login and retrurns the session
26
+ def login(options={})
27
+ options = HashWithIndifferentAccess.new(options)
28
+ username = options.fetch(:username, ENV['SALESFORCE_USERNAME'])
29
+ password = options.fetch(:password, ENV['SALESFORCE_PASSWORD'])
30
+ security_token = options.fetch(:security_token, ENV['SALESFORCE_SECURITY_TOKEN'])
31
+ Login.new(username, password, security_token).login
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,86 @@
1
+ module Metaforce
2
+ class AbstractClient
3
+ class << self
4
+ # Internal
5
+ def endpoint(key)
6
+ define_method :endpoint do; @options[key] end
7
+ end
8
+
9
+ # Internal
10
+ def wsdl(wsdl)
11
+ define_method :wsdl do; wsdl end
12
+ end
13
+ end
14
+
15
+ # Public: Initialize a new client.
16
+ #
17
+ # options - A hash of options, which should have a :session_id key
18
+ def initialize(options={})
19
+ raise 'Please specify a hash of options' unless options.is_a?(Hash)
20
+ @options = options
21
+ end
22
+
23
+ private
24
+
25
+ # Internal: The Savon client to send SOAP requests with.
26
+ def client
27
+ @client ||= Savon.client(wsdl) do |wsdl|
28
+ wsdl.endpoint = endpoint
29
+ end.tap do |client|
30
+ client.config.soap_header = soap_headers
31
+ client.http.auth.ssl.verify_mode = :none
32
+ end
33
+ end
34
+
35
+ # Internal: Performs a SOAP request. If the session is invalid, it will
36
+ # attempt to reauthenticate by called the reauthentication handler if
37
+ # present.
38
+ def request(*args, &block)
39
+ authenticate! unless session_id
40
+ retries = authentication_retries
41
+ begin
42
+ perform_request(*args, &block)
43
+ rescue Savon::SOAP::Fault => e
44
+ if e.message =~ /INVALID_SESSION_ID/ && authentication_handler && retries > 0
45
+ authenticate!
46
+ retries -= 1
47
+ retry
48
+ end
49
+ raise
50
+ end
51
+ end
52
+
53
+ def perform_request(*args, &block)
54
+ response = client.request(*args, &block)
55
+ Hashie::Mash.new(response.body)[:"#{args[0]}_response"].result
56
+ end
57
+
58
+ # Internal Calls the authentication handler, which should set @options to a new
59
+ # hash.
60
+ def authenticate!
61
+ options = authentication_handler.call(self, @options)
62
+ @options.merge!(options)
63
+ client.config.soap_header = soap_headers
64
+ end
65
+
66
+ # A proc object that gets called when the client needs to reauthenticate.
67
+ def authentication_handler
68
+ Metaforce.configuration.authentication_handler
69
+ end
70
+
71
+ def authentication_retries
72
+ 3
73
+ end
74
+
75
+ # Internal: Soap headers to set for authenticate.
76
+ def soap_headers
77
+ { 'ins0:SessionHeader' => { 'ins0:sessionId' => session_id } }
78
+ end
79
+
80
+ # Internal: The session id, which can be obtained by calling
81
+ # Metaforce.login or through OAuth.
82
+ def session_id
83
+ @options[:session_id]
84
+ end
85
+ end
86
+ end