elastic-app-search 7.4.0 → 7.8.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.
@@ -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,130 @@
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
+ end
130
+ 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
@@ -0,0 +1,25 @@
1
+ describe Elastic::AppSearch::Client::Documents 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 'Documents' do
8
+ describe '#list_documents' do
9
+ context 'when no options are specified' do
10
+ it 'will return all documents' do
11
+ response = client.list_documents(engine_name)
12
+ expect(response['results'].size).to(eq(2))
13
+ expect(response['results'].map { |d| d['id'] }).to(include(document1['id'], document2['id']))
14
+ end
15
+ end
16
+
17
+ context 'when options are specified' do
18
+ it 'will return all documents' do
19
+ response = client.list_documents(engine_name, :page => { :size => 1, :current => 2 })
20
+ expect(response['results'].size).to(eq(1))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -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,73 @@
1
+ describe Elastic::AppSearch::Client::MetaEngines do
2
+ include_context 'App Search Credentials'
3
+ include_context 'Engine Name'
4
+ include_context 'Meta Engine Name'
5
+ include_context 'Test Engine'
6
+
7
+ let(:client) { Elastic::AppSearch::Client.new(client_options) }
8
+ let(:source_engines) { [engine_name] }
9
+
10
+ # CI currently runs against SaaS. This feature is a Self-Managed only feature.
11
+ context 'Meta Engines', :skip => "Unable to test Self-Managed features in CI." do
12
+
13
+ after do
14
+ client.destroy_engine(meta_engine_name) rescue Elastic::AppSearch::NonExistentRecord
15
+ end
16
+
17
+ context '#create_meta_engine' do
18
+ it 'should create a meta engine when given a right set of parameters' do
19
+ expect { client.get_engine(meta_engine_name) }.to raise_error(Elastic::AppSearch::NonExistentRecord)
20
+ client.create_meta_engine(meta_engine_name, source_engines)
21
+ expect { client.get_engine(meta_engine_name) }.to_not raise_error
22
+ end
23
+
24
+ it 'should return a meta engine object' do
25
+ engine = client.create_meta_engine(meta_engine_name, source_engines)
26
+ expect(engine).to be_kind_of(Hash)
27
+ expect(engine['name']).to eq(meta_engine_name)
28
+ expect(engine['type']).to eq('meta')
29
+ expect(engine['source_engines']).to eq(source_engines)
30
+ end
31
+
32
+ it 'should return an error when the engine source engine is empty' do
33
+ expect { client.create_meta_engine(engine_name, []) }.to(raise_error) do |e|
34
+ expect(e).to be_a(Elastic::AppSearch::BadRequest)
35
+ expect(e.errors).to eq(['Source engines are required for meta engines'])
36
+ end
37
+ end
38
+ end
39
+
40
+ context '#add_meta_engine_sources' do
41
+ before do
42
+ client.create_meta_engine(meta_engine_name, source_engines)
43
+ client.delete_meta_engine_sources(meta_engine_name, source_engines)
44
+ end
45
+
46
+ it 'should add the source engine' do
47
+ expect { client.add_meta_engine_sources(meta_engine_name, source_engines) }.to_not raise_error do |engine|
48
+ expect(engine).to be_kind_of(Hash)
49
+ expect(engine['name']).to eq(meta_engine_name)
50
+ expect(engine['type']).to eq('meta')
51
+ expect(engine['source_engines']).to be_kind_of(Array)
52
+ expect(engine['source_engines']).to eq(source_engines)
53
+ end
54
+ end
55
+ end
56
+
57
+ context '#delete_meta_engine_sources' do
58
+ before do
59
+ client.create_meta_engine(meta_engine_name, source_engines)
60
+ end
61
+
62
+ it 'should remove the source engine' do
63
+ expect { client.delete_meta_engine_sources(meta_engine_name, source_engines) }.to_not raise_error do |engine|
64
+ expect(engine).to be_kind_of(Hash)
65
+ expect(engine['name']).to eq(meta_engine_name)
66
+ expect(engine['type']).to eq('meta')
67
+ expect(engine['source_engines']).to be_kind_of(Array)
68
+ expect(engine['source_engines']).to be_empty
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end