logstash-input-salesforce 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 76ea7e2676f417fdf939152847cc94bec98d4df0
4
+ data.tar.gz: 98057efaa75fbda1370ff6a341d32a28d065f8b1
5
+ SHA512:
6
+ metadata.gz: 58c0067521a8709ab646b2acd5250505c8afd83f48d4c48852b6d5d8f9a163b0ccdc71db93cff7479f38987dea2ed601589586974fede3875535531fad70bb13
7
+ data.tar.gz: 4c1a7c87ccd8fdef2fdd63f28eddc10a7924c74baa7dcdf8048f4879bca9f4e2bae766d6f4e0e81c0929d16d98ff89e9fcd8a56adba29c2330d13b036a44f957
data/CHANGELOG.md ADDED
File without changes
data/DEVELOPER.md ADDED
@@ -0,0 +1,2 @@
1
+ # logstash-input-salesforce
2
+ This input will query objects from salesforce and turn each record into a message
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012–2015 Elasticsearch <http://www.elastic.co>
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/NOTICE.TXT ADDED
@@ -0,0 +1,5 @@
1
+ Elasticsearch
2
+ Copyright 2012-2015 Elasticsearch
3
+
4
+ This product includes software developed by The Apache Software
5
+ Foundation (http://www.apache.org/).
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Logstash Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elasticsearch/logstash).
4
+
5
+ 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
+
7
+ ## Documentation
8
+
9
+ 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/).
10
+
11
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
12
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elasticsearch/docs#asciidoc-guide
13
+
14
+ ## Need Help?
15
+
16
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
17
+
18
+ ## Developing
19
+
20
+ ### 1. Plugin Developement and Testing
21
+
22
+ #### Code
23
+ - To get started, you'll need JRuby with the Bundler gem installed.
24
+
25
+ - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
26
+
27
+ - Install dependencies
28
+ ```sh
29
+ bundle install
30
+ ```
31
+
32
+ #### Test
33
+
34
+ - Update your dependencies
35
+
36
+ ```sh
37
+ bundle install
38
+ ```
39
+
40
+ - Run tests
41
+
42
+ ```sh
43
+ bundle exec rspec
44
+ ```
45
+
46
+ ### 2. Running your unpublished Plugin in Logstash
47
+
48
+ #### 2.1 Run in a local Logstash clone
49
+
50
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
51
+ ```ruby
52
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
53
+ ```
54
+ - Install plugin
55
+ ```sh
56
+ bin/plugin install --no-verify
57
+ ```
58
+ - Run Logstash with your plugin
59
+ ```sh
60
+ bin/logstash -e 'filter {awesome {}}'
61
+ ```
62
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
63
+
64
+ #### 2.2 Run in an installed Logstash
65
+
66
+ You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
67
+
68
+ - Build your plugin gem
69
+ ```sh
70
+ gem build logstash-filter-awesome.gemspec
71
+ ```
72
+ - Install the plugin from the Logstash home
73
+ ```sh
74
+ bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
75
+ ```
76
+ - Start Logstash and proceed to test the plugin
77
+
78
+ ## Contributing
79
+
80
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
81
+
82
+ Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
83
+
84
+ It is more important to the community that you are able to contribute.
85
+
86
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elasticsearch/logstash/blob/master/CONTRIBUTING.md) file.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "logstash/devutils/rake"
@@ -0,0 +1,176 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
+
5
+ # This Logstash input plugin allows you to query Salesforce using SOQL and puts the results
6
+ # into Logstash, one row per event. You can configure it to pull entire sObjects or only
7
+ # specific fields.
8
+ #
9
+ # NOTE: This input plugin will stop after all the results of the query are processed and will
10
+ # need to be re-run to fetch new results. It does not utilize the streaming API.
11
+ #
12
+ # In order to use this plugin, you will need to create a new SFDC Application using
13
+ # oauth. More details can be found here:
14
+ # https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm
15
+ #
16
+ # You will also need a username, password, and security token for your salesforce instance.
17
+ # More details for generating a token can be found here:
18
+ # https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm
19
+ #
20
+ # In addition to specifying an sObject, you can also supply a list of API fields
21
+ # that will be used in the SOQL query.
22
+ #
23
+ # ==== Example
24
+ # This example prints all the Salesforce Opportunities to standard out
25
+ #
26
+ # [source,ruby]
27
+ # ----------------------------------
28
+ # input {
29
+ # salesforce {
30
+ # client_id => 'OAUTH CLIENT ID FROM YOUR SFDC APP'
31
+ # client_secret => 'OAUTH CLIENT SECRET FROM YOUR SFDC APP'
32
+ # username => 'email@example.com'
33
+ # password => 'super-secret'
34
+ # security_token => 'SECURITY TOKEN FOR THIS USER'
35
+ # sfdc_object_name => 'Opportunity'
36
+ # }
37
+ # }
38
+ #
39
+ # output {
40
+ # stdout {
41
+ # codec => rubydebug
42
+ # }
43
+ # }
44
+ # ----------------------------------
45
+
46
+ class LogStash::Inputs::Salesforce < LogStash::Inputs::Base
47
+
48
+ config_name "salesforce"
49
+ default :codec, "plain" #not used
50
+
51
+ # Set this to true to connect to a sandbox sfdc instance
52
+ # logging in through test.salesforce.com
53
+ config :use_test_sandbox, :validate => :boolean, :default => false
54
+ # By default, this uses the default Restforce API version.
55
+ # To override this, set this to something like "32.0" for example
56
+ config :api_version, :validate => :string, :required => false
57
+ # Consumer Key for authentication. You must set up a new SFDC
58
+ # connected app with oath to use this output. More information
59
+ # can be found here:
60
+ # https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm
61
+ config :client_id, :validate => :string, :required => true
62
+ # Consumer Secret from your oauth enabled connected app
63
+ config :client_secret, :validate => :string, :required => true
64
+ # A valid salesforce user name, usually your email address.
65
+ # Used for authentication and will be the user all objects
66
+ # are created or modified by
67
+ config :username, :validate => :string, :required => true
68
+ # The password used to login to sfdc
69
+ config :password, :validate => :string, :required => true
70
+ # The security token for this account. For more information about
71
+ # generting a security token, see:
72
+ # https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm
73
+ config :security_token, :validate => :string, :required => true
74
+ # The name of the salesforce object you are creating or updating
75
+ config :sfdc_object_name, :validate => :string, :required => true
76
+ # These are the field names to return in the Salesforce query
77
+ # If this is empty, all fields are returned.
78
+ config :sfdc_fields, :validate => :array, :default => []
79
+ # These options will be added to the WHERE clause in the
80
+ # SOQL statement. Additional fields can be filtered on by
81
+ # adding field1 = value1 AND field2 = value2 AND...
82
+ config :sfdc_filters, :validate => :string, :default => ""
83
+ # Setting this to true will convert SFDC's NamedFields__c to named_fields__c
84
+ config :to_underscores, :validate => :boolean, :default => false
85
+
86
+ public
87
+ def register
88
+ require 'restforce'
89
+ obj_desc = client.describe(@sfdc_object_name)
90
+ @sfdc_field_types = get_field_types(obj_desc)
91
+ @sfdc_fields = get_all_fields if @sfdc_fields.empty?
92
+ end # def register
93
+
94
+ public
95
+ def run(queue)
96
+ results = client.query(get_query())
97
+ if results && results.first
98
+ results.each do |result|
99
+ event = LogStash::Event.new()
100
+ decorate(event)
101
+ @sfdc_fields.each do |field|
102
+ field_type = @sfdc_field_types[field]
103
+ value = result.send(field)
104
+ event_key = @to_underscores ? underscore(field) : field
105
+ if not value.nil?
106
+ case field_type
107
+ when 'datetime'
108
+ event[event_key] = LogStash::Timestamp.parse(value)
109
+ when 'date'
110
+ event[event_key] = LogStash::Timestamp.parse(value)
111
+ else
112
+ event[event_key] = value
113
+ end
114
+ end
115
+ end
116
+ queue << event
117
+ end
118
+ end
119
+ end # def run
120
+
121
+ private
122
+ def client
123
+ @client ||= Restforce.new client_options
124
+ end
125
+
126
+ private
127
+ def client_options
128
+ options = {
129
+ :username => @username,
130
+ :password => @password,
131
+ :security_token => @security_token,
132
+ :client_id => @client_id,
133
+ :client_secret => @client_secret
134
+ }
135
+ options.merge!({ :host => "test.salesforce.com" }) if @use_test_sandbox
136
+ options.merge!({ :api_version => @api_version }) if @api_version
137
+ return options
138
+ end
139
+
140
+ private
141
+ def get_query()
142
+ query = ["SELECT",@sfdc_fields.join(','),
143
+ "FROM",@sfdc_object_name]
144
+ query << ["WHERE",@sfdc_filters] unless @sfdc_filters.empty?
145
+ query << "ORDER BY LastModifiedDate DESC" if @sfdc_fields.include?('LastModifiedDate')
146
+ query_str = query.flatten.join(" ")
147
+ @logger.debug? && @logger.debug("SFDC Query", :query => query_str)
148
+ return query_str
149
+ end
150
+
151
+ private
152
+ def get_field_types(obj_desc)
153
+ field_types = {}
154
+ obj_desc.fields.each do |f|
155
+ field_types[f.name] = f.type
156
+ end
157
+ @logger.debug? && @logger.debug("Field types", :field_types => field_types.to_s)
158
+ return field_types
159
+ end
160
+
161
+ private
162
+ def get_all_fields
163
+ return @sfdc_field_types.keys
164
+ end
165
+
166
+ private
167
+ # From http://stackoverflow.com/a/1509957/4701287
168
+ def underscore(camel_cased_word)
169
+ camel_cased_word.to_s.gsub(/::/, '/').
170
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
171
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
172
+ tr("-", "_").
173
+ downcase
174
+ end
175
+
176
+ end # class LogStash::Inputs::Salesforce
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-input-salesforce'
3
+ s.version = '0.1.1'
4
+ s.licenses = ['Apache License (2.0)']
5
+ s.summary = "Pulls objects from Salesforce into Logstash"
6
+ s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
7
+ s.authors = ["Russ Savage"]
8
+ s.email = 'russ@elastic.co'
9
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
10
+ s.require_paths = ["lib"]
11
+
12
+ # Files
13
+ s.files = `git ls-files`.split($\)
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency "logstash-core", '>= 1.4.0', '< 2.0.0'
22
+ s.add_runtime_dependency 'logstash-codec-plain'
23
+ s.add_runtime_dependency 'restforce'
24
+ s.add_development_dependency 'logstash-devutils'
25
+ s.add_development_dependency 'vcr'
26
+ s.add_development_dependency 'webmock'
27
+ s.add_development_dependency 'json'
28
+ end
@@ -0,0 +1,87 @@
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 21:31:28 GMT
23
+ Set-Cookie:
24
+ - BrowserId=xxxx;Path=/;Domain=.salesforce.com;Expires=Mon, 26-Oct-2015 21:31:28 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":"1440711088469","token_type":"Bearer","instance_url":"https://eu2.salesforce.com","signature":"xxxx","access_token":"xxxx"}'
38
+ http_version:
39
+ recorded_at: Thu, 27 Aug 2015 21:31:28 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 21:31:29 GMT
62
+ Set-Cookie:
63
+ - BrowserId=xxxx;Path=/;Domain=.salesforce.com;Expires=Mon, 26-Oct-2015 21:31:29 GMT
64
+ Expires:
65
+ - Thu, 01 Jan 1970 00:00:00 GMT
66
+ Sforce-Limit-Info:
67
+ - api-usage=204568/451000
68
+ Org.eclipse.jetty.server.include.etag:
69
+ - f3049665
70
+ Last-Modified:
71
+ - Thu, 27 Aug 2015 18:14:47 GMT
72
+ Content-Type:
73
+ - application/json;charset=UTF-8
74
+ Etag:
75
+ - f304966-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 21:31:30 GMT
87
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,124 @@
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%20FROM%20Lead%20WHERE%20Email%20LIKE%20'%25@elastic.co'
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
124
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,123 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/inputs/salesforce"
3
+ require "vcr"
4
+ require 'json'
5
+
6
+ RSpec.describe LogStash::Inputs::Salesforce do
7
+ describe "inputs/salesforce" do
8
+ let(:options) do
9
+ {
10
+ "client_id" => "",
11
+ "client_secret" => "",
12
+ "username" => "",
13
+ "password" => "",
14
+ "security_token" => "",
15
+ "sfdc_object_name" => ""
16
+ }
17
+ end
18
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
19
+ subject { input }
20
+
21
+ it "should convert to lowercase with underscores" do
22
+ camel_cased_words = ['CleanStatus','CreatedBy',
23
+ 'of_Open_Opp_related_account__c',
24
+ 'USAField','ABCD_ABCDE_Threshold__c']
25
+ underscore_words = ['clean_status','created_by',
26
+ 'of_open_opp_related_account__c',
27
+ 'usa_field','abcd_abcde_threshold__c']
28
+
29
+ camel_cased_words.zip(underscore_words).each do |c,u|
30
+ expect(subject.send(:underscore,c)).to eq(u)
31
+ end
32
+ end
33
+
34
+ context "add fields and filters" do
35
+ let(:options) do
36
+ {
37
+ "client_id" => "",
38
+ "client_secret" => "",
39
+ "username" => "",
40
+ "password" => "",
41
+ "security_token" => "",
42
+ "sfdc_object_name" => "Lead",
43
+ "sfdc_fields" => ["Something"]
44
+ }
45
+ end
46
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
47
+
48
+ it "should build a query" do
49
+ expect(subject.send(:get_query)).to eq('SELECT Something FROM Lead')
50
+ end
51
+ end
52
+
53
+ context "describe Lead object" do
54
+ VCR.configure do |config|
55
+ config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
56
+ config.hook_into :webmock
57
+ config.before_record do |i|
58
+ if i.response.body.encoding.to_s == 'ASCII-8BIT'
59
+ # required because sfdc doesn't send back the content encoding and it
60
+ # confuses the yaml parser
61
+ json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8"))
62
+ i.response.body = json_body.to_json
63
+ i.response.update_content_length_header
64
+ end
65
+ end
66
+ end
67
+ let(:options) do
68
+ {
69
+ "client_id" => "",
70
+ "client_secret" => "",
71
+ "username" => "",
72
+ "password" => "",
73
+ "security_token" => "",
74
+ "sfdc_object_name" => "Lead"
75
+ }
76
+ end
77
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
78
+ let(:expected_fields_result) { ["Id", "IsDeleted",
79
+ "LastName", "FirstName", "Salutation"] }
80
+ let(:expected_types_result) { [["FirstName", "string"],
81
+ ["Id", "id"],
82
+ ["IsDeleted", "boolean"],
83
+ ["LastName", "string"],
84
+ ["Salutation", "picklist"]] }
85
+ subject { input }
86
+ it "loads the Lead object fields" do
87
+ VCR.use_cassette("describe_lead_object",:decode_compressed_response => true) do
88
+ subject.register
89
+ expect(subject.instance_variable_get(:@sfdc_field_types)).to match_array(expected_types_result)
90
+ expect(subject.instance_variable_get(:@sfdc_fields)).to match_array(expected_fields_result)
91
+ end
92
+ end
93
+ context "load Lead objects" do
94
+ let(:options) do
95
+ {
96
+ "client_id" => "",
97
+ "client_secret" => "",
98
+ "username" => "",
99
+ "password" => "",
100
+ "security_token" => "",
101
+ "sfdc_object_name" => "Lead",
102
+ "sfdc_fields" => ["Id", "IsDeleted", "LastName", "FirstName", "Salutation"],
103
+ "sfdc_filters" => "Email LIKE '%@elastic.co'"
104
+ }
105
+ end
106
+ let(:input) { LogStash::Inputs::Salesforce.new(options) }
107
+ subject { input }
108
+ let(:queue) { [] }
109
+ it "loads some lead records" do
110
+ VCR.use_cassette("load some lead objects",:decode_compressed_response => true) do
111
+ subject.register
112
+ subject.run(queue)
113
+ expect(queue.length).to eq(3)
114
+ e = queue.pop
115
+ expected_fields_result.each do |f|
116
+ expect(e.to_hash).to include(f)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,25 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ config.expect_with :rspec do |expectations|
19
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
20
+ end
21
+
22
+ config.mock_with :rspec do |mocks|
23
+ mocks.verify_partial_doubles = true
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-salesforce
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Russ Savage
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.0
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.0
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: 1.4.0
28
+ - - <
29
+ - !ruby/object:Gem::Version
30
+ version: 2.0.0
31
+ prerelease: false
32
+ type: :runtime
33
+ - !ruby/object:Gem::Dependency
34
+ name: logstash-codec-plain
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ prerelease: false
46
+ type: :runtime
47
+ - !ruby/object:Gem::Dependency
48
+ name: restforce
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ prerelease: false
60
+ type: :runtime
61
+ - !ruby/object:Gem::Dependency
62
+ name: logstash-devutils
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ prerelease: false
74
+ type: :development
75
+ - !ruby/object:Gem::Dependency
76
+ name: vcr
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ prerelease: false
88
+ type: :development
89
+ - !ruby/object:Gem::Dependency
90
+ name: webmock
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ prerelease: false
102
+ type: :development
103
+ - !ruby/object:Gem::Dependency
104
+ name: json
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ prerelease: false
116
+ type: :development
117
+ description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
118
+ email: russ@elastic.co
119
+ executables: []
120
+ extensions: []
121
+ extra_rdoc_files: []
122
+ files:
123
+ - CHANGELOG.md
124
+ - DEVELOPER.md
125
+ - Gemfile
126
+ - LICENSE
127
+ - NOTICE.TXT
128
+ - README.md
129
+ - Rakefile
130
+ - lib/logstash/inputs/salesforce.rb
131
+ - logstash-input-salesforce.gemspec
132
+ - spec/fixtures/vcr_cassettes/describe_lead_object.yml
133
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects.yml
134
+ - spec/inputs/salesforce_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: http://www.elastic.co/guide/en/logstash/current/index.html
137
+ licenses:
138
+ - Apache License (2.0)
139
+ metadata:
140
+ logstash_plugin: 'true'
141
+ logstash_group: input
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.1.9
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: Pulls objects from Salesforce into Logstash
162
+ test_files:
163
+ - spec/fixtures/vcr_cassettes/describe_lead_object.yml
164
+ - spec/fixtures/vcr_cassettes/load_some_lead_objects.yml
165
+ - spec/inputs/salesforce_spec.rb
166
+ - spec/spec_helper.rb