├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── NOTICE.TXT ├── README.md ├── Rakefile ├── docs └── index.asciidoc ├── lib └── logstash │ └── inputs │ └── salesforce.rb ├── logstash-input-salesforce.gemspec └── spec ├── fixtures └── vcr_cassettes │ ├── describe_apex_test_run_result_object.yml │ ├── describe_lead_object.yml │ ├── load_some_lead_objects.yml │ ├── load_some_lead_objects_order_by_lastmodifieddate.yml │ ├── load_some_lead_objects_twice.yml │ ├── load_some_lead_objects_with_lastmodifieddate_filter.yml │ ├── load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate.yml │ ├── load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate_no_filters.yml │ └── login_into_mydomain.yml ├── inputs └── salesforce_spec.rb └── spec_helper.rb /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Logstash 2 | 3 | All contributions are welcome: ideas, patches, documentation, bug reports, 4 | complaints, etc! 5 | 6 | Programming is not a required skill, and there are many ways to help out! 7 | It is more important to us that you are able to contribute. 8 | 9 | That said, some basic guidelines, which you are free to ignore :) 10 | 11 | ## Want to learn? 12 | 13 | Want to lurk about and see what others are doing with Logstash? 14 | 15 | * The irc channel (#logstash on irc.freenode.org) is a good place for this 16 | * The [forum](https://discuss.elastic.co/c/logstash) is also 17 | great for learning from others. 18 | 19 | ## Got Questions? 20 | 21 | Have a problem you want Logstash to solve for you? 22 | 23 | * You can ask a question in the [forum](https://discuss.elastic.co/c/logstash) 24 | * Alternately, you are welcome to join the IRC channel #logstash on 25 | irc.freenode.org and ask for help there! 26 | 27 | ## Have an Idea or Feature Request? 28 | 29 | * File a ticket on [GitHub](https://github.com/elastic/logstash/issues). Please remember that GitHub is used only for issues and feature requests. If you have a general question, the [forum](https://discuss.elastic.co/c/logstash) or IRC would be the best place to ask. 30 | 31 | ## Something Not Working? Found a Bug? 32 | 33 | If you think you found a bug, it probably is a bug. 34 | 35 | * If it is a general Logstash or a pipeline issue, file it in [Logstash GitHub](https://github.com/elasticsearch/logstash/issues) 36 | * If it is specific to a plugin, please file it in the respective repository under [logstash-plugins](https://github.com/logstash-plugins) 37 | * or ask the [forum](https://discuss.elastic.co/c/logstash). 38 | 39 | # Contributing Documentation and Code Changes 40 | 41 | If you have a bugfix or new feature that you would like to contribute to 42 | logstash, and you think it will take more than a few minutes to produce the fix 43 | (ie; write code), it is worth discussing the change with the Logstash users and developers first! You can reach us via [GitHub](https://github.com/elastic/logstash/issues), the [forum](https://discuss.elastic.co/c/logstash), or via IRC (#logstash on freenode irc) 44 | Please note that Pull Requests without tests will not be merged. If you would like to contribute but do not have experience with writing tests, please ping us on IRC/forum or create a PR and ask our help. 45 | 46 | ## Contributing to plugins 47 | 48 | Check our [documentation](https://www.elastic.co/guide/en/logstash/current/contributing-to-logstash.html) on how to contribute to plugins or write your own! It is super easy! 49 | 50 | ## Contribution Steps 51 | 52 | 1. Test your changes! [Run](https://github.com/elastic/logstash#testing) the test suite 53 | 2. Please make sure you have signed our [Contributor License 54 | Agreement](https://www.elastic.co/contributor-agreement/). We are not 55 | asking you to assign copyright to us, but to give us the right to distribute 56 | your code without restriction. We ask this of all contributors in order to 57 | assure our users of the origin and continuing existence of the code. You 58 | only need to sign the CLA once. 59 | 3. Send a pull request! Push your changes to your fork of the repository and 60 | [submit a pull 61 | request](https://help.github.com/articles/using-pull-requests). In the pull 62 | request, describe what your changes do and mention any bugs/issues related 63 | to the pull request. 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please post all product and debugging questions on our [forum](https://discuss.elastic.co/c/logstash). Your questions will reach our wider community members there, and if we confirm that there is a bug, then we can open a new issue here. 2 | 3 | For all general issues, please provide the following details for fast resolution: 4 | 5 | - Version: 6 | - Operating System: 7 | - Config File (if you have sensitive info, please remove it): 8 | - Sample Data: 9 | - Steps to Reproduce: 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thanks for contributing to Logstash! If you haven't already signed our CLA, here's a handy link: https://www.elastic.co/contributor-agreement/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | import: 2 | - logstash-plugins/.ci:travis/travis.yml@1.x -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 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 | 7 | ## 3.2.1 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) 9 | 10 | ## 3.2.0 11 | - Added `use_tooling_api` configuration to connect to the Salesforce Tooling API instead of the regular Rest API. [#26](https://github.com/logstash-plugins/logstash-input-salesforce/pull/26) 12 | 13 | ## 3.1.0 14 | - Added `sfdc_instance_url` configuration to connect to a specific url. [#28](https://github.com/logstash-plugins/logstash-input-salesforce/pull/28) 15 | - Switch to restforce v5+ (for logstash 8.x compatibility) 16 | 17 | ## 3.0.7 18 | - Added description for `SALESFORCE_PROXY_URI` environment variable. 19 | 20 | ## 3.0.6 21 | - Make sure 'recent' restforce dependency is used (to help dependency resolution) 22 | 23 | ## 3.0.5 24 | - Docs: Set the default_codec doc attribute. 25 | 26 | ## 3.0.4 27 | - Update gemspec summary 28 | 29 | ## 3.0.3 30 | - Fix some documentation issues 31 | 32 | # 3.0.1 33 | - Correctly format time from salesforce events 34 | 35 | # 3.0.0 36 | - Update dependency of logstash-core-plugin-api to ">= 1.60", "<= 2.99" (for logstash 5.x compatibility) 37 | - Update event api to .set and .get accessors 38 | 39 | # 2.0.4 40 | - Depend on logstash-core-plugin-api instead of logstash-core, removing the need to mass update plugins on major releases of logstash 41 | 42 | # 2.0.3 43 | - New dependency requirements for logstash-core for the 5.0 release 44 | 45 | ## 2.0.0 46 | - Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully, 47 | instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895 48 | - Dependency on logstash-core update to 2.0 49 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | logstash_path = ENV["LOGSTASH_PATH"] || "../../logstash" 6 | use_logstash_source = ENV["LOGSTASH_SOURCE"] && ENV["LOGSTASH_SOURCE"].to_s == "1" 7 | 8 | if Dir.exist?(logstash_path) && use_logstash_source 9 | gem 'logstash-core', :path => "#{logstash_path}/logstash-core" 10 | gem 'logstash-core-plugin-api', :path => "#{logstash_path}/logstash-core-plugin-api" 11 | end 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Elastic and contributors 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE.TXT: -------------------------------------------------------------------------------- 1 | Elasticsearch 2 | Copyright 2012-2015 Elasticsearch 3 | 4 | This product includes software developed by The Apache Software 5 | Foundation (http://www.apache.org/). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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. 6 | 7 | This is a plugin for [Logstash](https://github.com/elasticsearch/logstash). 8 | 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. 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 | 32 | ## Documentation 33 | 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/). 35 | 36 | - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive 37 | - For more asciidoc formatting tips, see the excellent reference here https://github.com/elasticsearch/docs#asciidoc-guide 38 | 39 | ## Need Help? 40 | 41 | Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum. 42 | 43 | ## Developing 44 | 45 | ### 1. Plugin Development and Testing 46 | 47 | #### Code 48 | 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. 56 | 57 | - Install dependencies 58 | ```sh 59 | bundle install --path=vendor/bundle 60 | ``` 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 | 66 | #### Test 67 | 68 | - Update your dependencies 69 | 70 | ```sh 71 | bundle install --path=vendor/bundle 72 | ``` 73 | 74 | - Run tests 75 | 76 | ```sh 77 | export LOGSTASH_PATH= 78 | export LOGSTASH_SOURCE=1 79 | bundle exec rspec 80 | ``` 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 | 87 | ### 2. Running your unpublished Plugin in Logstash 88 | 89 | #### 2.1 Run in a local Logstash clone 90 | 91 | - Edit Logstash `Gemfile` and add the local plugin path, for example: 92 | ```ruby 93 | gem "logstash-input-salesforce", :path => "/your/local/logstash-input-salesforce" 94 | ``` 95 | - Install plugin 96 | ```sh 97 | # Logstash 2.3 and higher 98 | bin/logstash-plugin install --no-verify 99 | 100 | # Prior to Logstash 2.3 101 | bin/plugin install --no-verify 102 | 103 | ``` 104 | - Run Logstash with your plugin 105 | ```sh 106 | bin/logstash -e 'input { salesforce { ... } }' 107 | ``` 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. 109 | 110 | #### 2.2 Run in an installed Logstash 111 | 112 | 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: 113 | 114 | - Build your plugin gem 115 | ```sh 116 | gem build logstash-input-salesforce.gemspec 117 | ``` 118 | - Install the plugin from the Logstash home 119 | ```sh 120 | # Logstash 2.3 and higher 121 | bin/logstash-plugin install --no-verify 122 | 123 | # Prior to Logstash 2.3 124 | bin/plugin install --no-verify 125 | 126 | ``` 127 | - Start Logstash and proceed to test the plugin 128 | 129 | ## Contributing 130 | 131 | All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin. 132 | 133 | 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. 134 | 135 | It is more important to the community that you are able to contribute. 136 | 137 | For more information about contributing, see the [CONTRIBUTING](https://github.com/elasticsearch/logstash/blob/master/CONTRIBUTING.md) file. 138 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "logstash/devutils/rake" 2 | -------------------------------------------------------------------------------- /docs/index.asciidoc: -------------------------------------------------------------------------------- 1 | :plugin: salesforce 2 | :type: input 3 | :default_codec: plain 4 | 5 | /////////////////////////////////////////// 6 | START - GENERATED VARIABLES, DO NOT EDIT! 7 | /////////////////////////////////////////// 8 | :version: %VERSION% 9 | :release_date: %RELEASE_DATE% 10 | :changelog_url: %CHANGELOG_URL% 11 | :include_path: ../../../../logstash/docs/include 12 | /////////////////////////////////////////// 13 | END - GENERATED VARIABLES, DO NOT EDIT! 14 | /////////////////////////////////////////// 15 | 16 | [id="plugins-{type}s-{plugin}"] 17 | 18 | === Salesforce input plugin 19 | 20 | include::{include_path}/plugin_header.asciidoc[] 21 | 22 | ==== Description 23 | 24 | This Logstash input plugin allows you to query Salesforce using SOQL and puts the results 25 | into Logstash, one row per event. You can configure it to pull entire sObjects or only 26 | specific fields. 27 | 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. 31 | 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: 34 | https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm 35 | 36 | You will also need a username, password, and security token for your Salesforce instance. 37 | More details for generating a token can be found here: 38 | https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm 39 | 40 | In addition to specifying an sObject, you can also supply a list of API fields 41 | that will be used in the SOQL query. 42 | 43 | ==== HTTP proxy 44 | 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"`). 46 | 47 | ==== Example 48 | This example prints all the Salesforce Opportunities to standard out 49 | 50 | [source,ruby] 51 | ---------------------------------- 52 | input { 53 | salesforce { 54 | client_id => 'OAUTH CLIENT ID FROM YOUR SFDC APP' 55 | client_secret => 'OAUTH CLIENT SECRET FROM YOUR SFDC APP' 56 | username => 'email@example.com' 57 | password => 'super-secret' 58 | security_token => 'SECURITY TOKEN FOR THIS USER' 59 | sfdc_object_name => 'Opportunity' 60 | } 61 | } 62 | 63 | output { 64 | stdout { 65 | codec => rubydebug 66 | } 67 | } 68 | ---------------------------------- 69 | 70 | [id="plugins-{type}s-{plugin}-options"] 71 | ==== Salesforce Input Configuration Options 72 | 73 | This plugin supports the following configuration options plus the <> described later. 74 | 75 | [cols="<,<,<",options="header",] 76 | |======================================================================= 77 | |Setting |Input type|Required 78 | | <> |<>|No 79 | | <> |<>|No 80 | | <> |<>|Yes 81 | | <> |<>|Yes 82 | | <> |<>|No 83 | | <> |<>|Yes 84 | | <> |<>|Yes 85 | | <> |<>|No 86 | | <> |<>|No 87 | | <> |<>|No 88 | | <> |<>|Yes 89 | | <> |<>|No 90 | | <> |<>|No 91 | | <> |<>|No 92 | | <> |<>|No 93 | | <> |<>|No 94 | | <> |<>|No 95 | | <> |<>|Yes 96 | |======================================================================= 97 | 98 | Also see <> for a list of options supported by all 99 | input plugins. 100 | 101 |   102 | 103 | [id="plugins-{type}s-{plugin}-api_version"] 104 | ===== `api_version` 105 | 106 | * Value type is <> 107 | * There is no default value for this setting. 108 | 109 | By default, this uses the default Restforce API version. 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 <> 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 <>. 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 <>. 122 | 123 | This clause is combined with any <> 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}" 136 | 137 | [id="plugins-{type}s-{plugin}-client_id"] 138 | ===== `client_id` 139 | 140 | * This is a required setting. 141 | * Value type is <> 142 | * There is no default value for this setting. 143 | 144 | Consumer Key for authentication. You must set up a new Salesforce 145 | connected app with OAuth enabled to use this plugin. More information 146 | can be found here: 147 | https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm. 148 | 149 | [id="plugins-{type}s-{plugin}-client_secret"] 150 | ===== `client_secret` 151 | 152 | * This is a required setting. 153 | * Value type is <> 154 | * There is no default value for this setting. 155 | 156 | Consumer secret from your OAuth enabled connected app. 157 | 158 | [id="plugins-{type}s-{plugin}-interval"] 159 | ===== `interval` 160 | 161 | * Value type is <> 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. 174 | 175 | [id="plugins-{type}s-{plugin}-password"] 176 | ===== `password` 177 | 178 | * This is a required setting. 179 | * Value type is <> 180 | * There is no default value for this setting. 181 | 182 | The password used to log in to Salesforce. 183 | 184 | [id="plugins-{type}s-{plugin}-security_token"] 185 | ===== `security_token` 186 | 187 | * This is a required setting. 188 | * Value type is <> 189 | * There is no default value for this setting. 190 | 191 | The security token for this account. For more information about 192 | generating a security token, see: 193 | https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm. 194 | 195 | [id="plugins-{type}s-{plugin}-sfdc_fields"] 196 | ===== `sfdc_fields` 197 | 198 | * Value type is <> 199 | * Default value is `[]` 200 | 201 | These are the field names to return in the Salesforce query 202 | If this is empty, all fields are returned. 203 | 204 | [id="plugins-{type}s-{plugin}-sfdc_filters"] 205 | ===== `sfdc_filters` 206 | 207 | * Value type is <> 208 | * Default value is `""` 209 | 210 | These options will be added to the `WHERE` clause in the 211 | SOQL statement. Additional fields can be filtered on by 212 | adding `field1 = value1 AND field2 = value2 AND...`. 213 | 214 | [id="plugins-{type}s-{plugin}-sfdc_instance_url"] 215 | ===== `sfdc_instance_url` 216 | 217 | * Value type is <> 218 | * There is no default value for this setting. 219 | 220 | The url of a Salesforce instance. Provide the url if you want to connect 221 | to your Salesforce instance instead of 222 | `login.salesforce.com` or `test.salesforce.com` at login. 223 | 224 | Use either this or the `use_test_sandbox` configuration option 225 | but not both to configure the url to which the plugin connects to. 226 | 227 | [id="plugins-{type}s-{plugin}-sfdc_object_name"] 228 | ===== `sfdc_object_name` 229 | 230 | * This is a required setting. 231 | * Value type is <> 232 | * There is no default value for this setting. 233 | 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 <> 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. 246 | 247 | [id="plugins-{type}s-{plugin}-to_underscores"] 248 | ===== `to_underscores` 249 | 250 | * Value type is <> 251 | * Default value is `false` 252 | 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 <> 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 <> 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 | <>, 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 | <> 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 <>, 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 <> 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 <> is configured, 301 | a changed data filter clause is added to the query (and combined with any <> 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. 306 | 307 | [id="plugins-{type}s-{plugin}-use_test_sandbox"] 308 | ===== `use_test_sandbox` 309 | 310 | * Value type is <> 311 | * Default value is `false` 312 | 313 | Set this to true to connect to a sandbox sfdc instance 314 | logging in through test.salesforce.com. 315 | 316 | Use either this or the `sfdc_instance_url` configuration option 317 | but not both to configure the url to which the plugin connects to. 318 | 319 | [id="plugins-{type}s-{plugin}-use_tooling_api"] 320 | ===== `use_tooling_api` 321 | 322 | * Value type is <> 323 | * Default value is `false` 324 | 325 | Set this to true to connect to the sfdc tooling api instead of the regular 326 | sfdc rest api. See 327 | https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling 328 | for details about the sfdc tooling api. Use cases for the sfdc tooling api 329 | include reading apex unit test results, flow coverage results (e.g. coverage 330 | of elements of sfdc flows) and security health check risks. 331 | 332 | [id="plugins-{type}s-{plugin}-username"] 333 | ===== `username` 334 | 335 | * This is a required setting. 336 | * Value type is <> 337 | * There is no default value for this setting. 338 | 339 | A valid Salesforce username, usually your email address. 340 | Used for authentication and will be the user all objects 341 | are created or modified by. 342 | 343 | 344 | 345 | [id="plugins-{type}s-{plugin}-common-options"] 346 | include::{include_path}/{type}.asciidoc[] 347 | 348 | :default_codec!: 349 | -------------------------------------------------------------------------------- /lib/logstash/inputs/salesforce.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require "logstash/inputs/base" 3 | require "logstash/namespace" 4 | require "time" 5 | require "stud/interval" 6 | 7 | # This Logstash input plugin allows you to query Salesforce using SOQL and puts the results 8 | # into Logstash, one row per event. You can configure it to pull entire sObjects or only 9 | # specific fields. 10 | # 11 | # NOTE: This input plugin will stop after all the results of the query are processed and will 12 | # need to be re-run to fetch new results. It does not utilize the streaming API. 13 | # 14 | # In order to use this plugin, you will need to create a new SFDC Application using 15 | # oauth. More details can be found here: 16 | # https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm 17 | # 18 | # You will also need a username, password, and security token for your salesforce instance. 19 | # More details for generating a token can be found here: 20 | # https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm 21 | # 22 | # In addition to specifying an sObject, you can also supply a list of API fields 23 | # that will be used in the SOQL query. 24 | # 25 | # ==== Example 26 | # This example prints all the Salesforce Opportunities to standard out 27 | # 28 | # [source,ruby] 29 | # ---------------------------------- 30 | # input { 31 | # salesforce { 32 | # client_id => 'OAUTH CLIENT ID FROM YOUR SFDC APP' 33 | # client_secret => 'OAUTH CLIENT SECRET FROM YOUR SFDC APP' 34 | # username => 'email@example.com' 35 | # password => 'super-secret' 36 | # security_token => 'SECURITY TOKEN FOR THIS USER' 37 | # sfdc_object_name => 'Opportunity' 38 | # } 39 | # } 40 | # 41 | # output { 42 | # stdout { 43 | # codec => rubydebug 44 | # } 45 | # } 46 | # ---------------------------------- 47 | 48 | class LogStash::Inputs::Salesforce < LogStash::Inputs::Base 49 | 50 | config_name "salesforce" 51 | default :codec, "plain" #not used 52 | 53 | # Set this to true to connect via the Tooling API instead of the Rest API. 54 | # This allows accessing information like Apex Unit Test Results, 55 | # Flow Coverage Results, Security Health Check Risks, etc. 56 | # See https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling 57 | # for more details about the Tooling API 58 | config :use_tooling_api, :validate => :boolean, :default => false 59 | 60 | # Set this to true to connect to a sandbox sfdc instance 61 | # logging in through test.salesforce.com 62 | config :use_test_sandbox, :validate => :boolean, :default => false 63 | 64 | # Set this to the instance url of the sfdc instance you want 65 | # to connect to already during login. If you have configured 66 | # a MyDomain in your sfdc instance you would provide 67 | # .my.salesforce.com here. 68 | config :sfdc_instance_url, :validate => :string, :required => false 69 | 70 | # By default, this uses the default Restforce API version. 71 | # To override this, set this to something like "32.0" for example 72 | config :api_version, :validate => :string, :required => false 73 | 74 | # Consumer Key for authentication. You must set up a new SFDC 75 | # connected app with oath to use this output. More information 76 | # can be found here: 77 | # https://help.salesforce.com/apex/HTViewHelpDoc?id=connected_app_create.htm 78 | config :client_id, :validate => :string, :required => true 79 | 80 | # Consumer Secret from your oauth enabled connected app 81 | config :client_secret, :validate => :password, :required => true 82 | 83 | # A valid salesforce user name, usually your email address. 84 | # Used for authentication and will be the user all objects 85 | # are created or modified by 86 | config :username, :validate => :string, :required => true 87 | 88 | # The password used to login to sfdc 89 | config :password, :validate => :password, :required => true 90 | 91 | # The security token for this account. For more information about 92 | # generting a security token, see: 93 | # https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm 94 | config :security_token, :validate => :password, :required => true 95 | 96 | # The name of the salesforce object you are creating or updating 97 | config :sfdc_object_name, :validate => :string, :required => true 98 | 99 | # These are the field names to return in the Salesforce query 100 | # If this is empty, all fields are returned. 101 | config :sfdc_fields, :validate => :array, :default => [] 102 | 103 | # These options will be added to the WHERE clause in the 104 | # SOQL statement. Additional fields can be filtered on by 105 | # adding field1 = value1 AND field2 = value2 AND... 106 | config :sfdc_filters, :validate => :string, :default => "" 107 | 108 | # RESTForce request timeout in seconds. 109 | config :timeout, :validate => :number, :default => 60, :required => false 110 | 111 | # Setting this to true will convert SFDC's NamedFields__c to named_fields__c 112 | config :to_underscores, :validate => :boolean, :default => false 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 | 137 | public 138 | def register 139 | require 'restforce' 140 | obj_desc = client.describe(@sfdc_object_name) 141 | @sfdc_field_types = get_field_types(obj_desc) 142 | @sfdc_fields = get_all_fields if @sfdc_fields.empty? 143 | end # def register 144 | 145 | public 146 | def run(queue) 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 166 | end 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) 181 | end 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 198 | end 199 | end # def run 200 | 201 | private 202 | def client 203 | if @use_tooling_api 204 | @client ||= Restforce.tooling client_options 205 | else 206 | @client ||= Restforce.new client_options 207 | end 208 | end 209 | 210 | private 211 | def client_options 212 | options = { 213 | :username => @username, 214 | :password => @password.value, 215 | :security_token => @security_token.value, 216 | :client_id => @client_id, 217 | :client_secret => @client_secret.value, 218 | :timeout => @timeout 219 | } 220 | # configure the endpoint to which restforce connects to for authentication 221 | if @sfdc_instance_url && @use_test_sandbox 222 | raise ::LogStash::ConfigurationError.new("Both \"use_test_sandbox\" and \"sfdc_instance_url\" can't be set simultaneously. Please specify either \"use_test_sandbox\" or \"sfdc_instance_url\"") 223 | elsif @sfdc_instance_url 224 | options.merge!({ :host => @sfdc_instance_url }) 225 | elsif @use_test_sandbox 226 | options.merge!({ :host => "test.salesforce.com" }) 227 | end 228 | options.merge!({ :api_version => @api_version }) if @api_version 229 | return options 230 | end 231 | 232 | private 233 | def get_query() 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 257 | query_str = query.flatten.join(" ") 258 | @logger.debug? && @logger.debug("SFDC Query", :query => query_str) 259 | return query_str 260 | end 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 | 271 | private 272 | def get_field_types(obj_desc) 273 | field_types = {} 274 | obj_desc.fields.each do |f| 275 | field_types[f.name] = f.type 276 | end 277 | @logger.debug? && @logger.debug("Field types", :field_types => field_types.to_s) 278 | return field_types 279 | end 280 | 281 | private 282 | def get_all_fields 283 | return @sfdc_field_types.keys 284 | end 285 | 286 | private 287 | # From http://stackoverflow.com/a/1509957/4701287 288 | def underscore(camel_cased_word) 289 | camel_cased_word.to_s.gsub(/::/, '/'). 290 | gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). 291 | gsub(/([a-z\d])([A-Z])/,'\1_\2'). 292 | tr("-", "_"). 293 | downcase 294 | end 295 | 296 | private 297 | def format_time(string) 298 | # salesforce can use different time formats so until we have a higher 299 | # performance requirement we can just use Time.parse 300 | # otherwise it's possible to use a sequence of DateTime.strptime, for example 301 | LogStash::Timestamp.new(Time.parse(string)) 302 | end 303 | 304 | end # class LogStash::Inputs::Salesforce 305 | -------------------------------------------------------------------------------- /logstash-input-salesforce.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'logstash-input-salesforce' 3 | s.version = '3.3.0' 4 | s.licenses = ['apache-2.0'] 5 | s.summary = "Creates events based on a Salesforce SOQL query" 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 | 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 = Dir["lib/**/*","spec/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT", "vendor/jar-dependencies/**/*.jar", "vendor/jar-dependencies/**/*.rb", "VERSION", "docs/**/*"] 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-plugin-api", ">= 1.60", "<= 2.99" 22 | s.add_runtime_dependency "logstash-codec-plain" 23 | s.add_runtime_dependency "restforce", ">= 5", "< 5.2" 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 | s.add_development_dependency 'public_suffix', '>= 1.4' # required due to ruby < 2.0 29 | end 30 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/describe_apex_test_run_result_object.yml: -------------------------------------------------------------------------------- 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/v52.0/tooling/sobjects/ApexTestRunResult/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: '{"actionOverrides":[],"activateable":false,"associateEntityType":null,"associateParentEntity":null,"childRelationships":[],"compactLayoutable":false,"createable":true,"custom":false,"customSetting":false,"deepCloneable":false,"defaultImplementation":null, 81 | "deletable":true,"deprecatedAndHidden":false,"extendedBy":null,"extendsInterfaces":null,"feedEnabled":false,"fields":[{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":18,"calculated":false,"calculatedFormula":null,"cascadeDelete":false, 82 | "caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":false,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":true,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0, 83 | "displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":true, 84 | "inlineHelpText":null,"label":"ApexTestRunResult ID","length":18,"mask":null,"maskType":null,"name":"Id","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0, 85 | "queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"tns:ID","sortable":true,"type":"id", 86 | "unique":false,"updateable":false,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":18,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":true,"compoundFieldName":null, 87 | "controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"encrypted":false,"externalId":false, 88 | "extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Apex Job ID","length":18,"mask":null, 89 | "maskType":null,"name":"AsyncApexJobId","nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":["AsyncApexJob"], 90 | "relationshipName":"AsyncApexJob","relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"tns:ID","sortable":true,"type":"reference","unique":true,"updateable":true, 91 | "writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":18,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null, 92 | "createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null, 93 | "filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"User ID","length":18,"mask":null,"maskType":null,"name":"UserId", 94 | "nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":["User"],"relationshipName":"User","relationshipOrder":null, 95 | "restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"tns:ID","sortable":true,"type":"reference","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false, 96 | "autoNumber":false,"byteLength":765,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null, 97 | "defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false, 98 | "groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Name of the job","length":255,"mask":null,"maskType":null,"name":"JobName","nameField":false,"namePointing":false,"nillable":true,"permissionable":false, 99 | "picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0, 100 | "searchPrefilterable":false,"soapType":"xsd:string","sortable":true,"type":"string","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":false,"aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false, 101 | "calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":false,"defaultValueFormula":null,"defaultedOnCreate":true,"dependentPicklist":false, 102 | "deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false, 103 | "htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"allTests","length":0,"mask":null,"maskType":null,"name":"IsAllTests","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[], 104 | "polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false, 105 | "soapType":"xsd:boolean","sortable":true,"type":"boolean","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":765,"calculated":false,"calculatedFormula":null, 106 | "cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0, 107 | "displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false, 108 | "inlineHelpText":null,"label":"Client that kicked off the test run","length":255,"mask":null,"maskType":null,"name":"Source","nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false, 109 | "precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:string","sortable":true, 110 | "type":"string","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false, 111 | "compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false, 112 | "encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":false,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null, 113 | "label":"Start time of the test run","length":0,"mask":null,"maskType":null,"name":"StartTime","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false, 114 | "referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:dateTime","sortable":true,"type":"datetime","unique":false, 115 | "updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null, 116 | "createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null, 117 | "filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":false,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"End time of the test run","length":0,"mask":null,"maskType":null, 118 | "name":"EndTime","nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null, 119 | "relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:dateTime","sortable":true,"type":"datetime","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true, 120 | "aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null, 121 | "defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":9,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null, 122 | "formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Time(ms) actually spent running tests","length":0,"mask":null,"maskType":null,"name":"TestTime","nameField":false, 123 | "namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null, 124 | "restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:int","sortable":true,"type":"int","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false, 125 | "autoNumber":false,"byteLength":765,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null, 126 | "defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":0,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false, 127 | "groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Status of the test run","length":255,"mask":null,"maskType":null,"name":"Status","nameField":false,"namePointing":false,"nillable":false, 128 | "permissionable":false,"picklistValues":[{"active":true,"defaultValue":false,"label":"Queued","validFor":null,"value":"Queued"},{"active":true,"defaultValue":false,"label":"Processing","validFor":null,"value":"Processing"},{"active":true,"defaultValue":false, 129 | "label":"Aborted","validFor":null,"value":"Aborted"},{"active":true,"defaultValue":false,"label":"Completed","validFor":null,"value":"Completed"},{"active":true,"defaultValue":false,"label":"Failed","validFor":null,"value":"Failed"},{"active":true, 130 | "defaultValue":false,"label":"Preparing","validFor":null,"value":"Preparing"},{"active":true,"defaultValue":false,"label":"Holding","validFor":null,"value":"Holding"}],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false, 131 | "referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":true,"scale":0,"searchPrefilterable":false,"soapType":"xsd:string","sortable":true,"type":"picklist","unique":false, 132 | "updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null, 133 | "controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":9,"displayLocationInDecimal":false,"encrypted":false,"externalId":false, 134 | "extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Number of classes enqueued in this test run", 135 | "length":0,"mask":null,"maskType":null,"name":"ClassesEnqueued","nameField":false,"namePointing":false,"nillable":false,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null, 136 | "referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:int","sortable":true,"type":"int","unique":false,"updateable":true,"writeRequiresMasterRead":false}, 137 | {"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null, 138 | "defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":9,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null, 139 | "formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Number of classes completed in this test run","length":0,"mask":null,"maskType":null,"name":"ClassesCompleted", 140 | "nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null, 141 | "restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:int","sortable":true,"type":"int","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false, 142 | "autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null, 143 | "defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":9,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false, 144 | "groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Number of methods enqueued in this test run","length":0,"mask":null,"maskType":null,"name":"MethodsEnqueued","nameField":false,"namePointing":false, 145 | "nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false, 146 | "restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:int","sortable":true,"type":"int","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":0, 147 | "calculated":false,"calculatedFormula":null,"cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false, 148 | "deprecatedAndHidden":false,"digits":9,"displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false, 149 | "htmlFormatted":false,"idLookup":false,"inlineHelpText":null,"label":"Number of methods completed in this test run","length":0,"mask":null,"maskType":null,"name":"MethodsCompleted","nameField":false,"namePointing":false,"nillable":true,"permissionable":false, 150 | "picklistValues":[],"polymorphicForeignKey":false,"precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0, 151 | "searchPrefilterable":false,"soapType":"xsd:int","sortable":true,"type":"int","unique":false,"updateable":true,"writeRequiresMasterRead":false},{"aggregatable":true,"aiPredictionField":false,"autoNumber":false,"byteLength":0,"calculated":false,"calculatedFormula":null, 152 | "cascadeDelete":false,"caseSensitive":false,"compoundFieldName":null,"controllerName":null,"createable":true,"custom":false,"defaultValue":null,"defaultValueFormula":null,"defaultedOnCreate":false,"dependentPicklist":false,"deprecatedAndHidden":false,"digits":9, 153 | "displayLocationInDecimal":false,"encrypted":false,"externalId":false,"extraTypeInfo":null,"filterable":true,"filteredLookupInfo":null,"formulaTreatNullNumberAsZero":false,"groupable":true,"highScaleNumber":false,"htmlFormatted":false,"idLookup":false, 154 | "inlineHelpText":null,"label":"Number of methods failed in this test run","length":0,"mask":null,"maskType":null,"name":"MethodsFailed","nameField":false,"namePointing":false,"nillable":true,"permissionable":false,"picklistValues":[],"polymorphicForeignKey":false, 155 | "precision":0,"queryByDistance":false,"referenceTargetField":null,"referenceTo":[],"relationshipName":null,"relationshipOrder":null,"restrictedDelete":false,"restrictedPicklist":false,"scale":0,"searchPrefilterable":false,"soapType":"xsd:int","sortable":true, 156 | "type":"int","unique":false,"updateable":true,"writeRequiresMasterRead":false}],"hasSubtypes":false,"implementedBy":null,"implementsInterfaces":null,"isInterface":false,"isSubtype":false,"keyPrefix":"05m","label":"Apex Test Run Result", 157 | "labelPlural":"Apex Test Run Result","layoutable":false,"listviewable":null,"lookupLayoutable":null,"mergeable":false,"mruEnabled":false,"name":"ApexTestRunResult","namedLayoutInfos":[],"networkScopeFieldName":null,"queryable":true,"recordTypeInfos":[], 158 | "replicateable":false,"retrieveable":true,"searchLayoutable":false,"searchable":false,"sobjectDescribeOption":"FULL","supportedScopes":[{"label":"All apex test run results","name":"everything"}],"triggerable":false,"undeletable":false,"updateable":true, 159 | "urls":{"rowTemplate":"/services/data/v52.0/tooling/sobjects/ApexTestRunResult/{ID}","describe":"/services/data/v52.0/tooling/sobjects/ApexTestRunResult/describe","sobject":"/services/data/v52.0/tooling/sobjects/ApexTestRunResult"}}' 160 | http_version: 161 | recorded_at: Thu, 27 Aug 2015 21:31:30 GMT 162 | recorded_with: VCR 2.9.3 163 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/describe_lead_object.yml: -------------------------------------------------------------------------------- 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 88 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/load_some_lead_objects.yml: -------------------------------------------------------------------------------- 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%27%25@elastic.co%27 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 125 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/load_some_lead_objects_order_by_lastmodifieddate.yml: -------------------------------------------------------------------------------- 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%20Email%20LIKE%20%27%25@elastic.co%27%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.","LastModifiedDate":"2025-05-03T10:07:54Z"},{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Grand","FirstName":"Adrien","Salutation":"Dr.","LastModifiedDate":"2025-05-03T13:52:09Z"},{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Hardy","FirstName":"Alan","Salutation":"Overlord","LastModifiedDate":"2025-05-07T14:32:17Z"}]}' 122 | http_version: 123 | recorded_at: Thu, 27 Aug 2015 23:57:45 GMT 124 | recorded_with: VCR 2.9.3 125 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/load_some_lead_objects_twice.yml: -------------------------------------------------------------------------------- 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%27%25@elastic.co%27 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 | - request: 125 | method: get 126 | uri: https://eu2.salesforce.com/services/data/v26.0/query?q=SELECT%20Id,IsDeleted,LastName,FirstName,Salutation%20FROM%20Lead%20WHERE%20Email%20LIKE%20%27%25@elastic.co%27 127 | body: 128 | encoding: US-ASCII 129 | string: '' 130 | headers: 131 | User-Agent: 132 | - Faraday v0.9.1 133 | Authorization: 134 | - OAuth xxx 135 | Accept-Encoding: 136 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 137 | Accept: 138 | - '*/*' 139 | response: 140 | status: 141 | code: 200 142 | message: OK 143 | headers: 144 | Date: 145 | - Thu, 27 Aug 2015 23:57:45 GMT 146 | Set-Cookie: 147 | - BrowserId=xxxx;Path=/;Domain=.salesforce.com;Expires=Mon, 26-Oct-2015 23:57:45 GMT 148 | Expires: 149 | - Thu, 01 Jan 1970 00:00:00 GMT 150 | Sforce-Limit-Info: 151 | - api-usage=211063/451000 152 | Content-Type: 153 | - application/json;charset=UTF-8 154 | Transfer-Encoding: 155 | - chunked 156 | body: 157 | encoding: UTF-8 158 | string: '{"totalSize":5,"done":true,"records":[{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Alva","FirstName":"Lenard","Salutation":"Mr."},{"attributes":{"type":"Lead","url":"/services/data/v26.0/sobjects/Lead/xxxx"},"Id":"xxxx","IsDeleted":false,"LastName":"Byron","FirstName":"Jerri","Salutation":"Dr."},{"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"}]}' 159 | http_version: 160 | recorded_at: Thu, 27 Aug 2015 23:57:45 GMT 161 | recorded_with: VCR 2.9.3 162 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter.yml: -------------------------------------------------------------------------------- 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%27%25@elastic.co%27%20AND%20LastModifiedDate%20%3E%202025-05-07T14:32:17Z 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 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate.yml: -------------------------------------------------------------------------------- 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%20Email%20LIKE%20%27%25@elastic.co%27%20AND%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 124 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/load_some_lead_objects_with_lastmodifieddate_filter_order_by_lastmodifieddate_no_filters.yml: -------------------------------------------------------------------------------- 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 124 | -------------------------------------------------------------------------------- /spec/fixtures/vcr_cassettes/login_into_mydomain.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://my-domain.my.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://my-domain.my.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://my-domain.my.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 88 | -------------------------------------------------------------------------------- /spec/inputs/salesforce_spec.rb: -------------------------------------------------------------------------------- 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" => ::LogStash::Util::Password.new("secret-key"), 12 | "username" => "", 13 | "password" => ::LogStash::Util::Password.new("secret-password"), 14 | "security_token" => ::LogStash::Util::Password.new("secret-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" => ::LogStash::Util::Password.new("secret-key"), 39 | "username" => "", 40 | "password" => ::LogStash::Util::Password.new("secret-password"), 41 | "security_token" => ::LogStash::Util::Password.new("secret-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" => ::LogStash::Util::Password.new("secret-key"), 71 | "username" => "", 72 | "password" => ::LogStash::Util::Password.new("secret-password"), 73 | "security_token" => ::LogStash::Util::Password.new("secret-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" => ::LogStash::Util::Password.new("secret-key"), 98 | "username" => "", 99 | "password" => ::LogStash::Util::Password.new("secret-password"), 100 | "security_token" => ::LogStash::Util::Password.new("secret-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 | 123 | context "use sfdc instance url" do 124 | VCR.configure do |config| 125 | config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes') 126 | config.hook_into :webmock 127 | config.before_record do |i| 128 | if i.response.body.encoding.to_s == 'ASCII-8BIT' 129 | # required because sfdc doesn't send back the content encoding and it 130 | # confuses the yaml parser 131 | json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8")) 132 | i.response.body = json_body.to_json 133 | i.response.update_content_length_header 134 | end 135 | end 136 | end 137 | let(:options) do 138 | { 139 | "client_id" => "", 140 | "client_secret" => ::LogStash::Util::Password.new("secret-key"), 141 | "username" => "", 142 | "password" => ::LogStash::Util::Password.new("secret-password"), 143 | "security_token" => ::LogStash::Util::Password.new("secret-token"), 144 | "sfdc_instance_url" => "my-domain.my.salesforce.com", 145 | "sfdc_object_name" => "Lead" 146 | } 147 | end 148 | let (:input) { LogStash::Inputs::Salesforce.new(options) } 149 | let(:expected_fields_result) { ["Id", "IsDeleted", 150 | "LastName", "FirstName", "Salutation"] } 151 | let(:expected_types_result) { [["FirstName", "string"], 152 | ["Id", "id"], 153 | ["IsDeleted", "boolean"], 154 | ["LastName", "string"], 155 | ["Salutation", "picklist"]] } 156 | subject { input } 157 | it "logs into sfdc instance url" do 158 | VCR.use_cassette("login into mydomain", :decode_compressed_response => true) do 159 | subject.register 160 | expect(subject.instance_variable_get(:@sfdc_field_types)).to match_array(expected_types_result) 161 | expect(subject.instance_variable_get(:@sfdc_fields)).to match_array(expected_fields_result) 162 | end 163 | end 164 | context "...but not use_test_sandbox" do 165 | let(:options) do 166 | { 167 | "client_id" => "", 168 | "client_secret" => ::LogStash::Util::Password.new("secret-key"), 169 | "username" => "", 170 | "password" => ::LogStash::Util::Password.new("secret-password"), 171 | "security_token" => ::LogStash::Util::Password.new("secret-token"), 172 | "sfdc_instance_url" => "my-domain.my.salesforce.com", 173 | "sfdc_object_name" => "Lead", 174 | "use_test_sandbox" => true 175 | } 176 | end 177 | let (:input) { LogStash::Inputs::Salesforce.new(options) } 178 | subject { input } 179 | it "should raise a LogStash::ConfigurationError" do 180 | expect { subject.register }.to raise_error(::LogStash::ConfigurationError) 181 | end 182 | end 183 | end 184 | 185 | context "use Tooling Api" do 186 | VCR.configure do |config| 187 | config.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes') 188 | config.hook_into :webmock 189 | config.before_record do |i| 190 | if i.response.body.encoding.to_s == 'ASCII-8BIT' 191 | # required because sfdc doesn't send back the content encoding and it 192 | # confuses the yaml parser 193 | json_body = JSON.load(i.response.body.encode("ASCII-8BIT").force_encoding("utf-8")) 194 | i.response.body = json_body.to_json 195 | i.response.update_content_length_header 196 | end 197 | end 198 | end 199 | let(:options) do 200 | { 201 | "api_version" => "52.0", 202 | "client_id" => "", 203 | "client_secret" => ::LogStash::Util::Password.new("secret-key"), 204 | "username" => "", 205 | "password" => ::LogStash::Util::Password.new("secret-password"), 206 | "security_token" => ::LogStash::Util::Password.new("secret-token"), 207 | "use_tooling_api" => true, 208 | "sfdc_object_name" => "ApexTestRunResult" 209 | } 210 | end 211 | let(:input) { LogStash::Inputs::Salesforce.new(options) } 212 | subject { input } 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 215 | subject.register 216 | expect(subject.send(:client).send(:api_path, "query")).to eq('/services/data/v52.0/tooling/query') 217 | end 218 | end 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 480 | end 481 | end 482 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 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 26 | --------------------------------------------------------------------------------