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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +50 -9
- data/docs/index.asciidoc +138 -27
- data/lib/logstash/inputs/salesforce.rb +120 -21
- data/logstash-input-salesforce.gemspec +2 -2
- data/spec/fixtures/vcr_cassettes/load_some_lead_objects_order_by_lastmodifieddate.yml +124 -0
- data/spec/fixtures/vcr_cassettes/load_some_lead_objects_twice.yml +161 -0
- data/spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter.yml +123 -0
- data/spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate.yml +123 -0
- data/spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate_no_filters.yml +123 -0
- data/spec/inputs/salesforce_spec.rb +264 -4
- metadata +30 -24
- data/DEVELOPER.md +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c35a5c5144e60a5958fefebef36dfe5eb74a9b833e56dc513639f4e56d8e476
|
4
|
+
data.tar.gz: ff39efdf9da888ad2668e91fa1afcf7ac8c99ebb56579f675b9efa27387154fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3237d6346f270425502e9d1ec134b976bbb750149ecc2c6814647ae19a4e5a14f9ad9f59e201ce2c8fa354230ab4e848a6d16872ee9cb6df502f79b9ab39d58e
|
7
|
+
data.tar.gz: ed6170f0cb50898d9431f9eacc1d5f6661785dfcde4877ffdc3ead944e1bff15846274bdf60af0e89dbda8704dd59a3a4297fe5ae8c5725f7515c94fc0ca71e1
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 3.3.0
|
2
|
+
- Added `timeout` configuration to override the connect/read timeout for REST requests to Salesforce
|
3
|
+
- Added `interval` configuration to run the plugin continuously, querying records and publishing events
|
4
|
+
for them at a set interval instead of running once and quitting
|
5
|
+
- Added incremental data loading feature, controlled by configuration options `tracking_field`, `tracking_field_value_file`, and `changed_data_filter`
|
6
|
+
|
1
7
|
## 3.2.1
|
2
8
|
- Changes sensitive configs type to Password for better protection from leaks in debug logs. [#35](https://github.com/logstash-plugins/logstash-input-salesforce/pull/35)
|
3
9
|
|
data/README.md
CHANGED
@@ -1,9 +1,34 @@
|
|
1
|
-
# Logstash Plugin
|
1
|
+
# Logstash Salesforce input Plugin
|
2
|
+
|
3
|
+
This Logstash input plugin allows you to query Salesforce using SOQL and puts the results
|
4
|
+
into Logstash, one row per event. You can configure it to pull entire sObjects or only
|
5
|
+
specific fields.
|
2
6
|
|
3
7
|
This is a plugin for [Logstash](https://github.com/elasticsearch/logstash).
|
4
8
|
|
5
9
|
It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
|
6
10
|
|
11
|
+
## How to use
|
12
|
+
|
13
|
+
Add the input plugin to your Logstash pipeline definition.
|
14
|
+
|
15
|
+
This example queries all the Salesforce Opportunities and publishes an event for each opportunity found:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
input {
|
19
|
+
salesforce {
|
20
|
+
client_id => 'OAUTH CLIENT ID FROM YOUR SFDC APP'
|
21
|
+
client_secret => 'OAUTH CLIENT SECRET FROM YOUR SFDC APP'
|
22
|
+
username => 'email@example.com'
|
23
|
+
password => 'super-secret'
|
24
|
+
security_token => 'SECURITY TOKEN FOR THIS USER'
|
25
|
+
sfdc_object_name => 'Opportunity'
|
26
|
+
}
|
27
|
+
}
|
28
|
+
```
|
29
|
+
|
30
|
+
For more examples and an explanation of all configuration options, see https://www.elastic.co/docs/reference/logstash/plugins/plugins-inputs-salesforce.
|
31
|
+
|
7
32
|
## Documentation
|
8
33
|
|
9
34
|
Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elasticsearch.org/guide/en/logstash/current/).
|
@@ -17,39 +42,55 @@ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/log
|
|
17
42
|
|
18
43
|
## Developing
|
19
44
|
|
20
|
-
### 1. Plugin
|
45
|
+
### 1. Plugin Development and Testing
|
21
46
|
|
22
47
|
#### Code
|
23
|
-
- To get started, you'll need JRuby with the Bundler gem installed.
|
24
48
|
|
25
|
-
-
|
49
|
+
- To get started, you'll need JRuby with the Bundler gem installed. We strongly recommend
|
50
|
+
using a Ruby Version Manager such as `rvm` to install JRuby. If you're using a JRuby installed
|
51
|
+
by Homebrew on macOS, replace the `bundle` command with `jbundle` in all examples in this
|
52
|
+
document: Homebrew renames the JRuby binaries so that they don't clash with those from the system
|
53
|
+
(C) Ruby that ships with macOS.
|
54
|
+
|
55
|
+
- Clone the plugin code from the GitHub [logstash-plugins/logstash-input-salesforce](https://github.com/logstash-plugins/logstash-input-salesforce) repository.
|
26
56
|
|
27
57
|
- Install dependencies
|
28
58
|
```sh
|
29
|
-
bundle install
|
59
|
+
bundle install --path=vendor/bundle
|
30
60
|
```
|
31
61
|
|
62
|
+
- Download a source release of the Logstash version you're targeting
|
63
|
+
(e.g. https://github.com/elastic/logstash/archive/refs/tags/v8.14.3.zip) and
|
64
|
+
extract (unzip) it to a local directory.
|
65
|
+
|
32
66
|
#### Test
|
33
67
|
|
34
68
|
- Update your dependencies
|
35
69
|
|
36
70
|
```sh
|
37
|
-
bundle install
|
71
|
+
bundle install --path=vendor/bundle
|
38
72
|
```
|
39
73
|
|
40
74
|
- Run tests
|
41
75
|
|
42
76
|
```sh
|
77
|
+
export LOGSTASH_PATH=<path to logstash source>
|
78
|
+
export LOGSTASH_SOURCE=1
|
43
79
|
bundle exec rspec
|
44
80
|
```
|
45
81
|
|
82
|
+
If you get an error like `Could not find logstash-core-plugin-api-2.1.16-java, logstash-core-8.14.3-java in locally
|
83
|
+
installed gems`, double check that you've set and exported the `LOGSTASH_PATH` and `LOGSTASH_SOURCE` environment
|
84
|
+
variables as explained in the previous section, and that the `LOGSTASH_PATH` points to an unzipped Logstash source
|
85
|
+
distribution.
|
86
|
+
|
46
87
|
### 2. Running your unpublished Plugin in Logstash
|
47
88
|
|
48
89
|
#### 2.1 Run in a local Logstash clone
|
49
90
|
|
50
91
|
- Edit Logstash `Gemfile` and add the local plugin path, for example:
|
51
92
|
```ruby
|
52
|
-
gem "logstash-
|
93
|
+
gem "logstash-input-salesforce", :path => "/your/local/logstash-input-salesforce"
|
53
94
|
```
|
54
95
|
- Install plugin
|
55
96
|
```sh
|
@@ -62,7 +103,7 @@ bin/plugin install --no-verify
|
|
62
103
|
```
|
63
104
|
- Run Logstash with your plugin
|
64
105
|
```sh
|
65
|
-
bin/logstash -e '
|
106
|
+
bin/logstash -e 'input { salesforce { ... } }'
|
66
107
|
```
|
67
108
|
At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
|
68
109
|
|
@@ -72,7 +113,7 @@ You can use the same **2.1** method to run your plugin in an installed Logstash
|
|
72
113
|
|
73
114
|
- Build your plugin gem
|
74
115
|
```sh
|
75
|
-
gem build logstash-
|
116
|
+
gem build logstash-input-salesforce.gemspec
|
76
117
|
```
|
77
118
|
- Install the plugin from the Logstash home
|
78
119
|
```sh
|
data/docs/index.asciidoc
CHANGED
@@ -25,14 +25,15 @@ This Logstash input plugin allows you to query Salesforce using SOQL and puts th
|
|
25
25
|
into Logstash, one row per event. You can configure it to pull entire sObjects or only
|
26
26
|
specific fields.
|
27
27
|
|
28
|
-
NOTE:
|
29
|
-
need to be re-run to fetch new results. It does not utilize the streaming API.
|
28
|
+
NOTE: By default, this input plugin will stop after all the results of the query are processed and will
|
29
|
+
need to be re-run to fetch new results. It does not utilize the streaming API. However, by setting the `interval`
|
30
|
+
configuration option you can configure the plugin to automatically run at a set frequency.
|
30
31
|
|
31
|
-
In order to use this plugin, you will need to create a new
|
32
|
-
|
32
|
+
In order to use this plugin, you will need to create a new Salesforce Connected App with OAuth enabled.
|
33
|
+
More details can be found here:
|
33
34
|
https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm
|
34
35
|
|
35
|
-
You will also need a username, password, and security token for your
|
36
|
+
You will also need a username, password, and security token for your Salesforce instance.
|
36
37
|
More details for generating a token can be found here:
|
37
38
|
https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm
|
38
39
|
|
@@ -41,7 +42,7 @@ that will be used in the SOQL query.
|
|
41
42
|
|
42
43
|
==== HTTP proxy
|
43
44
|
|
44
|
-
If your infrastructure uses
|
45
|
+
If your infrastructure uses an HTTP proxy, you can set the `SALESFORCE_PROXY_URI` environment variable with the desired URI value (e.g `export SALESFORCE_PROXY_URI="http://proxy.example.com:123"`).
|
45
46
|
|
46
47
|
==== Example
|
47
48
|
This example prints all the Salesforce Opportunities to standard out
|
@@ -75,15 +76,20 @@ This plugin supports the following configuration options plus the <<plugins-{typ
|
|
75
76
|
|=======================================================================
|
76
77
|
|Setting |Input type|Required
|
77
78
|
| <<plugins-{type}s-{plugin}-api_version>> |<<string,string>>|No
|
79
|
+
| <<plugins-{type}s-{plugin}-changed_data_filter>> |<<string,string>>|No
|
78
80
|
| <<plugins-{type}s-{plugin}-client_id>> |<<string,string>>|Yes
|
79
81
|
| <<plugins-{type}s-{plugin}-client_secret>> |<<password,password>>|Yes
|
82
|
+
| <<plugins-{type}s-{plugin}-interval>> |<<number,number>>|No
|
80
83
|
| <<plugins-{type}s-{plugin}-password>> |<<password,password>>|Yes
|
81
84
|
| <<plugins-{type}s-{plugin}-security_token>> |<<password,password>>|Yes
|
82
85
|
| <<plugins-{type}s-{plugin}-sfdc_fields>> |<<array,array>>|No
|
83
86
|
| <<plugins-{type}s-{plugin}-sfdc_filters>> |<<string,string>>|No
|
84
87
|
| <<plugins-{type}s-{plugin}-sfdc_instance_url>> |<<string,string>>|No
|
85
88
|
| <<plugins-{type}s-{plugin}-sfdc_object_name>> |<<string,string>>|Yes
|
89
|
+
| <<plugins-{type}s-{plugin}-timeout>> |<<number,number>>|No
|
86
90
|
| <<plugins-{type}s-{plugin}-to_underscores>> |<<boolean,boolean>>|No
|
91
|
+
| <<plugins-{type}s-{plugin}-tracking_field>> |<<string,string>>|No
|
92
|
+
| <<plugins-{type}s-{plugin}-tracking_field_value_file>> |<<string,string>>|No
|
87
93
|
| <<plugins-{type}s-{plugin}-use_test_sandbox>> |<<boolean,boolean>>|No
|
88
94
|
| <<plugins-{type}s-{plugin}-use_tooling_api>> |<<boolean,boolean>>|No
|
89
95
|
| <<plugins-{type}s-{plugin}-username>> |<<string,string>>|Yes
|
@@ -101,7 +107,32 @@ input plugins.
|
|
101
107
|
* There is no default value for this setting.
|
102
108
|
|
103
109
|
By default, this uses the default Restforce API version.
|
104
|
-
To override this, set this to something like "32.0" for example
|
110
|
+
To override this, set this to something like "32.0" for example.
|
111
|
+
|
112
|
+
[id="plugins-{type}s-{plugin}-changed_data_filter"]
|
113
|
+
===== `changed_data_filter`
|
114
|
+
|
115
|
+
* Value type is <<string,string>>
|
116
|
+
* There is no default value for this setting.
|
117
|
+
|
118
|
+
The filter to add to the Salesforce query when a previous tracking field value
|
119
|
+
was read from the <<plugins-{type}s-{plugin}-tracking_field_value_file>>.
|
120
|
+
The string can (and should) contain a placeholder `%+{last_tracking_field_value}+` that
|
121
|
+
will be substituted with the actual value read from the <<plugins-{type}s-{plugin}-tracking_field_value_file>>.
|
122
|
+
|
123
|
+
This clause is combined with any <<plugins-{type}s-{plugin}-sfdc_filters>>
|
124
|
+
clause that is configured using the `AND` operator.
|
125
|
+
|
126
|
+
The value should be properly quoted according to the SOQL rules for the field
|
127
|
+
type.
|
128
|
+
|
129
|
+
**Examples:**
|
130
|
+
|
131
|
+
[source,ruby]
|
132
|
+
"changed_data_filter" => "Number > '%{last_tracking_field_value}'"
|
133
|
+
|
134
|
+
[source,ruby]
|
135
|
+
"changed_data_filter" => "LastModifiedDate >= %{last_tracking_field_value}"
|
105
136
|
|
106
137
|
[id="plugins-{type}s-{plugin}-client_id"]
|
107
138
|
===== `client_id`
|
@@ -110,10 +141,10 @@ To override this, set this to something like "32.0" for example
|
|
110
141
|
* Value type is <<string,string>>
|
111
142
|
* There is no default value for this setting.
|
112
143
|
|
113
|
-
Consumer Key for authentication. You must set up a new
|
114
|
-
connected app with
|
144
|
+
Consumer Key for authentication. You must set up a new Salesforce
|
145
|
+
connected app with OAuth enabled to use this plugin. More information
|
115
146
|
can be found here:
|
116
|
-
https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm
|
147
|
+
https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm.
|
117
148
|
|
118
149
|
[id="plugins-{type}s-{plugin}-client_secret"]
|
119
150
|
===== `client_secret`
|
@@ -122,30 +153,47 @@ https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm
|
|
122
153
|
* Value type is <<password,password>>
|
123
154
|
* There is no default value for this setting.
|
124
155
|
|
125
|
-
Consumer
|
156
|
+
Consumer secret from your OAuth enabled connected app.
|
157
|
+
|
158
|
+
[id="plugins-{type}s-{plugin}-interval"]
|
159
|
+
===== `interval`
|
160
|
+
|
161
|
+
* Value type is <<number,number>>
|
162
|
+
* There is no default value for this setting.
|
163
|
+
|
164
|
+
The interval in seconds between each run of the plugin.
|
165
|
+
|
166
|
+
If specified, the plugin only terminates when it receives the stop
|
167
|
+
signal from Logstash, e.g. when you press Ctrl-C when running interactively,
|
168
|
+
or when the process receives a TERM signal. It will query and publish
|
169
|
+
events for all results, then sleep until `interval` seconds from the start
|
170
|
+
of the previous run of the plugin have passed. If the plugin ran for longer
|
171
|
+
than `interval` seconds, it will run again immediately.
|
172
|
+
|
173
|
+
If this property is not specified or is set to -1, the plugin will run once and then exit.
|
126
174
|
|
127
175
|
[id="plugins-{type}s-{plugin}-password"]
|
128
|
-
===== `password`
|
176
|
+
===== `password`
|
129
177
|
|
130
178
|
* This is a required setting.
|
131
179
|
* Value type is <<password,password>>
|
132
180
|
* There is no default value for this setting.
|
133
181
|
|
134
|
-
The password used to
|
182
|
+
The password used to log in to Salesforce.
|
135
183
|
|
136
184
|
[id="plugins-{type}s-{plugin}-security_token"]
|
137
|
-
===== `security_token`
|
185
|
+
===== `security_token`
|
138
186
|
|
139
|
-
|
140
|
-
|
141
|
-
|
187
|
+
* This is a required setting.
|
188
|
+
* Value type is <<password,password>>
|
189
|
+
* There is no default value for this setting.
|
142
190
|
|
143
191
|
The security token for this account. For more information about
|
144
|
-
|
145
|
-
https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm
|
192
|
+
generating a security token, see:
|
193
|
+
https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm.
|
146
194
|
|
147
195
|
[id="plugins-{type}s-{plugin}-sfdc_fields"]
|
148
|
-
===== `sfdc_fields`
|
196
|
+
===== `sfdc_fields`
|
149
197
|
|
150
198
|
* Value type is <<array,array>>
|
151
199
|
* Default value is `[]`
|
@@ -159,9 +207,9 @@ If this is empty, all fields are returned.
|
|
159
207
|
* Value type is <<string,string>>
|
160
208
|
* Default value is `""`
|
161
209
|
|
162
|
-
These options will be added to the WHERE clause in the
|
210
|
+
These options will be added to the `WHERE` clause in the
|
163
211
|
SOQL statement. Additional fields can be filtered on by
|
164
|
-
adding field1 = value1 AND field2 = value2 AND
|
212
|
+
adding `field1 = value1 AND field2 = value2 AND...`.
|
165
213
|
|
166
214
|
[id="plugins-{type}s-{plugin}-sfdc_instance_url"]
|
167
215
|
===== `sfdc_instance_url`
|
@@ -183,7 +231,18 @@ but not both to configure the url to which the plugin connects to.
|
|
183
231
|
* Value type is <<string,string>>
|
184
232
|
* There is no default value for this setting.
|
185
233
|
|
186
|
-
The name of the
|
234
|
+
The name of the Salesforce object you are creating or updating.
|
235
|
+
|
236
|
+
[id="plugins-{type}s-{plugin}-timeout"]
|
237
|
+
===== `timeout`
|
238
|
+
|
239
|
+
* Value type is <<number,number>>
|
240
|
+
* Default value is `60`
|
241
|
+
|
242
|
+
The timeout to apply to REST API calls to Salesforce, in seconds. If
|
243
|
+
a connection to Salesforce cannot be made in this time, an error occurs.
|
244
|
+
If it takes longer than the timeout for a block of data (e.g. query results) to be
|
245
|
+
read, an error occurs.
|
187
246
|
|
188
247
|
[id="plugins-{type}s-{plugin}-to_underscores"]
|
189
248
|
===== `to_underscores`
|
@@ -191,7 +250,59 @@ The name of the salesforce object you are creating or updating
|
|
191
250
|
* Value type is <<boolean,boolean>>
|
192
251
|
* Default value is `false`
|
193
252
|
|
194
|
-
Setting this to true will convert
|
253
|
+
Setting this to true will convert Salesforce's `++NamedFields__c++` to `++named_fields__c++`.
|
254
|
+
|
255
|
+
[id="plugins-{type}s-{plugin}-tracking_field"]
|
256
|
+
===== `tracking_field`
|
257
|
+
|
258
|
+
* Value type is <<string,string>>
|
259
|
+
* There is no default value for this setting.
|
260
|
+
|
261
|
+
The field to track for incremental data loads. This field will
|
262
|
+
be used in an `ORDER BY ... ASC` clause that is added to the Salesforce query.
|
263
|
+
This field _should_ also be used in the <<plugins-{type}s-{plugin}-changed_data_filter>> clause
|
264
|
+
to actually achieve incremental loading of data.
|
265
|
+
|
266
|
+
The last value (which is the highest value if the query sorts by this field ascending)
|
267
|
+
value for this field will be saved to the file at the path configured by
|
268
|
+
<<plugins-{type}s-{plugin}-tracking_field_value_file>>, if specified.
|
269
|
+
|
270
|
+
This field should ideally be strictly ascending for new records. An
|
271
|
+
autonumber field is ideal for this.
|
272
|
+
|
273
|
+
The standard `LastModifiedDate` field can be used, but since it is not _strictly_
|
274
|
+
ascending (multiple records can have the same `LastModifiedDate`, the
|
275
|
+
<<plugins-{type}s-{plugin}-changed_data_filter>> should account for this by using the `>=`
|
276
|
+
operator, and duplicates should be expected.
|
277
|
+
|
278
|
+
Note that Salesforce does not guarantee that the standard `Id` field has ascending
|
279
|
+
values for new records (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_best_practices.htm).
|
280
|
+
Therefore, using `Id` as tracking field risks missing records and is not recommended.
|
281
|
+
|
282
|
+
If this field is not already included in the <<plugins-{type}s-{plugin}-sfdc_fields>>,
|
283
|
+
it is added.
|
284
|
+
|
285
|
+
[id="plugins-{type}s-{plugin}-tracking_field_value_file"]
|
286
|
+
===== `tracking_field_value_file`
|
287
|
+
|
288
|
+
* Value type is <<string,string>>
|
289
|
+
* There is no default value for this setting.
|
290
|
+
|
291
|
+
The full path to the file from which the latest tracking field value from the previous
|
292
|
+
plugin invocation will be read, and to which the new latest tracking field value will be
|
293
|
+
written after the current plugin invocation.
|
294
|
+
|
295
|
+
This keeps persistent track of the last seen value of the tracking field used for incremental
|
296
|
+
loading of data.
|
297
|
+
|
298
|
+
The file should be readable and writable by the Logstash process.
|
299
|
+
|
300
|
+
If the file exists and a <<plugins-{type}s-{plugin}-changed_data_filter>> is configured,
|
301
|
+
a changed data filter clause is added to the query (and combined with any <<plugins-{type}s-{plugin}-sfdc_filters>>
|
302
|
+
clause that is configured using the `AND` operator).
|
303
|
+
|
304
|
+
If the result set is not empty, the value for `tracking_field` from the last row is
|
305
|
+
written to the file.
|
195
306
|
|
196
307
|
[id="plugins-{type}s-{plugin}-use_test_sandbox"]
|
197
308
|
===== `use_test_sandbox`
|
@@ -200,7 +311,7 @@ Setting this to true will convert SFDC's NamedFields__c to named_fields__c
|
|
200
311
|
* Default value is `false`
|
201
312
|
|
202
313
|
Set this to true to connect to a sandbox sfdc instance
|
203
|
-
logging in through test.salesforce.com
|
314
|
+
logging in through test.salesforce.com.
|
204
315
|
|
205
316
|
Use either this or the `sfdc_instance_url` configuration option
|
206
317
|
but not both to configure the url to which the plugin connects to.
|
@@ -225,9 +336,9 @@ of elements of sfdc flows) and security health check risks.
|
|
225
336
|
* Value type is <<string,string>>
|
226
337
|
* There is no default value for this setting.
|
227
338
|
|
228
|
-
A valid
|
339
|
+
A valid Salesforce username, usually your email address.
|
229
340
|
Used for authentication and will be the user all objects
|
230
|
-
are created or modified by
|
341
|
+
are created or modified by.
|
231
342
|
|
232
343
|
|
233
344
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require "logstash/inputs/base"
|
3
3
|
require "logstash/namespace"
|
4
4
|
require "time"
|
5
|
+
require "stud/interval"
|
5
6
|
|
6
7
|
# This Logstash input plugin allows you to query Salesforce using SOQL and puts the results
|
7
8
|
# into Logstash, one row per event. You can configure it to pull entire sObjects or only
|
@@ -55,46 +56,84 @@ class LogStash::Inputs::Salesforce < LogStash::Inputs::Base
|
|
55
56
|
# See https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling
|
56
57
|
# for more details about the Tooling API
|
57
58
|
config :use_tooling_api, :validate => :boolean, :default => false
|
59
|
+
|
58
60
|
# Set this to true to connect to a sandbox sfdc instance
|
59
61
|
# logging in through test.salesforce.com
|
60
62
|
config :use_test_sandbox, :validate => :boolean, :default => false
|
63
|
+
|
61
64
|
# Set this to the instance url of the sfdc instance you want
|
62
65
|
# to connect to already during login. If you have configured
|
63
66
|
# a MyDomain in your sfdc instance you would provide
|
64
67
|
# <mydomain>.my.salesforce.com here.
|
65
68
|
config :sfdc_instance_url, :validate => :string, :required => false
|
69
|
+
|
66
70
|
# By default, this uses the default Restforce API version.
|
67
71
|
# To override this, set this to something like "32.0" for example
|
68
72
|
config :api_version, :validate => :string, :required => false
|
73
|
+
|
69
74
|
# Consumer Key for authentication. You must set up a new SFDC
|
70
75
|
# connected app with oath to use this output. More information
|
71
76
|
# can be found here:
|
72
77
|
# https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm
|
73
78
|
config :client_id, :validate => :string, :required => true
|
79
|
+
|
74
80
|
# Consumer Secret from your oauth enabled connected app
|
75
81
|
config :client_secret, :validate => :password, :required => true
|
82
|
+
|
76
83
|
# A valid salesforce user name, usually your email address.
|
77
84
|
# Used for authentication and will be the user all objects
|
78
85
|
# are created or modified by
|
79
86
|
config :username, :validate => :string, :required => true
|
87
|
+
|
80
88
|
# The password used to login to sfdc
|
81
89
|
config :password, :validate => :password, :required => true
|
90
|
+
|
82
91
|
# The security token for this account. For more information about
|
83
92
|
# generting a security token, see:
|
84
93
|
# https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm
|
85
94
|
config :security_token, :validate => :password, :required => true
|
95
|
+
|
86
96
|
# The name of the salesforce object you are creating or updating
|
87
97
|
config :sfdc_object_name, :validate => :string, :required => true
|
98
|
+
|
88
99
|
# These are the field names to return in the Salesforce query
|
89
100
|
# If this is empty, all fields are returned.
|
90
101
|
config :sfdc_fields, :validate => :array, :default => []
|
102
|
+
|
91
103
|
# These options will be added to the WHERE clause in the
|
92
104
|
# SOQL statement. Additional fields can be filtered on by
|
93
105
|
# adding field1 = value1 AND field2 = value2 AND...
|
94
106
|
config :sfdc_filters, :validate => :string, :default => ""
|
107
|
+
|
108
|
+
# RESTForce request timeout in seconds.
|
109
|
+
config :timeout, :validate => :number, :default => 60, :required => false
|
110
|
+
|
95
111
|
# Setting this to true will convert SFDC's NamedFields__c to named_fields__c
|
96
112
|
config :to_underscores, :validate => :boolean, :default => false
|
97
113
|
|
114
|
+
# File that stores the tracking field's latest value. This is read before querying data to interpolate
|
115
|
+
# the tracking field value into the incremental_filter, and the latest value of the tracking field is written
|
116
|
+
# to it after all the query results have been read.
|
117
|
+
config :tracking_field_value_file, :validate => :string, :required => false
|
118
|
+
|
119
|
+
# Filter clause to use for incremental retrieval and indexing of data that has changed since the last invodation
|
120
|
+
# of the plugin. This is combined with sfdc_filters using the AND operator, if tracking_field_value_path exists.
|
121
|
+
# String interpolation is applied to replace "%{last_tracking_field_value}" in this string with the value read
|
122
|
+
# from tracking_field_value_file. This would usually be something like "tracking_field > '%{last_tracking_field_value}'"
|
123
|
+
# where tracking_field is the API name of the actual tracking field set using the tracking_field configuration property,
|
124
|
+
# e.g. LastModifiedDate
|
125
|
+
config :changed_data_filter, :validate => :string, :required => false
|
126
|
+
|
127
|
+
# The field from which the last value will be stored in the tracking_field_value_file and interpolated
|
128
|
+
# for "%{last_tracking_field_value}" in the changed_data_filter expression. This field will also be used in an ORDER BY
|
129
|
+
# clause added to the query, with sorting done ascending, so that the last value in the results is also the
|
130
|
+
# highest.
|
131
|
+
config :tracking_field, :validate => :string, :required => false
|
132
|
+
|
133
|
+
# Interval to run the command. Value is in seconds. If no interval is given,
|
134
|
+
# this plugin only fetches data once.
|
135
|
+
config :interval, :validate => :number, :required => false, :default => -1
|
136
|
+
|
98
137
|
public
|
99
138
|
def register
|
100
139
|
require 'restforce'
|
@@ -105,26 +144,57 @@ class LogStash::Inputs::Salesforce < LogStash::Inputs::Base
|
|
105
144
|
|
106
145
|
public
|
107
146
|
def run(queue)
|
108
|
-
|
109
|
-
|
110
|
-
results
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
147
|
+
while !stop?
|
148
|
+
start = Time.now
|
149
|
+
results = client.query(get_query())
|
150
|
+
latest_tracking_field_value = nil
|
151
|
+
if results && results.first
|
152
|
+
results.each do |result|
|
153
|
+
event = LogStash::Event.new()
|
154
|
+
decorate(event)
|
155
|
+
@sfdc_fields.each do |field|
|
156
|
+
field_type = @sfdc_field_types[field]
|
157
|
+
value = result.send(field)
|
158
|
+
event_key = @to_underscores ? underscore(field) : field
|
159
|
+
unless value.nil?
|
160
|
+
case field_type
|
161
|
+
when 'datetime', 'date'
|
162
|
+
event.set(event_key, format_time(value))
|
163
|
+
else
|
164
|
+
event.set(event_key, value)
|
165
|
+
end
|
123
166
|
end
|
124
167
|
end
|
168
|
+
queue << event
|
169
|
+
unless @tracking_field.nil?
|
170
|
+
latest_tracking_field_value = result[@tracking_field]
|
171
|
+
end
|
172
|
+
end # loop sObjects
|
173
|
+
end
|
174
|
+
|
175
|
+
unless @tracking_field_value_file.nil?
|
176
|
+
unless latest_tracking_field_value.nil?
|
177
|
+
@logger.debug("Writing latest tracking field value " + latest_tracking_field_value + " to " + @tracking_field_value_file)
|
178
|
+
File.write(@tracking_field_value_file, latest_tracking_field_value)
|
179
|
+
else
|
180
|
+
@logger.debug("No tracking field value found in result, not updating " + @tracking_field_value_file)
|
125
181
|
end
|
126
|
-
queue << event
|
127
182
|
end
|
183
|
+
|
184
|
+
if @interval == -1
|
185
|
+
break
|
186
|
+
else
|
187
|
+
duration = Time.now - start
|
188
|
+
# Sleep for the remainder of the interval, or 0 if the duration ran
|
189
|
+
# longer than the interval.
|
190
|
+
sleeptime = [0, @interval - duration].max
|
191
|
+
if sleeptime == 0
|
192
|
+
@logger.warn("Execution ran longer than the interval. Skipping sleep.",
|
193
|
+
:duration => duration,
|
194
|
+
:interval => @interval)
|
195
|
+
end
|
196
|
+
Stud.stoppable_sleep(sleeptime) { stop? }
|
197
|
+
end # end interval check
|
128
198
|
end
|
129
199
|
end # def run
|
130
200
|
|
@@ -144,7 +214,8 @@ class LogStash::Inputs::Salesforce < LogStash::Inputs::Base
|
|
144
214
|
:password => @password.value,
|
145
215
|
:security_token => @security_token.value,
|
146
216
|
:client_id => @client_id,
|
147
|
-
:client_secret => @client_secret.value
|
217
|
+
:client_secret => @client_secret.value,
|
218
|
+
:timeout => @timeout
|
148
219
|
}
|
149
220
|
# configure the endpoint to which restforce connects to for authentication
|
150
221
|
if @sfdc_instance_url && @use_test_sandbox
|
@@ -160,15 +231,43 @@ class LogStash::Inputs::Salesforce < LogStash::Inputs::Base
|
|
160
231
|
|
161
232
|
private
|
162
233
|
def get_query()
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
234
|
+
sfdc_fields = @sfdc_fields.dup
|
235
|
+
unless @tracking_field.nil?
|
236
|
+
unless sfdc_fields.include?(@tracking_field)
|
237
|
+
sfdc_fields << [@tracking_field]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
query = ["SELECT", sfdc_fields.join(','),
|
241
|
+
"FROM", @sfdc_object_name]
|
242
|
+
where = []
|
243
|
+
unless @sfdc_filters.empty?
|
244
|
+
append_to_where_clause(@sfdc_filters, where)
|
245
|
+
end
|
246
|
+
unless @changed_data_filter.nil?
|
247
|
+
if File.exist?(@tracking_field_value_file)
|
248
|
+
last_tracking_field_value = File.read(@tracking_field_value_file)
|
249
|
+
changed_data_filter_interpolated = @changed_data_filter % { :last_tracking_field_value => last_tracking_field_value }
|
250
|
+
append_to_where_clause(changed_data_filter_interpolated, where)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
query << where
|
254
|
+
unless @tracking_field.nil?
|
255
|
+
query << ["ORDER BY", @tracking_field, "ASC"]
|
256
|
+
end
|
167
257
|
query_str = query.flatten.join(" ")
|
168
258
|
@logger.debug? && @logger.debug("SFDC Query", :query => query_str)
|
169
259
|
return query_str
|
170
260
|
end
|
171
261
|
|
262
|
+
def append_to_where_clause(changed_data_filter_interpolated, where)
|
263
|
+
if where.empty?
|
264
|
+
where << ["WHERE"]
|
265
|
+
else
|
266
|
+
where << ["AND"]
|
267
|
+
end
|
268
|
+
where << [changed_data_filter_interpolated]
|
269
|
+
end
|
270
|
+
|
172
271
|
private
|
173
272
|
def get_field_types(obj_desc)
|
174
273
|
field_types = {}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-input-salesforce'
|
3
|
-
s.version = '3.
|
4
|
-
s.licenses = ['
|
3
|
+
s.version = '3.3.0'
|
4
|
+
s.licenses = ['apache-2.0']
|
5
5
|
s.summary = "Creates events based on a Salesforce SOQL query"
|
6
6
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
|
7
7
|
s.authors = ["Russ Savage"]
|