metaforce 0.5.3 → 1.0.0a
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.
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/Gemfile +1 -11
- data/LICENSE +22 -0
- data/README.md +91 -96
- data/Rakefile +6 -14
- data/examples/example.rb +51 -0
- data/lib/metaforce/abstract_client.rb +76 -0
- data/lib/metaforce/client.rb +27 -0
- data/lib/metaforce/config.rb +41 -19
- data/lib/metaforce/job/crud.rb +13 -0
- data/lib/metaforce/job/deploy.rb +87 -0
- data/lib/metaforce/job/retrieve.rb +92 -0
- data/lib/metaforce/job.rb +183 -0
- data/lib/metaforce/login.rb +39 -0
- data/lib/metaforce/manifest.rb +18 -93
- data/lib/metaforce/metadata/client/crud.rb +86 -0
- data/lib/metaforce/metadata/client/file.rb +113 -0
- data/lib/metaforce/metadata/client.rb +7 -225
- data/lib/metaforce/services/client.rb +45 -86
- data/lib/metaforce/version.rb +1 -1
- data/lib/metaforce.rb +27 -7
- data/metaforce.gemspec +19 -16
- data/spec/fixtures/package.xml +1 -1
- data/spec/fixtures/payload.zip +0 -0
- data/spec/fixtures/requests/{describe_layout → foo}/invalid_session.xml +0 -0
- data/spec/fixtures/requests/send_email/success.xml +1 -0
- data/spec/lib/client_spec.rb +34 -0
- data/spec/lib/config_spec.rb +8 -50
- data/spec/lib/job/deploy_spec.rb +53 -0
- data/spec/lib/job/retrieve_spec.rb +28 -0
- data/spec/lib/job_spec.rb +95 -0
- data/spec/lib/login_spec.rb +18 -0
- data/spec/lib/manifest_spec.rb +22 -168
- data/spec/lib/metadata/client_spec.rb +84 -179
- data/spec/lib/metaforce_spec.rb +20 -0
- data/spec/lib/services/client_spec.rb +22 -35
- data/spec/spec_helper.rb +24 -3
- data/spec/support/client.rb +38 -0
- data/wsdl/26.0/metadata.xml +4750 -0
- data/wsdl/26.0/partner.xml +3340 -0
- metadata +114 -77
- data/Guardfile +0 -9
- data/bin/metaforce +0 -6
- data/lib/metaforce/core_extensions/string.rb +0 -31
- data/lib/metaforce/core_extensions.rb +0 -1
- data/lib/metaforce/custom_actions.rb +0 -29
- data/lib/metaforce/error.rb +0 -3
- data/lib/metaforce/login_details.rb +0 -28
- data/lib/metaforce/metadata/crud.rb +0 -103
- data/lib/metaforce/metadata/file.rb +0 -74
- data/lib/metaforce/metadata/transaction.rb +0 -100
- data/lib/metaforce/metadata.rb +0 -4
- data/lib/metaforce/rake/deploy.rb +0 -35
- data/lib/metaforce/rake/retrieve.rb +0 -39
- data/lib/metaforce/rake/tests.rb +0 -62
- data/lib/metaforce/rake.rb +0 -43
- data/lib/metaforce/services.rb +0 -1
- data/lib/metaforce/tasks/README.md +0 -62
- data/lib/metaforce/tasks/metaforce.rake +0 -5
- data/lib/metaforce/thor/metaforce.rb +0 -117
- data/lib/metaforce/types.rb +0 -249
- data/spec/.gitignore +0 -1
- data/spec/fixtures/sample/Rakefile +0 -2
- data/spec/fixtures/sample/metaforce.yml +0 -13
- data/spec/fixtures/sample/src/classes/TestClass.cls +0 -2
- data/spec/fixtures/sample/src/classes/TestClass.cls-meta.xml +0 -5
- data/spec/fixtures/sample/src/package.xml +0 -8
- data/spec/lib/core_extensions/string_spec.rb +0 -23
- data/spec/lib/metadata/crud_spec.rb +0 -66
- data/spec/lib/metadata/file_spec.rb +0 -17
- data/spec/lib/metadata/transaction_spec.rb +0 -68
@@ -0,0 +1,92 @@
|
|
1
|
+
module Metaforce
|
2
|
+
class Job::Retrieve < Job
|
3
|
+
|
4
|
+
# Public: Instantiate a new retrieve job.
|
5
|
+
#
|
6
|
+
# Examples
|
7
|
+
#
|
8
|
+
# job = Metaforce::Job::Retrieve.new(client)
|
9
|
+
# # => #<Metaforce::Job::Retrieve @id=nil>
|
10
|
+
#
|
11
|
+
# Returns self.
|
12
|
+
def initialize(client, options={})
|
13
|
+
super(client)
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Perform the job.
|
18
|
+
#
|
19
|
+
# Examples
|
20
|
+
#
|
21
|
+
# job = Metaforce::Job::Retrieve.new(client)
|
22
|
+
# job.perform
|
23
|
+
# # => #<Metaforce::Job::Retrieve @id='1234'>
|
24
|
+
#
|
25
|
+
# Returns self.
|
26
|
+
def perform
|
27
|
+
@id = client._retrieve(@options).id
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Get the detailed status of the retrieve.
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
#
|
35
|
+
# job.result
|
36
|
+
# # => { :id => '1234', :zip_file => '<base64 encoded content>', ... }
|
37
|
+
#
|
38
|
+
# Returns the RetrieveResult (http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_retrieveresult.htm).
|
39
|
+
def result
|
40
|
+
client.status(id, :retrieve)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Decodes the content of the returned zip file.
|
44
|
+
#
|
45
|
+
# Examples
|
46
|
+
#
|
47
|
+
# job.zip_file
|
48
|
+
# # => '<binary content>'
|
49
|
+
#
|
50
|
+
# Returns the decoded content.
|
51
|
+
def zip_file
|
52
|
+
Base64.decode64(result.zip_file)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Public: Unzips the returned zip file to the location.
|
56
|
+
#
|
57
|
+
# destination - Path to extract the contents to.
|
58
|
+
#
|
59
|
+
# Examples
|
60
|
+
#
|
61
|
+
# job.extract_to('./path')
|
62
|
+
# # => #<Metaforce::Job::Retrieve @id='1234'>
|
63
|
+
#
|
64
|
+
# Returns self.
|
65
|
+
def extract_to(destination)
|
66
|
+
return on_complete { |job| job.extract_to(destination) } unless @id
|
67
|
+
Zip::ZipFile.open(tmp_zip_file) do |zip|
|
68
|
+
zip.each do |f|
|
69
|
+
path = File.join(destination, f.name)
|
70
|
+
FileUtils.mkdir_p(File.dirname(path))
|
71
|
+
zip.extract(f, path) { true }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Internal: Writes the zip file content to a temporary location so it can
|
80
|
+
# be extracted.
|
81
|
+
def tmp_zip_file
|
82
|
+
@tmp_zip_file ||= begin
|
83
|
+
file = Tempfile.new('retrieve')
|
84
|
+
file.write(zip_file)
|
85
|
+
path = file.path
|
86
|
+
file.close
|
87
|
+
path
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'zip/zip'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Metaforce
|
5
|
+
class Job
|
6
|
+
autoload :Deploy, 'metaforce/job/deploy'
|
7
|
+
autoload :Retrieve, 'metaforce/job/retrieve'
|
8
|
+
autoload :CRUD, 'metaforce/job/crud'
|
9
|
+
|
10
|
+
# Public: The id of the AsyncResult returned from Salesforce for
|
11
|
+
# this job.
|
12
|
+
attr_reader :id
|
13
|
+
|
14
|
+
# Public: Instantiate a new job. Doesn't actually do anything until
|
15
|
+
# .perform is called.
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# job = Metaforce::Job.new(client)
|
20
|
+
# # => #<Metaforce::Job @id=nil>
|
21
|
+
#
|
22
|
+
# Returns self.
|
23
|
+
def initialize(client)
|
24
|
+
@_callbacks = Hash.new { |h,k| h[k] = [] }
|
25
|
+
@client = client
|
26
|
+
end
|
27
|
+
|
28
|
+
# Public: Perform the job.
|
29
|
+
#
|
30
|
+
# Examples
|
31
|
+
#
|
32
|
+
# job = Metaforce::Job.new
|
33
|
+
# job.perform
|
34
|
+
# # => #<Metaforce::Job @id=nil>
|
35
|
+
#
|
36
|
+
# Returns self.
|
37
|
+
def perform
|
38
|
+
start_heart_beat
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Register a block to be called when the job has completed.
|
43
|
+
#
|
44
|
+
# Yields the job.
|
45
|
+
#
|
46
|
+
# &block - Proc or Lambda to be run when the job completes.
|
47
|
+
#
|
48
|
+
# Examples
|
49
|
+
#
|
50
|
+
# job.on_complete do |job|
|
51
|
+
# puts "Job ##{job.id} completed!"
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Returns self.
|
55
|
+
def on_complete(&block)
|
56
|
+
@_callbacks[:on_complete] << block
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Register a block to be called when if the job fails.
|
61
|
+
#
|
62
|
+
# Yields the job.
|
63
|
+
#
|
64
|
+
# &block - Proc or Lambda to be run when the job fails.
|
65
|
+
#
|
66
|
+
# Examples
|
67
|
+
#
|
68
|
+
# job.on_error do |job|
|
69
|
+
# puts "Job ##{job.id} failed!"
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# Returns self.
|
73
|
+
def on_error(&block)
|
74
|
+
@_callbacks[:on_error] << block
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Queries the job status from the API.
|
79
|
+
#
|
80
|
+
# Examples
|
81
|
+
#
|
82
|
+
# job.status
|
83
|
+
# # => { :id => '1234', :done => false, ... }
|
84
|
+
#
|
85
|
+
# Returns the AsyncResult (http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_asyncresult.htm).
|
86
|
+
def status
|
87
|
+
client.status(id)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Public: Returns true if the job has completed.
|
91
|
+
#
|
92
|
+
# Examples
|
93
|
+
#
|
94
|
+
# job.done
|
95
|
+
# # => true
|
96
|
+
#
|
97
|
+
# Returns true if the job has completed, false otherwise.
|
98
|
+
def done?
|
99
|
+
status.done
|
100
|
+
end
|
101
|
+
|
102
|
+
# Public: Returns the state if the job has finished processing.
|
103
|
+
#
|
104
|
+
# Examples
|
105
|
+
#
|
106
|
+
# job.state
|
107
|
+
# # => 'Completed'
|
108
|
+
#
|
109
|
+
# Returns the state if the job is done, false otherwise.
|
110
|
+
def state
|
111
|
+
done? && status.state
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: Check if the job is in a given state.
|
115
|
+
#
|
116
|
+
# Examples
|
117
|
+
#
|
118
|
+
# job.queued?
|
119
|
+
# # => false
|
120
|
+
#
|
121
|
+
# Returns true or false.
|
122
|
+
#
|
123
|
+
# Signature
|
124
|
+
#
|
125
|
+
# queued?
|
126
|
+
# in_progress?
|
127
|
+
# completed?
|
128
|
+
# error?
|
129
|
+
%w[Queued InProgress Completed Error].each do |state|
|
130
|
+
define_method :"#{state.underscore}?" do; self.state == state end
|
131
|
+
end
|
132
|
+
|
133
|
+
def inspect
|
134
|
+
"#<#{self.class} @id=#{@id.inspect}>"
|
135
|
+
end
|
136
|
+
|
137
|
+
class << self
|
138
|
+
|
139
|
+
# Internal: Disable threading in tests.
|
140
|
+
def disable_threading!
|
141
|
+
self.class_eval do
|
142
|
+
def start_heart_beat
|
143
|
+
loop do
|
144
|
+
trigger_callbacks && break if completed? || error?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
attr_reader :client
|
154
|
+
|
155
|
+
# Internal: Starts a heart beat in a thread, which polls the job status
|
156
|
+
# until it has completed or timed out.
|
157
|
+
def start_heart_beat
|
158
|
+
Thread.abort_on_exception = true
|
159
|
+
@heart_beat ||= Thread.new do
|
160
|
+
delay = 1
|
161
|
+
loop do
|
162
|
+
sleep (delay = delay * 2)
|
163
|
+
trigger_callbacks && Thread.stop if completed? || error?
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def trigger_callbacks
|
169
|
+
@_callbacks[callback_type].each do |block|
|
170
|
+
block.call(self)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def callback_type
|
175
|
+
if completed?
|
176
|
+
:on_complete
|
177
|
+
elsif error?
|
178
|
+
:on_error
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Metaforce
|
2
|
+
class Login
|
3
|
+
def initialize(username, password, security_token=nil)
|
4
|
+
@username, @password, @security_token = username, password, security_token
|
5
|
+
end
|
6
|
+
|
7
|
+
# Public: Perform the login request.
|
8
|
+
#
|
9
|
+
# Returns a hash with the session id and server urls.
|
10
|
+
def login
|
11
|
+
response = client.request(:login) do
|
12
|
+
soap.body = {
|
13
|
+
:username => username,
|
14
|
+
:password => password
|
15
|
+
}
|
16
|
+
end
|
17
|
+
response.body[:login_response][:result]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Internal: Savon client.
|
23
|
+
def client
|
24
|
+
@client ||= Savon.client(Metaforce.configuration.partner_wsdl) do |wsdl|
|
25
|
+
wsdl.endpoint = Metaforce.configuration.endpoint
|
26
|
+
end.tap { |client| client.http.auth.ssl.verify_mode = :none }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Internal: Usernamed passed in from options.
|
30
|
+
def username
|
31
|
+
@username
|
32
|
+
end
|
33
|
+
|
34
|
+
# Internal: Password + Security Token combined.
|
35
|
+
def password
|
36
|
+
[@password, @security_token].join('')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/metaforce/manifest.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'nokogiri'
|
2
|
-
require '
|
2
|
+
require 'active_support/core_ext'
|
3
3
|
|
4
4
|
module Metaforce
|
5
|
-
class Manifest
|
5
|
+
class Manifest < Hash
|
6
6
|
|
7
|
-
# Initializes a new instance of a manifest (package.xml) file.
|
8
|
-
#
|
7
|
+
# Public: Initializes a new instance of a manifest (package.xml) file.
|
8
|
+
#
|
9
9
|
# It can either take a hash:
|
10
10
|
# {
|
11
11
|
# :apex_class => [
|
@@ -35,71 +35,17 @@ module Metaforce
|
|
35
35
|
# </types>
|
36
36
|
# <version>23.0</version>
|
37
37
|
# </Package>
|
38
|
-
#
|
38
|
+
#
|
39
39
|
def initialize(components={})
|
40
|
-
|
40
|
+
self.replace Hash.new { |h,k| h[k] = [] }
|
41
41
|
if components.is_a?(Hash)
|
42
|
-
|
42
|
+
self.merge!(components)
|
43
43
|
elsif components.is_a?(String)
|
44
|
-
@components = {}
|
45
44
|
self.parse(components)
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# manifest.add :apex_class, 'SomeClass'
|
52
|
-
def add(type, members=nil)
|
53
|
-
unless members.nil?
|
54
|
-
@components[type] = [] if @components[type].nil?
|
55
|
-
members = [members] if members.is_a?(String)
|
56
|
-
members.each do |member|
|
57
|
-
member = member.gsub(/.*\//, '').gsub(/\..*/, '');
|
58
|
-
@components[type].push(member)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
self
|
62
|
-
end
|
63
|
-
|
64
|
-
# Removes components from the package
|
65
|
-
#
|
66
|
-
# manifest.remove :apex_class, 'SomeClass'
|
67
|
-
def remove(type, members=nil)
|
68
|
-
unless members.nil?
|
69
|
-
members = [members] if members.is_a?(String)
|
70
|
-
members.each do |member|
|
71
|
-
member = member.gsub(/.*\//, '').gsub(/\..*/, '');
|
72
|
-
@components[type].delete(member)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
if @components[type].empty?
|
76
|
-
@components.delete(type)
|
77
|
-
end
|
78
|
-
self
|
79
|
-
end
|
80
|
-
|
81
|
-
# Filters the components based on a list of files
|
82
|
-
#
|
83
|
-
# manifest.only(['classes/SomeClass'])
|
84
|
-
def only(files)
|
85
|
-
components = @components
|
86
|
-
@components = {}
|
87
|
-
files.each do |file|
|
88
|
-
parts = file.split('/').last(2)
|
89
|
-
folder = parts[0]
|
90
|
-
file = parts[1].gsub(/.*\//, '').gsub(/\..*/, '')
|
91
|
-
components.each_key do |type|
|
92
|
-
if Metaforce::Metadata::Types.folder(type) =~ /#{folder}/i
|
93
|
-
unless components[type].index(file).nil?
|
94
|
-
self.add(type, file);
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
self
|
100
|
-
end
|
101
|
-
|
102
|
-
# Returns a string containing a package.xml file
|
48
|
+
# Public: Returns a string containing a package.xml file
|
103
49
|
#
|
104
50
|
# <?xml version="1.0"?>
|
105
51
|
# <Package xmlns="http://soap.sforce.com/2006/04/metadata">
|
@@ -120,13 +66,13 @@ module Metaforce
|
|
120
66
|
# </Package>
|
121
67
|
def to_xml
|
122
68
|
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
123
|
-
xml.Package(
|
124
|
-
|
69
|
+
xml.Package('xmlns' => 'http://soap.sforce.com/2006/04/metadata') {
|
70
|
+
self.each do |key, members|
|
125
71
|
xml.types {
|
126
72
|
members.each do |member|
|
127
73
|
xml.members member
|
128
74
|
end
|
129
|
-
xml.name
|
75
|
+
xml.name key.to_s.camelize
|
130
76
|
}
|
131
77
|
end
|
132
78
|
xml.version Metaforce.configuration.api_version
|
@@ -135,43 +81,22 @@ module Metaforce
|
|
135
81
|
xml_builder.to_xml
|
136
82
|
end
|
137
83
|
|
138
|
-
#
|
139
|
-
#
|
140
|
-
# {
|
141
|
-
# :apex_class => [
|
142
|
-
# "TestController",
|
143
|
-
# "TestClass"
|
144
|
-
# ],
|
145
|
-
# :apex_component => [
|
146
|
-
# "SiteLogin"
|
147
|
-
# ]
|
148
|
-
# }
|
149
|
-
def to_hash
|
150
|
-
@components
|
151
|
-
end
|
152
|
-
|
153
|
-
# Used internall my Metaforce::Metadata::Client
|
84
|
+
# Public: Converts the manifest into a format that can be used by the
|
85
|
+
# metadata api.
|
154
86
|
def to_package
|
155
|
-
|
156
|
-
|
157
|
-
name = Metaforce::Metadata::Types.name(type)
|
158
|
-
components.push :members => members, :name => name
|
87
|
+
self.map do |type, members|
|
88
|
+
{ :members => members, :name => type.to_s.camelize }
|
159
89
|
end
|
160
|
-
components
|
161
90
|
end
|
162
91
|
|
163
|
-
# Parses a package.xml file
|
92
|
+
# Public: Parses a package.xml file
|
164
93
|
def parse(file)
|
165
94
|
document = Nokogiri::XML(file).remove_namespaces!
|
166
95
|
document.xpath('//types').each do |type|
|
167
96
|
name = type.xpath('name').first.content
|
168
|
-
key =
|
97
|
+
key = name.underscore.to_sym
|
169
98
|
type.xpath('members').each do |member|
|
170
|
-
|
171
|
-
@components[key].push(member.content)
|
172
|
-
else
|
173
|
-
@components[key] = [member.content]
|
174
|
-
end
|
99
|
+
self[key] << member.content
|
175
100
|
end
|
176
101
|
end
|
177
102
|
self
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Metaforce
|
2
|
+
module Metadata
|
3
|
+
class Client
|
4
|
+
module CRUD
|
5
|
+
|
6
|
+
# Public: Create metadata
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# client._create(:apex_page, :full_name => 'TestPage', label: 'Test page', :content => '<apex:page>foobar</apex:page>')
|
11
|
+
def _create(type, metadata={})
|
12
|
+
type = type.to_s.camelize
|
13
|
+
request :create do |soap|
|
14
|
+
soap.body = {
|
15
|
+
:metadata => prepare(metadata)
|
16
|
+
}.merge(attributes!(type))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Delete metadata
|
21
|
+
#
|
22
|
+
# Examples
|
23
|
+
#
|
24
|
+
# client._delete(:apex_component, 'Component')
|
25
|
+
def _delete(type, *args)
|
26
|
+
type = type.to_s.camelize
|
27
|
+
metadata = args.map { |full_name| {:full_name => full_name} }
|
28
|
+
request :delete do |soap|
|
29
|
+
soap.body = {
|
30
|
+
:metadata => metadata
|
31
|
+
}.merge(attributes!(type))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: Update metadata
|
36
|
+
#
|
37
|
+
# Examples
|
38
|
+
#
|
39
|
+
# client._update(:apex_page, 'OldPage', :full_name => 'NewPage')
|
40
|
+
def _update(type, current_name, metadata={})
|
41
|
+
type = type.to_s.camelize
|
42
|
+
request :update do |soap|
|
43
|
+
soap.body = {
|
44
|
+
:metadata => {
|
45
|
+
:current_name => current_name,
|
46
|
+
:metadata => prepare(metadata),
|
47
|
+
:attributes! => { :metadata => { 'xsi:type' => "ins0:#{type}" } }
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def create(*args)
|
54
|
+
Job::CRUD.new(self, :_create, args)
|
55
|
+
end
|
56
|
+
|
57
|
+
def update(*args)
|
58
|
+
Job::CRUD.new(self, :_update, args)
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete(*args)
|
62
|
+
Job::CRUD.new(self, :_delete, args)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def attributes!(type)
|
68
|
+
{:attributes! => { 'ins0:metadata' => { 'xsi:type' => "ins0:#{type}" } }}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Internal: Prepare metadata by base64 encoding any content keys.
|
72
|
+
def prepare(metadata)
|
73
|
+
metadata = [metadata] unless metadata.is_a? Array
|
74
|
+
metadata.each { |m| encode_content(m) }
|
75
|
+
metadata
|
76
|
+
end
|
77
|
+
|
78
|
+
# Internal: Base64 encodes any :content keys.
|
79
|
+
def encode_content(metadata)
|
80
|
+
metadata[:content] = Base64.encode64(metadata[:content]) if metadata.has_key?(:content)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Metaforce
|
2
|
+
module Metadata
|
3
|
+
class Client
|
4
|
+
module File
|
5
|
+
|
6
|
+
# Public: Specify an array of component types to list.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# # Get a list of apex classes on the server and output the names of each
|
11
|
+
# client.list_metadata('ApexClass').collect { |t| t.full_name }
|
12
|
+
# #=> ["al__SObjectPaginatorListenerForTesting", "al__IndexOutOfBoundsException", ... ]
|
13
|
+
#
|
14
|
+
# # Get a list of apex components and apex classes
|
15
|
+
# client.list_metadata('CustomObject', 'ApexComponent')
|
16
|
+
# #=> ["ContractContactRole", "Solution", "Invoice_Statements__c", ... ]
|
17
|
+
def list_metadata(*args)
|
18
|
+
queries = args.map(&:to_s).map(&:camelize).map { |t| {:type => t} }
|
19
|
+
request :list_metadata do |soap|
|
20
|
+
soap.body = { :queries => queries }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Describe the organization's metadata.
|
25
|
+
#
|
26
|
+
# version - API version (default: latest).
|
27
|
+
#
|
28
|
+
# Examples
|
29
|
+
#
|
30
|
+
# # List the names of all metadata types
|
31
|
+
# client.describe.metadata_objects.collect { |t| t.xml_name }
|
32
|
+
# #=> ["CustomLabels", "StaticResource", "Scontrol", "ApexComponent", ... ]
|
33
|
+
def describe(version=nil)
|
34
|
+
request :describe_metadata do |soap|
|
35
|
+
soap.body = { :api_version => version } unless version.nil?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Checks the status of an async result.
|
40
|
+
#
|
41
|
+
# ids - A list of ids to check.
|
42
|
+
# type - either :deploy or :retrieve
|
43
|
+
#
|
44
|
+
# Examples
|
45
|
+
#
|
46
|
+
# client.status('04sU0000000Wx6KIAS')
|
47
|
+
# #=> {:done=>true, :id=>"04sU0000000Wx6KIAS", :state=>"Completed", ...}
|
48
|
+
def status(ids, type=nil)
|
49
|
+
method = :check_status
|
50
|
+
method = :"check_#{type}_status" if type
|
51
|
+
ids = [ids] unless ids.respond_to?(:each)
|
52
|
+
request method do |soap|
|
53
|
+
soap.body = { :ids => ids }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Public: Deploy code to Salesforce.
|
58
|
+
#
|
59
|
+
# zip_file - The base64 encoded contents of the zip file.
|
60
|
+
# options - Hash of DeployOptions.
|
61
|
+
#
|
62
|
+
# Returns the AsyncResult
|
63
|
+
def _deploy(zip_file, options={})
|
64
|
+
request :deploy do |soap|
|
65
|
+
soap.body = { :zip_file => zip_file, :deploy_options => options }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: Retrieve code from Salesforce.
|
70
|
+
#
|
71
|
+
# Returns the AsyncResult
|
72
|
+
def _retrieve(options={})
|
73
|
+
request :retrieve do |soap|
|
74
|
+
soap.body = { :retrieve_request => options }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Deploy code to Salesforce.
|
79
|
+
#
|
80
|
+
# path - A path to a zip file, or a directory to deploy.
|
81
|
+
# options - Deploy options.
|
82
|
+
#
|
83
|
+
# Examples
|
84
|
+
#
|
85
|
+
# client.deploy(File.expand_path('./src'))
|
86
|
+
def deploy(path, options={})
|
87
|
+
Job::Deploy.new(self, path, options)
|
88
|
+
end
|
89
|
+
|
90
|
+
def retrieve(options={})
|
91
|
+
Job::Retrieve.new(self, options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Public: Retrieves files specified in the manifest file (A package.xml
|
95
|
+
# file).
|
96
|
+
def retrieve_unpackaged(manifest, options={})
|
97
|
+
package = if manifest.is_a?(Metaforce::Manifest)
|
98
|
+
manifest
|
99
|
+
elsif manifest.is_a?(String)
|
100
|
+
Metaforce::Manifest.new(File.open(manifest).read)
|
101
|
+
end
|
102
|
+
options = {
|
103
|
+
:api_version => Metaforce.configuration.api_version,
|
104
|
+
:single_package => true,
|
105
|
+
:unpackaged => { :types => package.to_package }
|
106
|
+
}.merge(options)
|
107
|
+
retrieve(options)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|