SFDO-API 0.1.9 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -9
- data/SFDO-API.gemspec +0 -1
- data/lib/SFDO/API.rb +111 -47
- data/lib/SFDO/API/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad083f17c296ab1f98d9ccefeea3b04b594daa98
|
4
|
+
data.tar.gz: 465ec5467fcb682148573f07d531b2f57800d8d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d81c512b1ee4c35ec0b146da5cd1b565d3edbcb253cb47ac92c302036979346de9116f65e00f8157e1842943d7e8b1bc0259d6f12abf9719ad9223d1e8db08f8
|
7
|
+
data.tar.gz: 30225adca39ab25f808db5e74f6605025997aef2ca8ad341bf71d63df0bbdd1ea3fb0068188c45cf02f39cd28ec35e3f50f7fa96bd2e6bfbd09bde9673016729
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SFDO::API
|
2
2
|
|
3
|
-
SFDO-API is a convenient way to use the Salesforce API to manipulate objects in a target org. SFDO-API was intended orginally to
|
3
|
+
SFDO-API is a convenient way to use the Salesforce API to manipulate objects and fields on objects in a target org. SFDO-API was intended orginally to
|
4
4
|
facilitate the sharing of common API calls across multiple source repositories, but the project is evolving to provide powerful
|
5
5
|
tools for handiling SF objects, like support for managed and unmanaged namespaces, multiple namespaces in an org, etc.
|
6
6
|
|
@@ -58,6 +58,13 @@ addressing "npsp__General_Accounting_Unit__c" use plain "General_Accounting_Unit
|
|
58
58
|
gaus = select_api 'select Id from General_Accounting_Unit'
|
59
59
|
```
|
60
60
|
|
61
|
+
Likewise, when addressing custom fields, leave off any namespace values for the fields involved
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
#@relationshiop_id = create 'Relationship', npe4__Contact__c: contact, npe4__RelatedContact__c: related_contact
|
65
|
+
@relationshiop_id = create 'Relationship', Contact: contact, RelatedContact: related_contact
|
66
|
+
```
|
67
|
+
|
61
68
|
To delete a single instance of an object for which you have the Id value
|
62
69
|
|
63
70
|
```ruby
|
@@ -111,7 +118,17 @@ puts gaus.inspect
|
|
111
118
|
end
|
112
119
|
```
|
113
120
|
|
114
|
-
|
121
|
+
Likewise when using custom fields on any object, do not use any namespace value at the front of the object name, and leave off any
|
122
|
+
custom trailer values like "__c" or "__r, SFDO-API handles that for you. Instead of addressing
|
123
|
+
"npsp__General_Accounting_Unit__c" use plain "General_Accounting_Unit" instead, and instead of the field
|
124
|
+
"npe01__Account_Processor__c" use just "Account_Processor"
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
#@contact_id = create 'Contact', LastName: contact_name, MailingCity: 'hhmailingcity', npo02__Household__c: @hh_obj_id
|
128
|
+
@contact_id = create 'Contact', LastName: contact_name, MailingCity: 'hhmailingcity', Household: @hh_obj_id
|
129
|
+
```
|
130
|
+
|
131
|
+
### SELECT and UPDATE actions with custom objects and custom fields
|
115
132
|
|
116
133
|
Use the select_api() and update_api() methods without namespaces or trailing characters.
|
117
134
|
|
@@ -123,15 +140,13 @@ to change, then call update_api() with the altered version of the restforce obje
|
|
123
140
|
api_client do
|
124
141
|
acc_id = select_api 'select Id from Contacts_And_Orgs_Settings'
|
125
142
|
acc = acc_id.first
|
126
|
-
acc.npe01__Account_Processor__c = to_value
|
143
|
+
#acc.npe01__Account_Processor__c = to_value
|
144
|
+
acc.Account_Processor = to_value
|
127
145
|
update_api(acc)
|
128
146
|
end
|
129
147
|
end
|
130
148
|
```
|
131
149
|
|
132
|
-
Note that at this time, *fields* on objects with custom namespaces are not discovered automatically at runtime.
|
133
|
-
See TODO section below:
|
134
|
-
|
135
150
|
### Using objects where local override changes required fields
|
136
151
|
|
137
152
|
Note that ISVs may override required fields on standard Salesforce objects, and these may be needed for SFDO-API to work properly
|
@@ -146,9 +161,9 @@ Note that ISVs may override required fields on standard Salesforce objects, and
|
|
146
161
|
|
147
162
|
### TODO
|
148
163
|
|
149
|
-
|
150
|
-
|
151
|
-
|
164
|
+
At this time SFDO-API does not accept custom fields that are properly named. For example if a field is in fact named
|
165
|
+
"npe01__Account_Processor__c" that field must be addressed as "Account_Processor" and will not function properly if the
|
166
|
+
calling code uses the properly namespaced value of the field.
|
152
167
|
|
153
168
|
## Development
|
154
169
|
|
data/SFDO-API.gemspec
CHANGED
data/lib/SFDO/API.rb
CHANGED
@@ -2,20 +2,32 @@ require 'SFDO/API/version'
|
|
2
2
|
require 'pry'
|
3
3
|
|
4
4
|
module SfdoAPI
|
5
|
-
|
6
5
|
def api_client
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
if ENV['SF_ACCESS_TOKEN'] && ENV['SF_INSTANCE_URL']
|
7
|
+
@api_client ||= Restforce.new(oauth_token: ENV['SF_ACCESS_TOKEN'],
|
8
|
+
instance_url: ENV['SF_INSTANCE_URL'],
|
9
|
+
api_version: '32.0')
|
10
|
+
yield
|
11
|
+
else
|
12
|
+
@api_client ||= Restforce.new api_version: '32.0',
|
13
|
+
refresh_token: ENV['SF_REFRESH_TOKEN'],
|
14
|
+
client_id: ENV['SF_CLIENT_KEY'],
|
15
|
+
client_secret: ENV['SF_CLIENT_SECRET']
|
16
|
+
yield
|
17
|
+
end
|
12
18
|
end
|
13
19
|
|
14
20
|
def create(type, obj_hash)
|
21
|
+
true_fields = {}
|
22
|
+
|
15
23
|
type = true_object_name(type)
|
24
|
+
obj_mash = Hashie::Mash.new obj_hash
|
25
|
+
|
26
|
+
obj_mash.map { |x, y| true_fields.store(true_field_name(x, type), y) }
|
27
|
+
|
16
28
|
if is_valid_obj_hash?(type, obj_hash, @fields_acceptibly_nil)
|
17
29
|
obj_id = api_client do
|
18
|
-
@api_client.create! type,
|
30
|
+
@api_client.create! type, true_fields
|
19
31
|
end
|
20
32
|
end
|
21
33
|
obj_id
|
@@ -28,26 +40,41 @@ module SfdoAPI
|
|
28
40
|
obj_name = query[/(?<=from )(.*)$/i]
|
29
41
|
end
|
30
42
|
|
43
|
+
# GET TRUE OBJECT NAME BEFORE GETTING TRUE FIELD NAMES
|
31
44
|
real_obj_name = true_object_name(obj_name)
|
32
45
|
|
33
|
-
|
46
|
+
# REMOVE NEWLINES IF ANY
|
47
|
+
query = query.gsub(/\n/, ' ')
|
48
|
+
# REMOVE EXTRA SPACES
|
49
|
+
query = query.gsub(/\s{2,}/, ' ')
|
50
|
+
# GET FIELDS ONLY
|
51
|
+
fields_array = query.split(' from ').first.scan /\w*\s*\s([a-zA-Z0-9_]*)/
|
34
52
|
|
35
|
-
|
36
|
-
|
37
|
-
|
53
|
+
fields_array.each do |field|
|
54
|
+
real_field = true_field_name(field[0], real_obj_name)
|
55
|
+
|
56
|
+
if obj_name != real_obj_name
|
57
|
+
query = query.gsub(/\b#{obj_name}\b/, real_obj_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
if field[0] != real_field
|
61
|
+
query = query.gsub(field[0], real_field)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
results = api_client do
|
66
|
+
@api_client.query query
|
67
|
+
end
|
38
68
|
results
|
39
69
|
end
|
40
70
|
|
41
71
|
def is_valid_obj_hash?(object_name, obj_hash, fields_acceptibly_nil)
|
42
|
-
|
43
|
-
#TODO We do it from here because this is the only place we know about fields on objects
|
44
|
-
required_fields = get_object_describe(object_name).map(&:fieldName)
|
72
|
+
required_fields = get_required_fields_on_object(object_name).map(&:fieldName)
|
45
73
|
valid = true
|
46
74
|
required_fields.each do |f|
|
47
|
-
|
48
75
|
valid = false if (!obj_hash.key? f.to_sym) && (begin
|
49
76
|
!fields_acceptibly_nil[object_name].contains? f
|
50
|
-
puts 'This field must be populated in order to create this object in this environment: ' +
|
77
|
+
puts 'This field must be populated in order to create this object in this environment: ' + f.inspect
|
51
78
|
rescue
|
52
79
|
false
|
53
80
|
end)
|
@@ -55,13 +82,13 @@ module SfdoAPI
|
|
55
82
|
valid
|
56
83
|
end
|
57
84
|
|
58
|
-
def org_describe
|
85
|
+
def org_describe
|
59
86
|
if @org_description.nil? || !@org_description.respond_to?(:contains)
|
60
87
|
@org_description = api_client do
|
61
88
|
@api_client.describe
|
62
89
|
end
|
63
90
|
end
|
64
|
-
|
91
|
+
@org_description
|
65
92
|
end
|
66
93
|
|
67
94
|
def prefix_to_name
|
@@ -71,33 +98,61 @@ module SfdoAPI
|
|
71
98
|
@prefix_to_name.store(z.keyPrefix, z.name)
|
72
99
|
end
|
73
100
|
end
|
74
|
-
|
101
|
+
@prefix_to_name
|
75
102
|
end
|
76
103
|
|
77
|
-
def
|
78
|
-
|
104
|
+
def true_field_name(field, obj)
|
105
|
+
# See if we've done an object describe on the object
|
106
|
+
# If so, return the fields for the object
|
107
|
+
# Otherwise do an object describe, save the object description and return the fields with their real names
|
108
|
+
@full_describe = {} if @full_describe.nil?
|
109
|
+
|
110
|
+
if @full_describe[obj].nil?
|
111
|
+
object_description = get_object_describe(obj)
|
112
|
+
fields = object_description.map do |f|
|
113
|
+
|
114
|
+
# MANAGED CODE
|
115
|
+
if f.fieldName.match /.*__.*__.*/
|
116
|
+
substituted = f.fieldName.gsub(/\A.*?__/, '').gsub(/__c\z/, '')
|
117
|
+
else
|
118
|
+
# UNMANAGED CODE
|
119
|
+
substituted = f.fieldName.gsub(/__c\z/, '')
|
120
|
+
end
|
121
|
+
|
122
|
+
{ substituted => f.fieldName }
|
123
|
+
end
|
124
|
+
@full_describe[obj] = fields.reduce({}, :merge)
|
125
|
+
end
|
126
|
+
|
127
|
+
# RETURN THE REAL NAME FROM OUR HASH OF INPUT TO REAL NAMES
|
128
|
+
@full_describe[obj][field]
|
129
|
+
end
|
130
|
+
|
131
|
+
def true_object_name(handle) # either an ID or a string name
|
132
|
+
# handle = (handle.end_with?("__c") || handle.end_with?("__r")) ? handle[0...-3] : handle
|
133
|
+
handle.end_with?('__c', '__r') ? handle[0...-3] : handle
|
79
134
|
from_id = prefix_to_name[handle[0..2]]
|
80
135
|
from_name = obj_names_without_custom_additions[handle]
|
81
136
|
if !from_name.nil? || !from_id.nil?
|
82
137
|
return from_name if from_id.nil?
|
83
138
|
return from_id if from_name.nil?
|
84
139
|
end
|
85
|
-
|
140
|
+
'Unable to find object. Be sure to call SFDO-API without preceding namespace or following __c or __r'
|
86
141
|
end
|
87
142
|
|
88
143
|
def obj_names_without_custom_additions
|
89
144
|
if @obj_names_without_custom_additions.nil? || !@obj_names_without_custom_additions.respond_to?(:contains)
|
90
145
|
@obj_names_without_custom_additions = {}
|
91
146
|
org_describe.each do |z|
|
92
|
-
tmp_var = z.name.split
|
93
|
-
save =
|
147
|
+
tmp_var = z.name.split '__'
|
148
|
+
save = ''
|
94
149
|
case tmp_var.size
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
150
|
+
when 2
|
151
|
+
save = tmp_var.first
|
152
|
+
when 3
|
153
|
+
save = tmp_var[1]
|
154
|
+
else
|
155
|
+
save = tmp_var.last
|
101
156
|
end
|
102
157
|
@obj_names_without_custom_additions.store(save, z.name)
|
103
158
|
end
|
@@ -105,23 +160,33 @@ module SfdoAPI
|
|
105
160
|
@obj_names_without_custom_additions
|
106
161
|
end
|
107
162
|
|
163
|
+
def get_required_fields_on_object(obj_name)
|
164
|
+
@object_fields = {} if @object_fields.nil?
|
165
|
+
|
166
|
+
if @object_fields[obj_name].nil?
|
167
|
+
@object_fields[obj_name] = get_object_describe(obj_name)
|
168
|
+
end
|
169
|
+
|
170
|
+
@object_fields[obj_name].select(&:required)
|
171
|
+
end
|
172
|
+
|
108
173
|
def get_object_describe(object_name)
|
109
174
|
api_client do
|
110
175
|
@description = @api_client.get("/services/data/v35.0/sobjects/#{object_name}/describe")
|
111
|
-
|
112
176
|
describeobject = Hashie::Mash.new(@description.body)
|
113
177
|
|
114
178
|
required = describeobject.fields.map do |x|
|
115
179
|
Hashie::Mash.new(
|
116
|
-
|
117
|
-
|
118
|
-
|
180
|
+
fieldName: x.name,
|
181
|
+
required: (!x.nillable && !x.defaultedOnCreate),
|
182
|
+
default: x.defaultValue
|
183
|
+
)
|
119
184
|
end
|
120
|
-
required
|
185
|
+
return required
|
121
186
|
end
|
122
187
|
end
|
123
188
|
|
124
|
-
def delete(
|
189
|
+
def delete(obj_id)
|
125
190
|
delete_by_id(obj_id)
|
126
191
|
end
|
127
192
|
|
@@ -148,19 +213,18 @@ module SfdoAPI
|
|
148
213
|
obj_type = breakdown.last.capitalize
|
149
214
|
|
150
215
|
case method_called.to_s
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
216
|
+
when /^delete_all_/
|
217
|
+
delete_all *args
|
218
|
+
when /^delete_one/
|
219
|
+
delete obj_type, *args
|
220
|
+
when /^create_/
|
221
|
+
create obj_type, *args
|
222
|
+
when /^select_api/
|
223
|
+
select_api *args
|
224
|
+
else
|
225
|
+
super.method_missing
|
161
226
|
end
|
162
227
|
end
|
163
|
-
|
164
228
|
end
|
165
229
|
# INCLUDE HERE RATHER THAN IN THE PRODUCT-SPECIFIC CODE USING THIS GEM
|
166
230
|
include SfdoAPI
|
data/lib/SFDO/API/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: SFDO-API
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris McMahon
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-02-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|