logstash-integration-elastic_enterprise_search 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/DEVELOPER.md +2 -1
- data/README.md +0 -1
- data/lib/logstash/outputs/elastic_app_search.rb +32 -0
- data/lib/logstash/outputs/elastic_workplace_search.rb +106 -0
- data/logstash-integration-elastic_enterprise_search.gemspec +3 -2
- data/spec/integration/outputs/{index_spec.rb → elastic_app_search_spec.rb} +0 -0
- data/spec/integration/outputs/elastic_workplace_search_spec.rb +83 -0
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d1202fb961af64675edfe8d9deb5a5496a6883a4ba4f73de08b8c53229a78d8
|
4
|
+
data.tar.gz: a69156a70dd56f29ad1ba16330b31e81725f03679f5ec7ed2b4bf0e6d3220ad0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19e16ac5f649cb599b67394558a998393adf9d84331af212d7567f614b290832669aab601378ad7ca48472f3ab6bce1167e1edfa53bdcb378ae9435413fa8964
|
7
|
+
data.tar.gz: 5ce52eb632f0897c9af62e53a02db844142b8864908da08dd6937328a635ded2c4cb11e18d0f9a1861a2f8c9aa04acb0c29d93386647e81b612ac03ec9c151b8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
## 2.
|
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
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.
|
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
|
File without changes
|
@@ -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.
|
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-
|
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/
|
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/
|
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
|