elastic-app-search 7.4.0 → 7.4.1
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/README.md +287 -20
- data/lib/elastic/app-search/client.rb +16 -2
- data/lib/elastic/app-search/client/analytics.rb +25 -0
- data/lib/elastic/app-search/client/click.rb +15 -0
- data/lib/elastic/app-search/client/credentials.rb +35 -0
- data/lib/elastic/app-search/client/curations.rb +35 -0
- data/lib/elastic/app-search/client/engines.rb +2 -0
- data/lib/elastic/app-search/client/logs.rb +15 -0
- data/lib/elastic/app-search/client/schema.rb +20 -0
- data/lib/elastic/app-search/client/synonyms.rb +35 -0
- data/lib/elastic/app-search/version.rb +1 -1
- data/spec/analytics_spec.rb +82 -0
- data/spec/click_spec.rb +30 -0
- data/spec/client_spec.rb +8 -452
- data/spec/config_helper.rb +4 -0
- data/spec/credentials_spec.rb +75 -0
- data/spec/curations_spec.rb +82 -0
- data/spec/documents_spec.rb +159 -0
- data/spec/engines_spec.rb +74 -0
- data/spec/logs_spec.rb +33 -0
- data/spec/query_suggestion_spec.rb +37 -0
- data/spec/schema_spec.rb +23 -0
- data/spec/search_settings_spec.rb +65 -0
- data/spec/search_spec.rb +96 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/synonyms_spec.rb +65 -0
- metadata +33 -2
data/spec/config_helper.rb
CHANGED
@@ -3,6 +3,10 @@ module ConfigHelper
|
|
3
3
|
ENV.fetch('AS_API_KEY', 'API_KEY')
|
4
4
|
end
|
5
5
|
|
6
|
+
def ConfigHelper.get_as_admin_key
|
7
|
+
ENV.fetch('AS_ADMIN_KEY', 'ADMIN_KEY')
|
8
|
+
end
|
9
|
+
|
6
10
|
def ConfigHelper.get_as_host_identifier
|
7
11
|
ENV['AS_ACCOUNT_HOST_KEY'] || ENV['AS_HOST_IDENTIFIER'] || 'ACCOUNT_HOST_KEY'
|
8
12
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
describe Elastic::AppSearch::Client::Credentials do
|
4
|
+
include_context 'App Search Admin Credentials'
|
5
|
+
include_context 'Test Engine'
|
6
|
+
|
7
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
8
|
+
let(:key_name) { "spec-key-#{SecureRandom.hex}" }
|
9
|
+
let(:api_key) do
|
10
|
+
{
|
11
|
+
:name => key_name,
|
12
|
+
:type => 'private',
|
13
|
+
:read => true,
|
14
|
+
:write => false,
|
15
|
+
:access_all_engines => false,
|
16
|
+
:engines => [
|
17
|
+
engine_name
|
18
|
+
]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
context '#create_credential' do
|
23
|
+
after { client.destroy_credential(key_name) }
|
24
|
+
subject { client.create_credential(api_key) }
|
25
|
+
|
26
|
+
it 'will create an API Key' do
|
27
|
+
expect(subject['name']).to(eq(key_name))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context '#get_credential' do
|
32
|
+
after { client.destroy_credential(key_name) }
|
33
|
+
before { client.create_credential(api_key) }
|
34
|
+
subject { client.get_credential(key_name) }
|
35
|
+
|
36
|
+
it 'will retrieve an API Key' do
|
37
|
+
expect(subject['name']).to(eq(key_name))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#update_credential' do
|
42
|
+
let(:updated_api_key) do
|
43
|
+
api_key['write'] = true
|
44
|
+
api_key
|
45
|
+
end
|
46
|
+
|
47
|
+
before { client.create_credential(api_key) }
|
48
|
+
after { client.destroy_credential(key_name) }
|
49
|
+
subject { client.update_credential(key_name, updated_api_key) }
|
50
|
+
|
51
|
+
it 'will update an API Key' do
|
52
|
+
expect(subject['name']).to(eq(key_name))
|
53
|
+
expect(subject['write']).to(eq(true))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#list_credentials' do
|
58
|
+
before { client.create_credential(api_key) }
|
59
|
+
after { client.destroy_credential(key_name) }
|
60
|
+
subject { client.list_credentials }
|
61
|
+
|
62
|
+
it 'will list all API Keys' do
|
63
|
+
expect(subject['results'].map { |r| r['name'] }.include?(key_name)).to(eq(true))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context '#destroy_credential' do
|
68
|
+
before { client.create_credential(api_key) }
|
69
|
+
subject { client.destroy_credential(key_name) }
|
70
|
+
|
71
|
+
it 'will delete an API Key' do
|
72
|
+
expect(subject['deleted']).to(eq(true))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Curations do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Static Test Engine'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
let(:curation) do
|
8
|
+
{
|
9
|
+
'queries' => [
|
10
|
+
'zion'
|
11
|
+
],
|
12
|
+
'promoted' => [
|
13
|
+
document1['id']
|
14
|
+
],
|
15
|
+
'hidden' => [
|
16
|
+
document2['id']
|
17
|
+
]
|
18
|
+
}
|
19
|
+
end
|
20
|
+
let(:curation_id) { client.create_curation(engine_name, curation)['id'] }
|
21
|
+
|
22
|
+
after(:each) do
|
23
|
+
begin
|
24
|
+
client.destroy_curation(engine_name, curation_id)
|
25
|
+
rescue
|
26
|
+
# Ignore it
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#create_curation' do
|
31
|
+
it 'will create a curation' do
|
32
|
+
expect(curation_id).not_to(be_empty)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '#get_curation' do
|
37
|
+
subject { client.get_curation(engine_name, curation_id) }
|
38
|
+
|
39
|
+
it 'will retrieve a curation' do
|
40
|
+
expect(subject['queries']).to(eq(['zion']))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#update_curation' do
|
45
|
+
let(:updated_curation) do
|
46
|
+
{
|
47
|
+
'queries' => [
|
48
|
+
'zion', 'lion'
|
49
|
+
],
|
50
|
+
'promoted' => [
|
51
|
+
document1['id']
|
52
|
+
]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
subject { client.update_curation(engine_name, curation_id, updated_curation) }
|
56
|
+
|
57
|
+
it 'will update a curation' do
|
58
|
+
expect(subject['id']).to(eq(curation_id))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context '#list_curations' do
|
63
|
+
subject { client.list_curations(engine_name, :current => 1, :size => 5) }
|
64
|
+
|
65
|
+
it 'will list curations' do
|
66
|
+
expect(subject['results']).to(eq([]))
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'supports paging params' do
|
70
|
+
expect(subject['meta']['page']['current']).to(eq(1))
|
71
|
+
expect(subject['meta']['page']['size']).to(eq(5))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context '#destroy_curation' do
|
76
|
+
subject { client.destroy_curation(engine_name, curation_id) }
|
77
|
+
|
78
|
+
it 'will destroy a curation' do
|
79
|
+
expect(subject['deleted']).to(eq(true))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Documents do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Test Engine'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
context 'Documents' do
|
8
|
+
let(:document) { { 'url' => 'http://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
9
|
+
|
10
|
+
describe '#index_document' do
|
11
|
+
subject { client.index_document(engine_name, document) }
|
12
|
+
|
13
|
+
it 'should return a processed document status hash' do
|
14
|
+
expect(subject).to(match('id' => anything))
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when the document has an id' do
|
18
|
+
let(:id) { 'some_id' }
|
19
|
+
let(:document) { { 'id' => id, 'url' => 'http://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
20
|
+
|
21
|
+
it 'should return a processed document status hash with the same id' do
|
22
|
+
expect(subject).to(eq('id' => id))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when a document has processing errors' do
|
27
|
+
let(:document) { { 'id' => 'too long' * 100 } }
|
28
|
+
|
29
|
+
it 'should raise an error when the API returns errors in the response' do
|
30
|
+
expect do
|
31
|
+
subject
|
32
|
+
end.to(raise_error(Elastic::AppSearch::InvalidDocument, /Invalid field/))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when a document has a Ruby Time object' do
|
37
|
+
let(:time_rfc3339) { '2018-01-01T01:01:01+00:00' }
|
38
|
+
let(:time_object) { Time.parse(time_rfc3339) }
|
39
|
+
let(:document) { { 'created_at' => time_object } }
|
40
|
+
|
41
|
+
it 'should serialize the time object in RFC 3339' do
|
42
|
+
response = subject
|
43
|
+
expect(response).to(have_key('id'))
|
44
|
+
document_id = response.fetch('id')
|
45
|
+
expect do
|
46
|
+
documents = client.get_documents(engine_name, [document_id])
|
47
|
+
expect(documents.size).to(eq(1))
|
48
|
+
expect(documents.first['created_at']).to(eq(time_rfc3339))
|
49
|
+
end.to_not(raise_error)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#index_documents' do
|
55
|
+
let(:documents) { [document, second_document] }
|
56
|
+
let(:second_document_id) { 'another_id' }
|
57
|
+
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
58
|
+
subject { client.index_documents(engine_name, documents) }
|
59
|
+
|
60
|
+
it 'should return an array of document status hashes' do
|
61
|
+
expected = [
|
62
|
+
{ 'id' => anything, 'errors' => [] },
|
63
|
+
{ 'id' => second_document_id, 'errors' => [] }
|
64
|
+
]
|
65
|
+
expect(subject).to(match(expected))
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when one of the documents has processing errors' do
|
69
|
+
let(:second_document) { { 'id' => 'too long' * 100 } }
|
70
|
+
|
71
|
+
it 'should return respective errors in an array of document processing hashes' do
|
72
|
+
expected = [
|
73
|
+
{ 'id' => anything, 'errors' => [] },
|
74
|
+
{ 'id' => anything, 'errors' => ['Invalid field type: id must be less than 800 characters'] },
|
75
|
+
]
|
76
|
+
expect(subject).to(match(expected))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#update_documents' do
|
82
|
+
let(:documents) { [document, second_document] }
|
83
|
+
let(:second_document_id) { 'another_id' }
|
84
|
+
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
85
|
+
let(:updates) do
|
86
|
+
[
|
87
|
+
{
|
88
|
+
'id' => second_document_id,
|
89
|
+
'url' => 'https://www.example.com'
|
90
|
+
}
|
91
|
+
]
|
92
|
+
end
|
93
|
+
|
94
|
+
subject { client.update_documents(engine_name, updates) }
|
95
|
+
|
96
|
+
before do
|
97
|
+
client.index_documents(engine_name, documents)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Note that since indexing a document takes up to a minute,
|
101
|
+
# we don't expect this to succeed, so we simply verify that
|
102
|
+
# the request responded with the correct 'id', even though
|
103
|
+
# the 'errors' object likely contains errors.
|
104
|
+
it 'should update existing documents' do
|
105
|
+
expect(subject).to(match(['id' => second_document_id, 'errors' => anything]))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#get_documents' do
|
110
|
+
let(:documents) { [first_document, second_document] }
|
111
|
+
let(:first_document_id) { 'id' }
|
112
|
+
let(:first_document) { { 'id' => first_document_id, 'url' => 'https://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
113
|
+
let(:second_document_id) { 'another_id' }
|
114
|
+
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
115
|
+
|
116
|
+
subject { client.get_documents(engine_name, [first_document_id, second_document_id]) }
|
117
|
+
|
118
|
+
before do
|
119
|
+
client.index_documents(engine_name, documents)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'will return documents by id' do
|
123
|
+
response = subject
|
124
|
+
expect(response.size).to(eq(2))
|
125
|
+
expect(response[0]['id']).to(eq(first_document_id))
|
126
|
+
expect(response[1]['id']).to(eq(second_document_id))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#list_documents' do
|
131
|
+
let(:documents) { [first_document, second_document] }
|
132
|
+
let(:first_document_id) { 'id' }
|
133
|
+
let(:first_document) { { 'id' => first_document_id, 'url' => 'https://www.youtube.com/watch?v=v1uyQZNg2vE' } }
|
134
|
+
let(:second_document_id) { 'another_id' }
|
135
|
+
let(:second_document) { { 'id' => second_document_id, 'url' => 'https://www.youtube.com/watch?v=9T1vfsHYiKY' } }
|
136
|
+
|
137
|
+
before do
|
138
|
+
client.index_documents(engine_name, documents)
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'when no options are specified' do
|
142
|
+
it 'will return all documents' do
|
143
|
+
response = client.list_documents(engine_name)
|
144
|
+
expect(response['results'].size).to(eq(2))
|
145
|
+
expect(response['results'][0]['id']).to(eq(first_document_id))
|
146
|
+
expect(response['results'][1]['id']).to(eq(second_document_id))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when options are specified' do
|
151
|
+
it 'will return all documents' do
|
152
|
+
response = client.list_documents(engine_name, :page => { :size => 1, :current => 2 })
|
153
|
+
expect(response['results'].size).to(eq(1))
|
154
|
+
expect(response['results'][0]['id']).to(eq(second_document_id))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Engines do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Engine Name'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
context 'Engines' do
|
8
|
+
after do
|
9
|
+
client.destroy_engine(engine_name) rescue Elastic::AppSearch::NonExistentRecord
|
10
|
+
end
|
11
|
+
|
12
|
+
context '#create_engine' do
|
13
|
+
it 'should create an engine when given a right set of parameters' do
|
14
|
+
expect { client.get_engine(engine_name) }.to(raise_error(Elastic::AppSearch::NonExistentRecord))
|
15
|
+
client.create_engine(engine_name)
|
16
|
+
expect { client.get_engine(engine_name) }.to_not(raise_error)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should accept an optional language parameter' do
|
20
|
+
expect { client.get_engine(engine_name) }.to(raise_error(Elastic::AppSearch::NonExistentRecord))
|
21
|
+
client.create_engine(engine_name, 'da')
|
22
|
+
expect(client.get_engine(engine_name)).to(match('name' => anything, 'type' => anything, 'language' => 'da'))
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return an engine object' do
|
26
|
+
engine = client.create_engine(engine_name)
|
27
|
+
expect(engine).to(be_kind_of(Hash))
|
28
|
+
expect(engine['name']).to(eq(engine_name))
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return an error when the engine name has already been taken' do
|
32
|
+
client.create_engine(engine_name)
|
33
|
+
expect { client.create_engine(engine_name) }.to(raise_error) do |e|
|
34
|
+
expect(e).to(be_a(Elastic::AppSearch::BadRequest))
|
35
|
+
expect(e.errors).to(eq(['Name is already taken']))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context '#list_engines' do
|
41
|
+
it 'should return an array with a list of engines' do
|
42
|
+
expect(client.list_engines['results']).to(be_an(Array))
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should include the engine name in listed objects' do
|
46
|
+
client.create_engine(engine_name)
|
47
|
+
|
48
|
+
engines = client.list_engines['results']
|
49
|
+
expect(engines.find { |e| e['name'] == engine_name }).to_not(be_nil)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should include the engine name in listed objects with pagination' do
|
53
|
+
client.create_engine(engine_name)
|
54
|
+
|
55
|
+
engines = client.list_engines(:current => 1, :size => 20)['results']
|
56
|
+
expect(engines.find { |e| e['name'] == engine_name }).to_not(be_nil)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context '#destroy_engine' do
|
61
|
+
it 'should destroy the engine if it exists' do
|
62
|
+
client.create_engine(engine_name)
|
63
|
+
expect { client.get_engine(engine_name) }.to_not(raise_error)
|
64
|
+
|
65
|
+
client.destroy_engine(engine_name)
|
66
|
+
expect { client.get_engine(engine_name) }.to(raise_error(Elastic::AppSearch::NonExistentRecord))
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should raise an error if the engine does not exist' do
|
70
|
+
expect { client.destroy_engine(engine_name) }.to(raise_error(Elastic::AppSearch::NonExistentRecord))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/spec/logs_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Logs do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Test Engine'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
context '#get_api_logs' do
|
8
|
+
let(:from) { Time.now.iso8601 }
|
9
|
+
let(:to) { Time.now.iso8601 }
|
10
|
+
|
11
|
+
subject do
|
12
|
+
options = {
|
13
|
+
:filters => {
|
14
|
+
:date => {
|
15
|
+
:from => from,
|
16
|
+
:to => to
|
17
|
+
}
|
18
|
+
},
|
19
|
+
:page => {
|
20
|
+
:total_results => 100,
|
21
|
+
:size => 20
|
22
|
+
},
|
23
|
+
:query => 'search',
|
24
|
+
:sort_direction => 'desc'
|
25
|
+
}
|
26
|
+
client.get_api_logs(engine_name, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'will retrieve api logs' do
|
30
|
+
expect(subject['results']).to(eq([]))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe Elastic::AppSearch::Client::Search do
|
2
|
+
include_context 'App Search Credentials'
|
3
|
+
include_context 'Static Test Engine'
|
4
|
+
|
5
|
+
let(:client) { Elastic::AppSearch::Client.new(client_options) }
|
6
|
+
|
7
|
+
context 'QuerySuggest' do
|
8
|
+
describe '#query_suggestion' do
|
9
|
+
let(:query) { 'cat' }
|
10
|
+
let(:options) { { :size => 3, :types => { :documents => { :fields => ['title'] } } } }
|
11
|
+
|
12
|
+
context 'when options are provided' do
|
13
|
+
subject { client.query_suggestion(engine_name, query, options) }
|
14
|
+
|
15
|
+
it 'should request query suggestions' do
|
16
|
+
expected = {
|
17
|
+
'meta' => anything,
|
18
|
+
'results' => anything
|
19
|
+
}
|
20
|
+
expect(subject).to(match(expected))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when options are omitted' do
|
25
|
+
subject { client.query_suggestion(engine_name, query) }
|
26
|
+
|
27
|
+
it 'should request query suggestions' do
|
28
|
+
expected = {
|
29
|
+
'meta' => anything,
|
30
|
+
'results' => anything
|
31
|
+
}
|
32
|
+
expect(subject).to(match(expected))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|