logstash-integration-elastic_enterprise_search 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe83963cbb7d00dffdf8b52106d2db2e34f8b5389c891a7a1db7f939657c6ed2
4
- data.tar.gz: 21be61a90719d99c958a9d4eed07a67a4d5ea22d6fbcf5cb8531a3af5e036c01
3
+ metadata.gz: 0d1202fb961af64675edfe8d9deb5a5496a6883a4ba4f73de08b8c53229a78d8
4
+ data.tar.gz: a69156a70dd56f29ad1ba16330b31e81725f03679f5ec7ed2b4bf0e6d3220ad0
5
5
  SHA512:
6
- metadata.gz: f4fece8fe3bec522750956526422d44da6b0ea1b2bcf42d01ca992f5835f67ab5e173e9543442da453f97dc68c78cf87fab8a7370925d7696e68069eeb20f16d
7
- data.tar.gz: 88ba3212c15e0f84a2023472a80b4acbb81663db628d6ef209cba3295ec4310a78b06e21c1c3a31a552ea08e249faf04a6fc8efa5f3f52998dddd1557a8b2eb6
6
+ metadata.gz: 19e16ac5f649cb599b67394558a998393adf9d84331af212d7567f614b290832669aab601378ad7ca48472f3ab6bce1167e1edfa53bdcb378ae9435413fa8964
7
+ data.tar.gz: 5ce52eb632f0897c9af62e53a02db844142b8864908da08dd6937328a635ded2c4cb11e18d0f9a1861a2f8c9aa04acb0c29d93386647e81b612ac03ec9c151b8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
- ## 2.0.0
1
+ ## 2.1.0
2
+ - Addition of Workplace Search Output plugin [#4](https://github.com/logstash-plugins/logstash-integration-elastic_enterprise_search/pull/4)
3
+ - [DOC] Updates output-elastic_app_search documentation to add an integration attribute and include the integration plugin header [#5](https://github.com/logstash-plugins/logstash-integration-elastic_enterprise_search/pull/5)
4
+
2
5
 
6
+ ## 2.0.0
3
7
  - Initial release of the Elastic EnterpriseSearch Integration Plugin, which carries the
4
8
  previous AppSearch Output plugin codebase;
5
9
  independent changelogs for previous versions can be found:
data/DEVELOPER.md CHANGED
@@ -2,4 +2,5 @@
2
2
  Elastic Enterprise Search integration for Logstash, including AppSearch and WorkplaceSearch output plugins
3
3
 
4
4
  # Dependencies
5
- * elastic-app-search
5
+ * elastic-app-search
6
+ * elastic-workplace-search
data/README.md CHANGED
@@ -97,4 +97,3 @@ Programming is not a required skill. Whatever you've seen about open source and
97
97
  It is more important to the community that you are able to contribute.
98
98
 
99
99
  For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
100
- >>>>>>> 418bfff... Rought output plugin creation
@@ -5,12 +5,44 @@ require "elastic-app-search"
5
5
  class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
6
6
  config_name "elastic_app_search"
7
7
 
8
+ # The name of the search engine you created in App Search, an information
9
+ # repository that includes the indexed document records.
10
+ # The `engine` field supports
11
+ # {logstash-ref}/event-dependent-configuration.html#sprintf[sprintf format] to
12
+ # allow the engine name to be derived from a field value from each event, for
13
+ # example `engine-%{engine_name}`.
14
+ #
15
+ # Invalid engine names cause ingestion to stop until the field value can be resolved into a valid engine name.
16
+ # This situation can happen if the interpolated field value resolves to a value without a matching engine,
17
+ # or, if the field is missing from the event and cannot be resolved at all.
8
18
  config :engine, :validate => :string, :required => true
19
+
20
+ # The hostname of the App Search API that is associated with your App Search account.
21
+ # Set this when using the https://www.elastic.co/cloud/app-search-service
9
22
  config :host, :validate => :string
23
+
24
+ # The value of the API endpoint in the form of a URL. Note: The value of the of the `path` setting will be will be appended to this URL.
25
+ # Set this when using the https://www.elastic.co/downloads/app-search
10
26
  config :url, :validate => :string
27
+
28
+ # The private API Key with write permissions. Visit the https://app.swiftype.com/as/credentials
29
+ # in the App Search dashboard to find the key associated with your account.
11
30
  config :api_key, :validate => :password, :required => true
31
+
32
+ # Where to move the value from the `@timestamp` field.
33
+ #
34
+ # All Logstash events contain a `@timestamp` field.
35
+ # App Search doesn't support fields starting with `@timestamp`, and
36
+ # by default, the `@timestamp` field will be deleted.
37
+ #
38
+ # To keep the timestamp field, set this value to the name of the field where you want `@timestamp` copied.
12
39
  config :timestamp_destination, :validate => :string
40
+
41
+ # The id for app search documents. This can be an interpolated value
42
+ # like `myapp-%{sequence_id}`. Reusing ids will cause documents to be rewritten.
13
43
  config :document_id, :validate => :string
44
+
45
+ # The path that is appended to the `url` parameter when connecting to a https://www.elastic.co/downloads/app-search
14
46
  config :path, :validate => :string, :default => "/api/as/v1/"
15
47
 
16
48
  ENGINE_WITH_SPRINTF_REGEX = /^.*%\{.+\}.*$/
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+ require "logstash/outputs/base"
3
+ require "elastic-workplace-search"
4
+
5
+ class LogStash::Outputs::ElasticWorkplaceSearch < LogStash::Outputs::Base
6
+ config_name "elastic_workplace_search"
7
+
8
+ # The value of the API endpoint in the form of a URL.
9
+ config :url, :validate => :string, :required => true
10
+
11
+ # The ID of the source you created in Workplace Search.
12
+ # The `source` field supports
13
+ # {logstash-ref}/event-dependent-configuration.html#sprintf[sprintf format] to
14
+ # allow the source ID to be derived from a field value from each event, for
15
+ # example `%{source_id}`.
16
+ #
17
+ # Invalid source IDs cause ingestion to stop until the field value can be resolved into a valid source ID.
18
+ # This situation can happen if the interpolated field value resolves to a value without a matching source,
19
+ # or, if the field is missing from the event and cannot be resolved at all.
20
+ config :source, :validate => :string, :required => true
21
+
22
+ # The source access token. Visit the source overview page in the Workplace Search dashboard
23
+ # to find the token associated with your source.
24
+ config :access_token, :validate => :password, :required => true
25
+
26
+ # Where to move the value from the `@timestamp` field.
27
+ #
28
+ # All Logstash events contain a `@timestamp` field.
29
+ # Workplace Search doesn't support fields starting with `@timestamp`, and
30
+ # by default, the `@timestamp` field will be deleted.
31
+ #
32
+ # To keep the timestamp field, set this value to the name of the field where you want `@timestamp` copied.
33
+ config :timestamp_destination, :validate => :string
34
+
35
+ # The id for workplace search documents. This can be an interpolated value
36
+ # like `myapp-%{sequence_id}`. Reusing ids will cause documents to be rewritten.
37
+ config :document_id, :validate => :string
38
+
39
+ public
40
+ def register
41
+ Elastic::WorkplaceSearch.endpoint = "#{@url.chomp('/')}/api/ws/v1/"
42
+ @client = Elastic::WorkplaceSearch::Client.new(:access_token => @access_token.value)
43
+ check_connection!
44
+ rescue Elastic::WorkplaceSearch::InvalidCredentials
45
+ raise ::LogStash::ConfigurationError.new("Failed to connect to Workplace Search. Error: 401. Please check your credentials")
46
+ rescue Elastic::WorkplaceSearch::NonExistentRecord
47
+ raise ::LogStash::ConfigurationError.new("Failed to connect to Workplace Search. Error: 404. Please check if url '#{@url}' is correct and you've created a source with ID '#{@source}'")
48
+ rescue => e
49
+ raise ::LogStash::ConfigurationError.new("Failed to connect to Workplace Search. #{e.message}")
50
+ end
51
+
52
+ public
53
+ def multi_receive(events)
54
+ # because Workplace Search has a limit of 100 documents per bulk
55
+ events.each_slice(100) do |events|
56
+ batch = format_batch(events)
57
+ if @logger.trace?
58
+ @logger.trace("Sending bulk to Workplace Search", :size => batch.size, :data => batch.inspect)
59
+ end
60
+ index(batch)
61
+ end
62
+ end
63
+
64
+ private
65
+ def format_batch(events)
66
+ events.map do |event|
67
+ doc = event.to_hash
68
+ # we need to remove default fields that start with "@"
69
+ # since Elastic Workplace Search doesn't accept them
70
+ if @timestamp_destination
71
+ doc[@timestamp_destination] = doc.delete("@timestamp")
72
+ else # delete it
73
+ doc.delete("@timestamp")
74
+ end
75
+ if @document_id
76
+ doc["id"] = event.sprintf(@document_id)
77
+ end
78
+ doc.delete("@version")
79
+ doc
80
+ end
81
+ end
82
+
83
+ def index(documents)
84
+ response = @client.index_documents(@source, documents)
85
+ report(documents, response)
86
+ rescue => e
87
+ @logger.error("Failed to execute index operation. Retrying..", :exception => e.class, :reason => e.message)
88
+ sleep(1)
89
+ retry
90
+ end
91
+
92
+ def report(documents, response)
93
+ documents.each_with_index do |document, i|
94
+ errors = response["results"][i]["errors"]
95
+ if errors.empty?
96
+ @logger.trace? && @logger.trace("Document was indexed with no errors", :document => document)
97
+ else
98
+ @logger.warn("Document failed to index. Dropping..", :document => document, :errors => errors.to_a)
99
+ end
100
+ end
101
+ end
102
+
103
+ def check_connection!
104
+ @client.list_all_permissions(@source)
105
+ end
106
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-integration-elastic_enterprise_search'
3
- s.version = '2.0.0'
3
+ s.version = '2.1.0'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "Integration with Elastic Enterprise Search - output plugins"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline "+
@@ -20,12 +20,13 @@ Gem::Specification.new do |s|
20
20
  s.metadata = {
21
21
  "logstash_plugin" => "true",
22
22
  "logstash_group" => "integration",
23
- "integration_plugins" => "logstash-output-elastic_app_search"
23
+ "integration_plugins" => "logstash-output-elastic_app_search, logstash-output-elastic_workplace_search"
24
24
  }
25
25
 
26
26
  # Gem dependencies
27
27
  s.add_runtime_dependency "logstash-core-plugin-api", "~> 2.0"
28
28
  s.add_runtime_dependency "logstash-codec-plain"
29
29
  s.add_runtime_dependency "elastic-app-search", '~>7.8.0'
30
+ s.add_runtime_dependency "elastic-workplace-search", '~>0.4.1'
30
31
  s.add_development_dependency "logstash-devutils"
31
32
  end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/outputs/elastic_workplace_search"
4
+ require "logstash/codecs/plain"
5
+ require "logstash/event"
6
+ require "json"
7
+ require "base64"
8
+
9
+ describe "indexing against running Workplace Search", :integration => true do
10
+
11
+ let(:url) { ENV['APPSEARCH_URL'] }
12
+ let(:auth) { Base64.strict_encode64("#{ENV['APPSEARCH_USERNAME']}:#{ENV['AS_PASSWORD']}") }
13
+ let(:source) do
14
+ response = Faraday.post(
15
+ "#{url}/ws/org/sources/form_create",
16
+ JSON.dump("service_type" => "custom", "name" => "whatever"),
17
+ "Content-Type" => "application/json",
18
+ "Accept" => "application/json",
19
+ "Authorization" => "Basic #{auth}"
20
+ )
21
+ JSON.load(response.body)
22
+ end
23
+ let(:source_id) { source.fetch("id") }
24
+
25
+ let(:config) do
26
+ {
27
+ "url" => url,
28
+ "source" => source_id,
29
+ "access_token" => source.fetch("accessToken")
30
+ }
31
+ end
32
+
33
+ subject(:workplace_search_output) { LogStash::Outputs::ElasticWorkplaceSearch.new(config) }
34
+
35
+ before(:each) { workplace_search_output.register }
36
+
37
+ describe "single event" do
38
+ let(:event) { LogStash::Event.new("message" => "an event to index") }
39
+
40
+ it "should be indexed" do
41
+ workplace_search_output.multi_receive([event])
42
+
43
+ results = Stud.try(20.times, RSpec::Expectations::ExpectationNotMetError) do
44
+ attempt_response = execute_search_call
45
+ expect(attempt_response.status).to eq(200)
46
+ parsed_resp = JSON.parse(attempt_response.body)
47
+ expect(parsed_resp.dig("meta", "page", "total_pages")).to eq(1)
48
+ parsed_resp["results"]
49
+ end
50
+ expect(results.first.fetch("message")).to eq "an event to index"
51
+ end
52
+ end
53
+
54
+ describe "multiple events" do
55
+ let(:events) { generate_events(200) } #2 times the slice size used to batch
56
+
57
+ it "all should be indexed" do
58
+ workplace_search_output.multi_receive(events)
59
+ results = Stud.try(20.times, RSpec::Expectations::ExpectationNotMetError) do
60
+ attempt_response = execute_search_call
61
+ expect(attempt_response.status).to eq(200)
62
+ parsed_resp = JSON.parse(attempt_response.body)
63
+ expect(parsed_resp.dig("meta", "page", "total_results")).to eq(200)
64
+ parsed_resp["results"]
65
+ end
66
+ expect(results.first.fetch("message")).to start_with("an event to index")
67
+ end
68
+ end
69
+
70
+ private
71
+ def execute_search_call
72
+ Faraday.post(
73
+ "#{url}/ws/org/sources/#{source_id}/documents",
74
+ nil,
75
+ "Accept" => "application/json",
76
+ "Authorization" => "Basic #{auth}"
77
+ )
78
+ end
79
+
80
+ def generate_events(num_events)
81
+ (1..num_events).map { |i| LogStash::Event.new("message" => "an event to index #{i}")}
82
+ end
83
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-integration-elastic_enterprise_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-05 00:00:00.000000000 Z
11
+ date: 2021-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 7.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 0.4.1
61
+ name: elastic-workplace-search
62
+ prerelease: false
63
+ type: :runtime
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.4.1
55
69
  - !ruby/object:Gem::Dependency
56
70
  requirement: !ruby/object:Gem::Requirement
57
71
  requirements:
@@ -82,8 +96,10 @@ files:
82
96
  - NOTICE.TXT
83
97
  - README.md
84
98
  - lib/logstash/outputs/elastic_app_search.rb
99
+ - lib/logstash/outputs/elastic_workplace_search.rb
85
100
  - logstash-integration-elastic_enterprise_search.gemspec
86
- - spec/integration/outputs/index_spec.rb
101
+ - spec/integration/outputs/elastic_app_search_spec.rb
102
+ - spec/integration/outputs/elastic_workplace_search_spec.rb
87
103
  - spec/unit/outputs/appsearch_spec.rb
88
104
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
89
105
  licenses:
@@ -91,7 +107,7 @@ licenses:
91
107
  metadata:
92
108
  logstash_plugin: 'true'
93
109
  logstash_group: integration
94
- integration_plugins: logstash-output-elastic_app_search
110
+ integration_plugins: logstash-output-elastic_app_search, logstash-output-elastic_workplace_search
95
111
  post_install_message:
96
112
  rdoc_options: []
97
113
  require_paths:
@@ -114,5 +130,6 @@ signing_key:
114
130
  specification_version: 4
115
131
  summary: Integration with Elastic Enterprise Search - output plugins
116
132
  test_files:
117
- - spec/integration/outputs/index_spec.rb
133
+ - spec/integration/outputs/elastic_app_search_spec.rb
134
+ - spec/integration/outputs/elastic_workplace_search_spec.rb
118
135
  - spec/unit/outputs/appsearch_spec.rb