SFDO-API 0.1.9 → 1.0.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.
- 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
|