elastic-app-search 7.4.0 → 7.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|