elasticsearch 7.1.0 → 8.10.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.
@@ -6,7 +6,7 @@
6
6
  # not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing,
12
12
  # software distributed under the License is distributed on an
@@ -16,5 +16,5 @@
16
16
  # under the License.
17
17
 
18
18
  module Elasticsearch
19
- VERSION = "7.1.0"
19
+ VERSION = '8.10.0'.freeze
20
20
  end
@@ -6,7 +6,7 @@
6
6
  # not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing,
12
12
  # software distributed under the License is distributed on an
data/lib/elasticsearch.rb CHANGED
@@ -6,7 +6,7 @@
6
6
  # not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing,
12
12
  # software distributed under the License is distributed on an
@@ -15,15 +15,186 @@
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
17
 
18
- require "elasticsearch/version"
19
-
20
- require 'elasticsearch/transport'
18
+ require 'elasticsearch/version'
19
+ require 'elastic/transport'
21
20
  require 'elasticsearch/api'
22
21
 
23
22
  module Elasticsearch
24
- module Transport
25
- class Client
26
- include Elasticsearch::API
23
+ NOT_ELASTICSEARCH_WARNING = 'The client noticed that the server is not Elasticsearch and we do not support this unknown product.'.freeze
24
+ SECURITY_PRIVILEGES_VALIDATION_WARNING = 'The client is unable to verify that the server is Elasticsearch due to security privileges on the server side. Some functionality may not be compatible if the server is running an unsupported product.'.freeze
25
+ VALIDATION_WARNING = 'The client is unable to verify that the server is Elasticsearch. Some functionality may not be compatible if the server is running an unsupported product.'.freeze
26
+
27
+ # This is the stateful Elasticsearch::Client, using an instance of elastic-transport.
28
+ class Client
29
+ include Elasticsearch::API
30
+ # The default port to use if connecting using a Cloud ID.
31
+ # Updated from 9243 to 443 in client version 7.10.1
32
+ #
33
+ # @since 7.2.0
34
+ DEFAULT_CLOUD_PORT = 443
35
+
36
+ # Create a client connected to an Elasticsearch cluster.
37
+ #
38
+ # @param [Hash] arguments - initializer arguments
39
+ # @option arguments [String] :cloud_id - The Cloud ID to connect to Elastic Cloud
40
+ # @option arguments [String, Hash] :api_key Use API Key Authentication, either the base64 encoding of `id` and `api_key`
41
+ # joined by a colon as a String, or a hash with the `id` and `api_key` values.
42
+ # @option arguments [String] :opaque_id_prefix set a prefix for X-Opaque-Id when initializing the client.
43
+ # This will be prepended to the id you set before each request
44
+ # if you're using X-Opaque-Id
45
+ # @option arguments [Hash] :headers Custom HTTP Request Headers
46
+ #
47
+ def initialize(arguments = {}, &block)
48
+ @verified = false
49
+ @warned = false
50
+ @opaque_id_prefix = arguments[:opaque_id_prefix] || nil
51
+ api_key(arguments) if arguments[:api_key]
52
+ setup_cloud(arguments) if arguments[:cloud_id]
53
+ set_user_agent!(arguments) unless sent_user_agent?(arguments)
54
+ @transport = Elastic::Transport::Client.new(arguments, &block)
55
+ end
56
+
57
+ def method_missing(name, *args, &block)
58
+ if methods.include?(name)
59
+ super
60
+ elsif name == :perform_request
61
+ # The signature for perform_request is:
62
+ # method, path, params, body, headers
63
+ if (opaque_id = args[2]&.delete(:opaque_id))
64
+ headers = args[4] || {}
65
+ opaque_id = @opaque_id_prefix ? "#{@opaque_id_prefix}#{opaque_id}" : opaque_id
66
+ args[4] = headers.merge('X-Opaque-Id' => opaque_id)
67
+ end
68
+ unless @verified
69
+ verify_elasticsearch(*args, &block)
70
+ else
71
+ @transport.perform_request(*args, &block)
72
+ end
73
+ else
74
+ @transport.send(name, *args, &block)
75
+ end
76
+ end
77
+
78
+ def respond_to_missing?(method_name, *args)
79
+ @transport.respond_to?(method_name) || super
80
+ end
81
+
82
+ private
83
+
84
+ def verify_elasticsearch(*args, &block)
85
+ begin
86
+ response = @transport.perform_request(*args, &block)
87
+ rescue Elastic::Transport::Transport::Errors::Unauthorized,
88
+ Elastic::Transport::Transport::Errors::Forbidden,
89
+ Elastic::Transport::Transport::Errors::RequestEntityTooLarge => e
90
+ warn(SECURITY_PRIVILEGES_VALIDATION_WARNING)
91
+ @verified = true
92
+ raise e
93
+ rescue Elastic::Transport::Transport::Error => e
94
+ unless @warned
95
+ warn(VALIDATION_WARNING)
96
+ @warned = true
97
+ end
98
+ raise e
99
+ rescue StandardError => e
100
+ warn(VALIDATION_WARNING)
101
+ raise e
102
+ end
103
+ raise Elasticsearch::UnsupportedProductError unless response.headers['x-elastic-product'] == 'Elasticsearch'
104
+ @verified = true
105
+ response
106
+ end
107
+
108
+ def setup_cloud_host(cloud_id, user, password, port)
109
+ name = cloud_id.split(':')[0]
110
+ cloud_url, elasticsearch_instance = Base64.decode64(cloud_id.gsub("#{name}:", '')).split('$')
111
+
112
+ if cloud_url.include?(':')
113
+ url, port = cloud_url.split(':')
114
+ host = "#{elasticsearch_instance}.#{url}"
115
+ else
116
+ host = "#{elasticsearch_instance}.#{cloud_url}"
117
+ port ||= DEFAULT_CLOUD_PORT
118
+ end
119
+ [{ scheme: 'https', user: user, password: password, host: host, port: port.to_i }]
120
+ end
121
+
122
+ def api_key(arguments)
123
+ api_key = if arguments[:api_key].is_a? Hash
124
+ encode(arguments[:api_key])
125
+ else
126
+ arguments[:api_key]
127
+ end
128
+ arguments.delete(:user)
129
+ arguments.delete(:password)
130
+ authorization = { 'Authorization' => "ApiKey #{api_key}" }
131
+ if (headers = arguments.dig(:transport_options, :headers))
132
+ headers.merge!(authorization)
133
+ else
134
+ arguments[:transport_options] ||= {}
135
+ arguments[:transport_options].merge!({ headers: authorization })
136
+ end
137
+ end
138
+
139
+ def setup_cloud(arguments)
140
+ arguments[:hosts] = setup_cloud_host(
141
+ arguments[:cloud_id],
142
+ arguments[:user],
143
+ arguments[:password],
144
+ arguments[:port]
145
+ )
146
+ end
147
+
148
+ # Encode credentials for the Authorization Header
149
+ # Credentials is the base64 encoding of id and api_key joined by a colon
150
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html
151
+ def encode(api_key)
152
+ Base64.strict_encode64([api_key[:id], api_key[:api_key]].join(':'))
153
+ end
154
+
155
+ def elasticsearch_validation_request
156
+ @transport.perform_request('GET', '/')
157
+ end
158
+
159
+ def sent_user_agent?(arguments)
160
+ return unless (headers = arguments&.[](:transport_options)&.[](:headers))
161
+ !!headers.keys.detect { |h| h =~ /user-?_?agent/ }
162
+ end
163
+
164
+ def set_user_agent!(arguments)
165
+ user_agent = [
166
+ "elasticsearch-ruby/#{Elasticsearch::VERSION}",
167
+ "elastic-transport-ruby/#{Elastic::Transport::VERSION}",
168
+ "RUBY_VERSION: #{RUBY_VERSION}"
169
+ ]
170
+ if RbConfig::CONFIG && RbConfig::CONFIG['host_os']
171
+ user_agent << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}"
172
+ end
173
+ arguments[:transport_options] ||= {}
174
+ arguments[:transport_options][:headers] ||= {}
175
+ arguments[:transport_options][:headers].merge!({ user_agent: user_agent.join('; ')})
27
176
  end
28
177
  end
178
+
179
+ # Error class for when we detect an unsupported version of Elasticsearch
180
+ class UnsupportedProductError < StandardError
181
+ def initialize(message = NOT_ELASTICSEARCH_WARNING)
182
+ super(message)
183
+ end
184
+ end
185
+ end
186
+
187
+ # Helper for the meta-header value for Cloud
188
+ module Elastic
189
+ # If the version is X.X.X.pre/alpha/beta, use X.X.Xp for the meta-header:
190
+ def self.client_meta_version
191
+ regexp = /^([0-9]+\.[0-9]+\.[0-9]+)\.?([a-z0-9.-]+)?$/
192
+ match = Elasticsearch::VERSION.match(regexp)
193
+ return "#{match[1]}p" if match[2]
194
+
195
+ Elasticsearch::VERSION
196
+ end
197
+
198
+ # Constant for elastic-transport meta-header
199
+ ELASTICSEARCH_SERVICE_VERSION = [:es, client_meta_version].freeze
29
200
  end
@@ -0,0 +1,94 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ ELASTICSEARCH_URL = ENV['TEST_ES_SERVER'] || "http://localhost:#{(ENV['PORT'] || 9200)}"
18
+ raise URI::InvalidURIError unless ELASTICSEARCH_URL =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
19
+
20
+ require 'spec_helper'
21
+
22
+ context 'Elasticsearch client' do
23
+ let(:client) do
24
+ Elasticsearch::Client.new(host: ELASTICSEARCH_URL, user: 'elastic', password: 'changeme')
25
+ end
26
+ let(:index) { 'tvs' }
27
+
28
+ after do
29
+ client.indices.delete(index: index)
30
+ end
31
+
32
+ context 'escaping spaces in ids' do
33
+ it 'escapes spaces for id when using index' do
34
+ response = client.index(index: index, id: 'a test 1', body: { name: 'A test 1' }, refresh: true)
35
+ expect(response.body['_id']).to eq 'a test 1'
36
+
37
+ response = client.search(index: index)
38
+ expect(response.body['hits']['hits'].first['_id']).to eq 'a test 1'
39
+
40
+ # Raises exception, _id is unrecognized
41
+ expect do
42
+ client.index(index: index, _id: 'a test 2', body: { name: 'A test 2' })
43
+ end.to raise_exception Elastic::Transport::Transport::Errors::BadRequest
44
+
45
+ # Raises exception, id is a query parameter
46
+ expect do
47
+ client.index(index: index, body: { name: 'A test 3', _id: 'a test 3' })
48
+ end.to raise_exception Elastic::Transport::Transport::Errors::BadRequest
49
+ end
50
+
51
+ it 'escapes spaces for id when using create' do
52
+ # Works with create
53
+ response = client.create(index: index, id: 'a test 4', body: { name: 'A test 4' })
54
+ expect(response.body['_id']).to eq 'a test 4'
55
+ end
56
+
57
+ it 'escapes spaces for id when using bulk' do
58
+ body = [
59
+ { create: { _index: index, _id: 'a test 5', data: { name: 'A test 5' } } }
60
+ ]
61
+ expect(client.bulk(body: body, refresh: true).status).to eq 200
62
+
63
+ response = client.search(index: index)
64
+ expect(
65
+ response.body['hits']['hits'].select { |a| a['_id'] == 'a test 5' }.size
66
+ ).to eq 1
67
+ end
68
+ end
69
+
70
+ context 'it doesnae escape plus signs in id' do
71
+ it 'escapes spaces for id when using index' do
72
+ response = client.index(index: index, id: 'a+test+1', body: { name: 'A test 1' })
73
+ expect(response.body['_id']).to eq 'a+test+1'
74
+ end
75
+
76
+ it 'escapes spaces for id when using create' do
77
+ # Works with create
78
+ response = client.create(index: index, id: 'a+test+2', body: { name: 'A test 2' })
79
+ expect(response.body['_id']).to eq 'a+test+2'
80
+ end
81
+
82
+ it 'escapes spaces for id when using bulk' do
83
+ body = [
84
+ { create: { _index: index, _id: 'a+test+3', data: { name: 'A test 3' } } }
85
+ ]
86
+ expect(client.bulk(body: body, refresh: true).status).to eq 200
87
+
88
+ response = client.search(index: index)
89
+ expect(
90
+ response.body['hits']['hits'].select { |a| a['_id'] == 'a+test+3' }.size
91
+ ).to eq 1
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,63 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ ELASTICSEARCH_URL = ENV['TEST_ES_SERVER'] || "http://localhost:#{(ENV['PORT'] || 9200)}"
18
+ raise URI::InvalidURIError unless ELASTICSEARCH_URL =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
19
+
20
+ require 'spec_helper'
21
+ require 'logger'
22
+
23
+ context 'Elasticsearch client' do
24
+ let(:logger) { Logger.new($stderr) }
25
+
26
+ let(:client) do
27
+ Elasticsearch::Client.new(
28
+ host: ELASTICSEARCH_URL,
29
+ logger: logger,
30
+ user: 'elastic',
31
+ password: 'changeme'
32
+ )
33
+ end
34
+
35
+ context 'Integrates with elasticsearch API' do
36
+ it 'should perform the API methods' do
37
+ expect do
38
+ # Index a document
39
+ client.index(index: 'test-index', id: '1', body: { title: 'Test' })
40
+
41
+ # Refresh the index
42
+ client.indices.refresh(index: 'test-index')
43
+
44
+ # Search
45
+ response = client.search(index: 'test-index', body: { query: { match: { title: 'test' } } })
46
+
47
+ expect(response['hits']['total']['value']).to eq 1
48
+ expect(response['hits']['hits'][0]['_source']['title']).to eq 'Test'
49
+
50
+ # Delete the index
51
+ client.indices.delete(index: 'test-index')
52
+ end.not_to raise_error
53
+ end
54
+ end
55
+
56
+ context 'Reports the right meta header' do
57
+ it 'Reports es service name and gem versio' do
58
+ headers = client.transport.connections.first.connection.headers
59
+ version = Class.new.extend(Elastic::Transport::MetaHeader).send(:client_meta_version, Elasticsearch::VERSION)
60
+ expect(headers['x-elastic-client-meta']).to match /^es=#{version}/
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,211 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ ELASTICSEARCH_URL = ENV['TEST_ES_SERVER'] || "http://localhost:#{(ENV['PORT'] || 9200)}"
18
+ raise URI::InvalidURIError unless ELASTICSEARCH_URL =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
19
+
20
+ require 'elasticsearch/helpers/bulk_helper'
21
+ require 'spec_helper'
22
+ require 'tempfile'
23
+
24
+ context 'Elasticsearch client helpers' do
25
+ context 'Bulk helper' do
26
+ let(:client) do
27
+ Elasticsearch::Client.new(
28
+ host: ELASTICSEARCH_URL,
29
+ user: 'elastic',
30
+ password: 'changeme'
31
+ )
32
+ end
33
+ let(:index) { 'bulk_animals' }
34
+ let(:params) { { refresh: 'wait_for' } }
35
+ let(:bulk_helper) { Elasticsearch::Helpers::BulkHelper.new(client, index, params) }
36
+ let(:docs) do
37
+ [
38
+ { scientific_name: 'Lama guanicoe', name:'Guanaco' },
39
+ { scientific_name: 'Tayassu pecari', name:'White-lipped peccary' },
40
+ { scientific_name: 'Snycerus caffer', name:'Buffalo, african' },
41
+ { scientific_name: 'Coluber constrictor', name:'Snake, racer' },
42
+ { scientific_name: 'Thalasseus maximus', name:'Royal tern' },
43
+ { scientific_name: 'Centrocercus urophasianus', name:'Hen, sage' },
44
+ { scientific_name: 'Sitta canadensis', name:'Nuthatch, red-breasted' },
45
+ { scientific_name: 'Aegypius tracheliotus', name:'Vulture, lappet-faced' },
46
+ { scientific_name: 'Bucephala clangula', name:'Common goldeneye' },
47
+ { scientific_name: 'Felis pardalis', name:'Ocelot' }
48
+ ]
49
+ end
50
+
51
+ after do
52
+ client.indices.delete(index: index, ignore: 404)
53
+ end
54
+
55
+ it 'Ingests documents' do
56
+ response = bulk_helper.ingest(docs)
57
+ expect(response).to be_an_instance_of Elasticsearch::API::Response
58
+ expect(response.status).to eq(200)
59
+ expect(response['items'].map { |a| a['index']['status'] }.uniq.first).to eq 201
60
+ end
61
+
62
+ it 'Updates documents' do
63
+ docs = [
64
+ { scientific_name: 'Otocyon megalotos', name: 'Bat-eared fox' },
65
+ { scientific_name: 'Herpestes javanicus', name: 'Small Indian mongoose' }
66
+ ]
67
+ bulk_helper.ingest(docs)
68
+ # Get the ingested documents, add id and modify them to update them:
69
+ animals = client.search(index: index)['hits']['hits']
70
+ # Add id to each doc
71
+ docs = animals.map { |animal| animal['_source'].merge({'id' => animal['_id'] }) }
72
+ docs.map { |doc| doc['scientific_name'].upcase! }
73
+ response = bulk_helper.update(docs)
74
+ expect(response.status).to eq(200)
75
+ expect(response['items'].map { |i| i['update']['result'] }.uniq.first).to eq('updated')
76
+ end
77
+
78
+ it 'Deletes documents' do
79
+ response = bulk_helper.ingest(docs)
80
+ ids = response.body['items'].map { |a| a['index']['_id'] }
81
+ response = bulk_helper.delete(ids)
82
+ expect(response.status).to eq 200
83
+ expect(response['items'].map { |item| item['delete']['result'] }.uniq.first).to eq('deleted')
84
+ expect(client.count(index: index)['count']).to eq(0)
85
+ end
86
+
87
+ it 'Ingests documents and yields response and docs' do
88
+ slice = 2
89
+ response = bulk_helper.ingest(docs, {slice: slice}) do |response, docs|
90
+ expect(response).to be_an_instance_of Elasticsearch::API::Response
91
+ expect(docs.count).to eq slice
92
+ end
93
+ end
94
+
95
+ context 'JSON File helper' do
96
+ let(:file) { Tempfile.new('test-data.json') }
97
+ let(:json) do
98
+ json = <<~JSON
99
+ [
100
+ {
101
+ "character_name": "Anallese Lonie",
102
+ "species": "mouse",
103
+ "catchphrase": "Seamless regional definition",
104
+ "favorite_food": "pizza"
105
+ },
106
+ {
107
+ "character_name": "Janey Davidovsky",
108
+ "species": "cat",
109
+ "catchphrase": "Down-sized responsive pricing structure",
110
+ "favorite_food": "pizza"
111
+ },
112
+ {
113
+ "character_name": "Morse Mountford",
114
+ "species": "cat",
115
+ "catchphrase": "Ameliorated modular data-warehouse",
116
+ "favorite_food": "carrots"
117
+ },
118
+ {
119
+ "character_name": "Saundra Kauble",
120
+ "species": "dog",
121
+ "catchphrase": "Synchronised 24/7 support",
122
+ "favorite_food": "carrots"
123
+ },
124
+ {
125
+ "character_name": "Kain Viggars",
126
+ "species": "cat",
127
+ "catchphrase": "Open-architected asymmetric circuit",
128
+ "favorite_food": "carrots"
129
+ }
130
+ ]
131
+ JSON
132
+ end
133
+
134
+ before do
135
+ file.write(json)
136
+ file.rewind
137
+ end
138
+
139
+ after do
140
+ file.close
141
+ file.unlink
142
+ end
143
+
144
+ it 'Ingests a JSON file' do
145
+ response = bulk_helper.ingest_json(file)
146
+
147
+ expect(response).to be_an_instance_of Elasticsearch::API::Response
148
+ expect(response.status).to eq(200)
149
+ end
150
+
151
+ context 'with data not in root of JSON file' do
152
+ let(:json) do
153
+ json = <<~JSON
154
+ {
155
+ "field": "value",
156
+ "status": 200,
157
+ "data": {
158
+ "items": [
159
+ {
160
+ "character_name": "Anallese Lonie",
161
+ "species": "mouse",
162
+ "catchphrase": "Seamless regional definition",
163
+ "favorite_food": "pizza"
164
+ },
165
+ {
166
+ "character_name": "Janey Davidovsky",
167
+ "species": "cat",
168
+ "catchphrase": "Down-sized responsive pricing structure",
169
+ "favorite_food": "pizza"
170
+ },
171
+ {
172
+ "character_name": "Morse Mountford",
173
+ "species": "cat",
174
+ "catchphrase": "Ameliorated modular data-warehouse",
175
+ "favorite_food": "carrots"
176
+ },
177
+ {
178
+ "character_name": "Saundra Kauble",
179
+ "species": "dog",
180
+ "catchphrase": "Synchronised 24/7 support",
181
+ "favorite_food": "carrots"
182
+ },
183
+ {
184
+ "character_name": "Kain Viggars",
185
+ "species": "cat",
186
+ "catchphrase": "Open-architected asymmetric circuit",
187
+ "favorite_food": "carrots"
188
+ }
189
+ ]
190
+ }
191
+ }
192
+ JSON
193
+ end
194
+
195
+ it 'Ingests a JSON file passing keys as Array' do
196
+ response = bulk_helper.ingest_json(file, { keys: ['data', 'items'] })
197
+ expect(response).to be_an_instance_of Elasticsearch::API::Response
198
+ expect(response.status).to eq(200)
199
+ expect(response['items'].map { |a| a['index']['status'] }.uniq.first).to eq 201
200
+ end
201
+
202
+ it 'Ingests a JSON file passing keys as String' do
203
+ response = bulk_helper.ingest_json(file, { keys: 'data,items' })
204
+ expect(response).to be_an_instance_of Elasticsearch::API::Response
205
+ expect(response.status).to eq(200)
206
+ expect(response['items'].map { |a| a['index']['status'] }.uniq.first).to eq 201
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end