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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c852c0ed4fb33d2702921867b16bfd991e8eaf28
4
- data.tar.gz: ee15a382213300a9d96f78c91987048e208644bb
3
+ metadata.gz: ad083f17c296ab1f98d9ccefeea3b04b594daa98
4
+ data.tar.gz: 465ec5467fcb682148573f07d531b2f57800d8d3
5
5
  SHA512:
6
- metadata.gz: bbae2077803087383d951e693b252da0196c4a62d1624266c466812a067bdc77da025419a2d9fae2857b3f8c899e9ad5dccbfa8f99f667b711d9efe8484e8ded
7
- data.tar.gz: e4cef1d73bf90664b90106687008a824ad3225332d993431d3237c25a8eb70dc32e6e6cde5997142ceb4863910de6b8dacb1abdf71b34851ecfc4194816255a4
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
- ### SELECT and UPDATE actions with custom objects
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
- Fields on namespaced object may be namespaced themselves. SFDO-API does not handle this case yet.
150
-
151
- Custom fields on standard Salesforce objects may have namespaces. SFDO-API does not handle such fields yet.
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
 
@@ -3,7 +3,6 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'SFDO/API/version'
5
5
 
6
-
7
6
  Gem::Specification.new do |spec|
8
7
  spec.name = "SFDO-API"
9
8
  spec.version = SFDO::API::VERSION
@@ -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
- @api_client ||= Restforce.new api_version: '32.0',
8
- refresh_token: ENV['SF_REFRESH_TOKEN'],
9
- client_id: ENV['SF_CLIENT_KEY'],
10
- client_secret: ENV['SF_CLIENT_SECRET']
11
- yield
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, obj_hash
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
- query = query.gsub(obj_name, real_obj_name)
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
- results = api_client do
36
- @api_client.query query
37
- end
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
- #TODO Take incoming field names;parse out namespace/__c values; get true namespace for fields also
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: ' + f.inspect
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
- return @org_description
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
- return @prefix_to_name
101
+ @prefix_to_name
75
102
  end
76
103
 
77
- def true_object_name(handle) #either an ID or a string name
78
- handle = (handle.end_with?("__c") || handle.end_with?("__r")) ? handle[0...-3] : handle
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
- return 'Unable to find object. Be sure to call SFDO-API without preceding namespace or following __c or __r'
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
- when 2
96
- save = tmp_var.first
97
- when 3
98
- save = tmp_var[1]
99
- else
100
- save = tmp_var.last
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
- fieldName: x.name,
117
- required: (!x.nillable && !x.defaultedOnCreate),
118
- default: x.defaultValue)
180
+ fieldName: x.name,
181
+ required: (!x.nillable && !x.defaultedOnCreate),
182
+ default: x.defaultValue
183
+ )
119
184
  end
120
- required.select(&:required)
185
+ return required
121
186
  end
122
187
  end
123
188
 
124
- def delete(type, obj_id)
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
- when /^delete_all_/
152
- delete_all *args
153
- when /^delete_one/
154
- delete obj_type, *args
155
- when /^create_/
156
- create obj_type, *args
157
- when /^select_api/
158
- select_api *args
159
- else
160
- super.method_missing
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
@@ -1,5 +1,5 @@
1
1
  module SFDO
2
2
  module API
3
- VERSION = "0.1.9"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
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.1.9
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: 2016-12-01 00:00:00.000000000 Z
12
+ date: 2017-02-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler