logstash-input-salesforce 3.2.1 → 3.3.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.
@@ -0,0 +1,123 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://login.salesforce.com/services/oauth2/token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=password&client_id=xxxx&client_secret=xxxx&username=xxxx&password=xxxx
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.9.1
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept:
15
+ - '*/*'
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Date:
22
+ - Thu, 27 Aug 2015 23:57:42 GMT
23
+ Set-Cookie:
24
+ - BrowserId=xxxx;Path=/;Domain=.salesforce.com;Expires=Mon, 26-Oct-2015 23:57:42 GMT
25
+ Expires:
26
+ - Thu, 01 Jan 1970 00:00:00 GMT
27
+ Pragma:
28
+ - no-cache
29
+ Cache-Control:
30
+ - no-cache, no-store
31
+ Content-Type:
32
+ - application/json;charset=UTF-8
33
+ Transfer-Encoding:
34
+ - chunked
35
+ body:
36
+ encoding: US-ASCII
37
+ string: '{"id":"https://login.salesforce.com/id/xxxx/xxxx","issued_at":"1440719862904","token_type":"Bearer","instance_url":"https://eu2.salesforce.com","signature":"xxxx","access_token":"xxxx"}'
38
+ http_version:
39
+ recorded_at: Thu, 27 Aug 2015 23:57:43 GMT
40
+ - request:
41
+ method: get
42
+ uri: https://eu2.salesforce.com/services/data/v26.0/sobjects/Lead/describe
43
+ body:
44
+ encoding: US-ASCII
45
+ string: ''
46
+ headers:
47
+ User-Agent:
48
+ - Faraday v0.9.1
49
+ Authorization:
50
+ - OAuth xxxx
51
+ Accept-Encoding:
52
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
53
+ Accept:
54
+ - '*/*'
55
+ response:
56
+ status:
57
+ code: 200
58
+ message: OK
59
+ headers:
60
+ Date:
61
+ - Thu, 27 Aug 2015 23:57:44 GMT
62
+ Set-Cookie:
63
+ - BrowserId=xxxx;Path=/;Domain=.salesforce.com;Expires=Mon, 26-Oct-2015 23:57:44 GMT
64
+ Expires:
65
+ - Thu, 01 Jan 1970 00:00:00 GMT
66
+ Sforce-Limit-Info:
67
+ - api-usage=211068/451000
68
+ Org.eclipse.jetty.server.include.etag:
69
+ - 5fb54cb6
70
+ Last-Modified:
71
+ - Thu, 27 Aug 2015 22:36:55 GMT
72
+ Content-Type:
73
+ - application/json;charset=UTF-8
74
+ Etag:
75
+ - 5fb54cb-gzip"
76
+ Transfer-Encoding:
77
+ - chunked
78
+ body:
79
+ encoding: UTF-8
80
+ string: '{"activateable":false,"childRelationships":[],"createable":true,"custom":false,"customSetting":false,"deletable":true,"deprecatedAndHidden":false,"feedEnabled":true,"fields":[{"autoNumber":false,"byteLength":18,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"controllerName":null,"createable":false,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":true,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"externalId":false,"filterable":true,"groupable":true,"htmlFormatted":false,"idLookup":true,"inlineHelpText":null,"label":"Lead
81
+ ID","length":18,"name":"Id","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[],"precision":0,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"soapType":"tns:ID","sortable":true,"type":"id","unique":false,"updateable":false,"writeRequiresMasterRead":false},{"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"controllerName":null,"createable":false,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":true,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"externalId":false,"filterable":true,"groupable":true,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Deleted","length":0,"name":"IsDeleted","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[],"precision":0,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"soapType":"xsd:boolean","sortable":true,"type":"boolean","unique":false,"updateable":false,"writeRequiresMasterRead":false},{"autoNumber":false,"byteLength":240,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"externalId":false,"filterable":true,"groupable":true,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Last
82
+ Name","length":80,"name":"LastName","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[],"precision":0,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"soapType":"xsd:string","sortable":true,"type":"string","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"autoNumber":false,"byteLength":120,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"externalId":false,"filterable":true,"groupable":true,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"First
83
+ Name","length":40,"name":"FirstName","nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"precision":0,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"soapType":"xsd:string","sortable":true,"type":"string","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"autoNumber":false,"byteLength":120,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"externalId":false,"filterable":true,"groupable":true,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Salutation","length":40,"name":"Salutation","nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[{"active":true,"defaultValue":false,"label":"Mr.","validFor":null,"value":"Mr."},{"active":true,"defaultValue":false,"label":"Ms.","validFor":null,"value":"Ms."},{"active":true,"defaultValue":false,"label":"Mrs.","validFor":null,"value":"Mrs."},{"active":true,"defaultValue":false,"label":"Dr.","validFor":null,"value":"Dr."},{"active":true,"defaultValue":false,"label":"Prof.","validFor":null,"value":"Prof."}],"precision":0,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"soapType":"xsd:string","sortable":true,"type":"picklist","unique":false,"updateable":true,"writeRequiresMasterRead":false}],"keyPrefix":"00Q","label":"Lead","labelPlural":"Leads","layoutable":true,"listviewable":null,"lookupLayoutable":null,"mergeable":true,"name":"Lead","queryable":true,"recordTypeInfos":[{"available":true,"defaultRecordTypeMapping":true,"name":"Marketing","recordTypeId":"xxxx"},{"available":true,"defaultRecordTypeMapping":false,"name":"Partner
84
+ Deal","recordTypeId":"xxxx"},{"available":true,"defaultRecordTypeMapping":false,"name":"Sales","recordTypeId":"xxxx"},{"available":true,"defaultRecordTypeMapping":false,"name":"Master","recordTypeId":"xxxx"}],"replicateable":true,"retrieveable":true,"searchLayoutable":null,"searchable":true,"triggerable":true,"undeletable":true,"updateable":true,"urls":{"uiEditTemplate":"https://xxx.salesforce.com/{ID}/e","sobject":"/services/data/v26.0/sobjects/Lead","uiDetailTemplate":"https://xxx.salesforce.com/{ID}","describe":"/services/data/v26.0/sobjects/Lead/describe","rowTemplate":"/services/data/v26.0/sobjects/Lead/{ID}","uiNewRecord":"https://xxx.salesforce.com/00Q/e"}}'
85
+ http_version:
86
+ recorded_at: Thu, 27 Aug 2015 23:57:44 GMT
87
+ - request:
88
+ method: get
89
+ uri: https://eu2.salesforce.com/services/data/v26.0/query?q=SELECT%20Id,IsDeleted,LastName,FirstName,Salutation,LastModifiedDate%20FROM%20Lead%20WHERE%20LastModifiedDate%20%3E%202025-05-07T14:32:17Z%20ORDER%20BY%20LastModifiedDate%20ASC
90
+ body:
91
+ encoding: US-ASCII
92
+ string: ''
93
+ headers:
94
+ User-Agent:
95
+ - Faraday v0.9.1
96
+ Authorization:
97
+ - OAuth xxx
98
+ Accept-Encoding:
99
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
100
+ Accept:
101
+ - '*/*'
102
+ response:
103
+ status:
104
+ code: 200
105
+ message: OK
106
+ headers:
107
+ Date:
108
+ - Thu, 27 Aug 2015 23:57:45 GMT
109
+ Set-Cookie:
110
+ - BrowserId=xxxx;Path=/;Domain=.salesforce.com;Expires=Mon, 26-Oct-2015 23:57:45 GMT
111
+ Expires:
112
+ - Thu, 01 Jan 1970 00:00:00 GMT
113
+ Sforce-Limit-Info:
114
+ - api-usage=211063/451000
115
+ Content-Type:
116
+ - application/json;charset=UTF-8
117
+ Transfer-Encoding:
118
+ - chunked
119
+ body:
120
+ encoding: UTF-8
121
+ string: '{"totalSize":3,"done":true,"records":[{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Katz","FirstName":"Aaron","Salutation":"Mr."},{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Grand","FirstName":"Adrien","Salutation":"Dr."},{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Hardy","FirstName":"Alan","Salutation":"Overlord"}]}'
122
+ http_version:
123
+ recorded_at: Thu, 27 Aug 2015 23:57:45 GMT
@@ -84,7 +84,7 @@ RSpec.describe LogStash::Inputs::Salesforce do
84
84
  ["Salutation", "picklist"]] }
85
85
  subject { input }
86
86
  it "loads the Lead object fields" do
87
- VCR.use_cassette("describe_lead_object",:decode_compressed_response => true) do
87
+ VCR.use_cassette("describe lead object",:decode_compressed_response => true) do
88
88
  subject.register
89
89
  expect(subject.instance_variable_get(:@sfdc_field_types)).to match_array(expected_types_result)
90
90
  expect(subject.instance_variable_get(:@sfdc_fields)).to match_array(expected_fields_result)
@@ -107,7 +107,7 @@ RSpec.describe LogStash::Inputs::Salesforce do
107
107
  subject { input }
108
108
  let(:queue) { [] }
109
109
  it "loads some lead records" do
110
- VCR.use_cassette("load some lead objects",:decode_compressed_response => true) do
110
+ VCR.use_cassette("load some lead objects", :decode_compressed_response => true) do
111
111
  subject.register
112
112
  subject.run(queue)
113
113
  expect(queue.length).to eq(3)
@@ -155,7 +155,7 @@ RSpec.describe LogStash::Inputs::Salesforce do
155
155
  ["Salutation", "picklist"]] }
156
156
  subject { input }
157
157
  it "logs into sfdc instance url" do
158
- VCR.use_cassette("login_into_mydomain", :decode_compressed_response => true) do
158
+ VCR.use_cassette("login into mydomain", :decode_compressed_response => true) do
159
159
  subject.register
160
160
  expect(subject.instance_variable_get(:@sfdc_field_types)).to match_array(expected_types_result)
161
161
  expect(subject.instance_variable_get(:@sfdc_fields)).to match_array(expected_fields_result)
@@ -211,11 +211,271 @@ RSpec.describe LogStash::Inputs::Salesforce do
211
211
  let(:input) { LogStash::Inputs::Salesforce.new(options) }
212
212
  subject { input }
213
213
  it "should use the Tooling Api Query resource path" do
214
- VCR.use_cassette("describe_apex_test_run_result_object",:decode_compressed_response => true) do
214
+ VCR.use_cassette("describe apex test run result object", :decode_compressed_response => true) do
215
215
  subject.register
216
216
  expect(subject.send(:client).send(:api_path, "query")).to eq('/services/data/v52.0/tooling/query')
217
217
  end
218
218
  end
219
219
  end
220
+
221
+ context "run continuously at interval" do
222
+ VCR.configure do |config|
223
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
224
+ config.hook_into :webmock
225
+ config.before_record do |i|
226
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
227
+ # required because sfdc doesn't send back the content encoding and it
228
+ # confuses the yaml parser
229
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
230
+ i.response.body = json_body.to_json
231
+ i.response.update_content_length_header
232
+ end
233
+ end
234
+ end
235
+ let(:options) do
236
+ {
237
+ "client_id" => "",
238
+ "client_secret" => ::LogStash::Util::Password.new("secret-key"),
239
+ "username" => "",
240
+ "password" => ::LogStash::Util::Password.new("secret-password"),
241
+ "security_token" => ::LogStash::Util::Password.new("secret-token"),
242
+ "use_tooling_api" => false,
243
+ "sfdc_object_name" => "Lead",
244
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
245
+ "sfdc_filters" => "Email LIKE '%@elastic.co'",
246
+ "interval" => 3
247
+ }
248
+ end
249
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
250
+ subject { input }
251
+ let(:queue) { [] }
252
+ it "loads some lead records" do
253
+ VCR.use_cassette("load some lead objects twice", :decode_compressed_response => true) do
254
+ subject.register
255
+ # Run a background thread to set the stop flag after 7 seconds, i.e. after the second run of the plugin
256
+ thr = Thread.new {
257
+ sleep(4) # more than 3, less than 6
258
+ subject.do_stop
259
+ }
260
+ subject.run(queue)
261
+ thr.join
262
+ expect(queue.length).to eq(8) # 3 + 5
263
+ end
264
+ end
265
+ end
266
+
267
+ context "run with last modified time from range field file" do
268
+ VCR.configure do |config|
269
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
270
+ config.hook_into :webmock
271
+ config.before_record do |i|
272
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
273
+ # required because sfdc doesn't send back the content encoding and it
274
+ # confuses the yaml parser
275
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
276
+ i.response.body = json_body.to_json
277
+ i.response.update_content_length_header
278
+ end
279
+ end
280
+ end
281
+ let(:options) do
282
+ {
283
+ "client_id" => "",
284
+ "client_secret" => ::LogStash::Util::Password.new("secret-key"),
285
+ "username" => "",
286
+ "password" => ::LogStash::Util::Password.new("secret-password"),
287
+ "security_token" => ::LogStash::Util::Password.new("secret-token"),
288
+ "use_tooling_api" => false,
289
+ "sfdc_object_name" => "Lead",
290
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
291
+ "sfdc_filters" => "Email LIKE '%@elastic.co'",
292
+ "changed_data_filter" => "LastModifiedDate > %{last_tracking_field_value}",
293
+ "tracking_field_value_file" => "last_tracking_field_value.txt"
294
+ }
295
+ end
296
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
297
+ subject { input }
298
+ let(:queue) { [] }
299
+ it "loads some lead records modified after 2025-05-07 14:32:17 UTC" do
300
+ VCR.use_cassette("load some lead objects with lastmodifieddate filter", :decode_compressed_response => true) do
301
+ subject.register
302
+ File.write("last_tracking_field_value.txt", "2025-05-07T14:32:17Z")
303
+ subject.run(queue)
304
+ expect(queue.length).to eq(3)
305
+ end
306
+ end
307
+ end
308
+
309
+ context "run with last modified time from range field file when file doesn't yet exist" do
310
+ VCR.configure do |config|
311
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
312
+ config.hook_into :webmock
313
+ config.before_record do |i|
314
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
315
+ # required because sfdc doesn't send back the content encoding and it
316
+ # confuses the yaml parser
317
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
318
+ i.response.body = json_body.to_json
319
+ i.response.update_content_length_header
320
+ end
321
+ end
322
+ end
323
+ let(:options) do
324
+ {
325
+ "client_id" => "",
326
+ "client_secret" => ::LogStash::Util::Password.new("secret-key"),
327
+ "username" => "",
328
+ "password" => ::LogStash::Util::Password.new("secret-password"),
329
+ "security_token" => ::LogStash::Util::Password.new("secret-token"),
330
+ "use_tooling_api" => false,
331
+ "sfdc_object_name" => "Lead",
332
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
333
+ "sfdc_filters" => "Email LIKE '%@elastic.co'",
334
+ "changed_data_filter" => "LastModifiedDate > %{last_tracking_field_value}",
335
+ "tracking_field_value_file" => "last_tracking_field_value.txt"
336
+ }
337
+ end
338
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
339
+ subject { input }
340
+ let(:queue) { [] }
341
+ it "loads some lead records" do
342
+ VCR.use_cassette("load some lead objects", :decode_compressed_response => true) do
343
+ subject.register
344
+ File.exist?("last_tracking_field_value.txt") && File.delete("last_tracking_field_value.txt")
345
+ subject.run(queue)
346
+ expect(queue.length).to eq(3)
347
+ end
348
+ end
349
+ end
350
+
351
+ context "run with last modified time from range field file with range field" do
352
+ VCR.configure do |config|
353
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
354
+ config.hook_into :webmock
355
+ config.before_record do |i|
356
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
357
+ # required because sfdc doesn't send back the content encoding and it
358
+ # confuses the yaml parser
359
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
360
+ i.response.body = json_body.to_json
361
+ i.response.update_content_length_header
362
+ end
363
+ end
364
+ end
365
+ let(:options) do
366
+ {
367
+ "client_id" => "",
368
+ "client_secret" => ::LogStash::Util::Password.new("secret-key"),
369
+ "username" => "",
370
+ "password" => ::LogStash::Util::Password.new("secret-password"),
371
+ "security_token" => ::LogStash::Util::Password.new("secret-token"),
372
+ "use_tooling_api" => false,
373
+ "sfdc_object_name" => "Lead",
374
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
375
+ "sfdc_filters" => "Email LIKE '%@elastic.co'",
376
+ "tracking_field" => "LastModifiedDate",
377
+ "changed_data_filter" => "LastModifiedDate > %{last_tracking_field_value}",
378
+ "tracking_field_value_file" => "last_tracking_field_value.txt"
379
+ }
380
+ end
381
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
382
+ subject { input }
383
+ let(:queue) { [] }
384
+ it "loads some lead records modified after 2025-05-07 14:32:17 UTC" do
385
+ VCR.use_cassette("load some lead objects with lastmodifieddate filter order by lastmodifieddate", :decode_compressed_response => true) do
386
+ subject.register
387
+ File.write("last_tracking_field_value.txt", "2025-05-07T14:32:17Z")
388
+ subject.run(queue)
389
+ expect(queue.length).to eq(3)
390
+ end
391
+ end
392
+ end
393
+
394
+ context "run with last modified time from range field file with range field and no other filters" do
395
+ VCR.configure do |config|
396
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
397
+ config.hook_into :webmock
398
+ config.before_record do |i|
399
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
400
+ # required because sfdc doesn't send back the content encoding and it
401
+ # confuses the yaml parser
402
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
403
+ i.response.body = json_body.to_json
404
+ i.response.update_content_length_header
405
+ end
406
+ end
407
+ end
408
+ let(:options) do
409
+ {
410
+ "client_id" => "",
411
+ "client_secret" => ::LogStash::Util::Password.new("secret-key"),
412
+ "username" => "",
413
+ "password" => ::LogStash::Util::Password.new("secret-password"),
414
+ "security_token" => ::LogStash::Util::Password.new("secret-token"),
415
+ "use_tooling_api" => false,
416
+ "sfdc_object_name" => "Lead",
417
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
418
+ "tracking_field" => "LastModifiedDate",
419
+ "changed_data_filter" => "LastModifiedDate > %{last_tracking_field_value}",
420
+ "tracking_field_value_file" => "last_tracking_field_value.txt"
421
+ }
422
+ end
423
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
424
+ subject { input }
425
+ let(:queue) { [] }
426
+ it "loads some lead records modified after 2025-05-07 14:32:17 UTC" do
427
+ VCR.use_cassette("load some lead objects with lastmodifieddate filter order by lastmodifieddate no filters", :decode_compressed_response => true) do
428
+ subject.register
429
+ File.write("last_tracking_field_value.txt", "2025-05-07T14:32:17Z")
430
+ subject.run(queue)
431
+ expect(queue.length).to eq(3)
432
+ end
433
+ end
434
+ end
435
+
436
+ context "run with last modified time from range field file when file doesn't yet exist with range field" do
437
+ VCR.configure do |config|
438
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
439
+ config.hook_into :webmock
440
+ config.before_record do |i|
441
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
442
+ # required because sfdc doesn't send back the content encoding and it
443
+ # confuses the yaml parser
444
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
445
+ i.response.body = json_body.to_json
446
+ i.response.update_content_length_header
447
+ end
448
+ end
449
+ end
450
+ let(:options) do
451
+ {
452
+ "client_id" => "",
453
+ "client_secret" => ::LogStash::Util::Password.new("secret-key"),
454
+ "username" => "",
455
+ "password" => ::LogStash::Util::Password.new("secret-password"),
456
+ "security_token" => ::LogStash::Util::Password.new("secret-token"),
457
+ "use_tooling_api" => false,
458
+ "sfdc_object_name" => "Lead",
459
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
460
+ "sfdc_filters" => "Email LIKE '%@elastic.co'",
461
+ "tracking_field" => "LastModifiedDate",
462
+ "changed_data_filter" => "LastModifiedDate > %{last_tracking_field_value}",
463
+ "tracking_field_value_file" => "last_tracking_field_value.txt"
464
+ }
465
+ end
466
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
467
+ subject { input }
468
+ let(:queue) { [] }
469
+ it "loads some lead records" do
470
+ VCR.use_cassette("load some lead objects order by lastmodifieddate", :decode_compressed_response => true) do
471
+ subject.register
472
+ File.exist?("last_tracking_field_value.txt") && File.delete("last_tracking_field_value.txt")
473
+ subject.run(queue)
474
+ expect(queue.length).to eq(3)
475
+ tracking_field_value = File.read("last_tracking_field_value.txt")
476
+ expect(tracking_field_value).to eq("2025-05-07T14:32:17Z")
477
+ end
478
+ end
479
+ end
220
480
  end
221
481
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-salesforce
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Russ Savage
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-05-30 00:00:00.000000000 Z
10
+ date: 2025-05-14 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
13
+ name: logstash-core-plugin-api
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -19,9 +19,8 @@ dependencies:
19
19
  - - "<="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.99'
22
- name: logstash-core-plugin-api
23
- prerelease: false
24
22
  type: :runtime
23
+ prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - ">="
@@ -31,20 +30,21 @@ dependencies:
31
30
  - !ruby/object:Gem::Version
32
31
  version: '2.99'
33
32
  - !ruby/object:Gem::Dependency
33
+ name: logstash-codec-plain
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
38
  version: '0'
39
- name: logstash-codec-plain
40
- prerelease: false
41
39
  type: :runtime
40
+ prerelease: false
42
41
  version_requirements: !ruby/object:Gem::Requirement
43
42
  requirements:
44
43
  - - ">="
45
44
  - !ruby/object:Gem::Version
46
45
  version: '0'
47
46
  - !ruby/object:Gem::Dependency
47
+ name: restforce
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
50
50
  - - ">="
@@ -53,9 +53,8 @@ dependencies:
53
53
  - - "<"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '5.2'
56
- name: restforce
57
- prerelease: false
58
56
  type: :runtime
57
+ prerelease: false
59
58
  version_requirements: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - ">="
@@ -65,70 +64,70 @@ dependencies:
65
64
  - !ruby/object:Gem::Version
66
65
  version: '5.2'
67
66
  - !ruby/object:Gem::Dependency
67
+ name: logstash-devutils
68
68
  requirement: !ruby/object:Gem::Requirement
69
69
  requirements:
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
72
  version: '0'
73
- name: logstash-devutils
74
- prerelease: false
75
73
  type: :development
74
+ prerelease: false
76
75
  version_requirements: !ruby/object:Gem::Requirement
77
76
  requirements:
78
77
  - - ">="
79
78
  - !ruby/object:Gem::Version
80
79
  version: '0'
81
80
  - !ruby/object:Gem::Dependency
81
+ name: vcr
82
82
  requirement: !ruby/object:Gem::Requirement
83
83
  requirements:
84
84
  - - ">="
85
85
  - !ruby/object:Gem::Version
86
86
  version: '0'
87
- name: vcr
88
- prerelease: false
89
87
  type: :development
88
+ prerelease: false
90
89
  version_requirements: !ruby/object:Gem::Requirement
91
90
  requirements:
92
91
  - - ">="
93
92
  - !ruby/object:Gem::Version
94
93
  version: '0'
95
94
  - !ruby/object:Gem::Dependency
95
+ name: webmock
96
96
  requirement: !ruby/object:Gem::Requirement
97
97
  requirements:
98
98
  - - ">="
99
99
  - !ruby/object:Gem::Version
100
100
  version: '0'
101
- name: webmock
102
- prerelease: false
103
101
  type: :development
102
+ prerelease: false
104
103
  version_requirements: !ruby/object:Gem::Requirement
105
104
  requirements:
106
105
  - - ">="
107
106
  - !ruby/object:Gem::Version
108
107
  version: '0'
109
108
  - !ruby/object:Gem::Dependency
109
+ name: json
110
110
  requirement: !ruby/object:Gem::Requirement
111
111
  requirements:
112
112
  - - ">="
113
113
  - !ruby/object:Gem::Version
114
114
  version: '0'
115
- name: json
116
- prerelease: false
117
115
  type: :development
116
+ prerelease: false
118
117
  version_requirements: !ruby/object:Gem::Requirement
119
118
  requirements:
120
119
  - - ">="
121
120
  - !ruby/object:Gem::Version
122
121
  version: '0'
123
122
  - !ruby/object:Gem::Dependency
123
+ name: public_suffix
124
124
  requirement: !ruby/object:Gem::Requirement
125
125
  requirements:
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
128
  version: '1.4'
129
- name: public_suffix
130
- prerelease: false
131
129
  type: :development
130
+ prerelease: false
132
131
  version_requirements: !ruby/object:Gem::Requirement
133
132
  requirements:
134
133
  - - ">="
@@ -143,7 +142,6 @@ extensions: []
143
142
  extra_rdoc_files: []
144
143
  files:
145
144
  - CHANGELOG.md
146
- - DEVELOPER.md
147
145
  - Gemfile
148
146
  - LICENSE
149
147
  - NOTICE.TXT
@@ -154,16 +152,20 @@ files:
154
152
  - spec/fixtures/vcr_cassettes/describe_apex_test_run_result_object.yml
155
153
  - spec/fixtures/vcr_cassettes/describe_lead_object.yml
156
154
  - spec/fixtures/vcr_cassettes/load_some_lead_objects.yml
155
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_order_by_lastmodifieddate.yml
156
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_twice.yml
157
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter.yml
158
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate.yml
159
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate_no_filters.yml
157
160
  - spec/fixtures/vcr_cassettes/login_into_mydomain.yml
158
161
  - spec/inputs/salesforce_spec.rb
159
162
  - spec/spec_helper.rb
160
163
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
161
164
  licenses:
162
- - Apache License (2.0)
165
+ - apache-2.0
163
166
  metadata:
164
167
  logstash_plugin: 'true'
165
168
  logstash_group: input
166
- post_install_message:
167
169
  rdoc_options: []
168
170
  require_paths:
169
171
  - lib
@@ -178,14 +180,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
180
  - !ruby/object:Gem::Version
179
181
  version: '0'
180
182
  requirements: []
181
- rubygems_version: 3.2.33
182
- signing_key:
183
+ rubygems_version: 3.6.3
183
184
  specification_version: 4
184
185
  summary: Creates events based on a Salesforce SOQL query
185
186
  test_files:
186
187
  - spec/fixtures/vcr_cassettes/describe_apex_test_run_result_object.yml
187
188
  - spec/fixtures/vcr_cassettes/describe_lead_object.yml
188
189
  - spec/fixtures/vcr_cassettes/load_some_lead_objects.yml
190
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_order_by_lastmodifieddate.yml
191
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_twice.yml
192
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter.yml
193
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate.yml
194
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate_no_filters.yml
189
195
  - spec/fixtures/vcr_cassettes/login_into_mydomain.yml
190
196
  - spec/inputs/salesforce_spec.rb
191
197
  - spec/spec_helper.rb
data/DEVELOPER.md DELETED
@@ -1,2 +0,0 @@
1
- # logstash-input-salesforce
2
- This input will query objects from salesforce and turn each record into a message