elastictastic 0.5.0 → 0.10.2
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.
- data/LICENSE +1 -1
- data/README.md +161 -10
- data/lib/elastictastic/adapter.rb +84 -0
- data/lib/elastictastic/association.rb +6 -0
- data/lib/elastictastic/basic_document.rb +213 -0
- data/lib/elastictastic/bulk_persistence_strategy.rb +64 -19
- data/lib/elastictastic/callbacks.rb +18 -12
- data/lib/elastictastic/child_collection_proxy.rb +15 -11
- data/lib/elastictastic/client.rb +47 -24
- data/lib/elastictastic/configuration.rb +59 -4
- data/lib/elastictastic/dirty.rb +43 -28
- data/lib/elastictastic/discrete_persistence_strategy.rb +48 -23
- data/lib/elastictastic/document.rb +1 -85
- data/lib/elastictastic/embedded_document.rb +34 -0
- data/lib/elastictastic/errors.rb +17 -5
- data/lib/elastictastic/field.rb +3 -0
- data/lib/elastictastic/mass_assignment_security.rb +2 -4
- data/lib/elastictastic/middleware.rb +66 -84
- data/lib/elastictastic/multi_get.rb +30 -0
- data/lib/elastictastic/multi_search.rb +70 -0
- data/lib/elastictastic/nested_document.rb +3 -27
- data/lib/elastictastic/new_relic_instrumentation.rb +8 -8
- data/lib/elastictastic/observing.rb +8 -6
- data/lib/elastictastic/optimistic_locking.rb +57 -0
- data/lib/elastictastic/parent_child.rb +56 -54
- data/lib/elastictastic/persistence.rb +16 -16
- data/lib/elastictastic/properties.rb +136 -96
- data/lib/elastictastic/railtie.rb +1 -1
- data/lib/elastictastic/rotor.rb +105 -0
- data/lib/elastictastic/scope.rb +186 -56
- data/lib/elastictastic/server_error.rb +20 -1
- data/lib/elastictastic/test_helpers.rb +152 -97
- data/lib/elastictastic/thrift/constants.rb +12 -0
- data/lib/elastictastic/thrift/rest.rb +83 -0
- data/lib/elastictastic/thrift/types.rb +124 -0
- data/lib/elastictastic/thrift_adapter.rb +61 -0
- data/lib/elastictastic/transport_methods.rb +27 -0
- data/lib/elastictastic/validations.rb +11 -13
- data/lib/elastictastic/version.rb +1 -1
- data/lib/elastictastic.rb +148 -27
- data/spec/environment.rb +1 -1
- data/spec/examples/bulk_persistence_strategy_spec.rb +151 -23
- data/spec/examples/callbacks_spec.rb +65 -34
- data/spec/examples/dirty_spec.rb +160 -1
- data/spec/examples/document_spec.rb +168 -106
- data/spec/examples/middleware_spec.rb +1 -61
- data/spec/examples/multi_get_spec.rb +127 -0
- data/spec/examples/multi_search_spec.rb +113 -0
- data/spec/examples/observing_spec.rb +24 -3
- data/spec/examples/optimistic_locking_spec.rb +417 -0
- data/spec/examples/parent_child_spec.rb +73 -33
- data/spec/examples/properties_spec.rb +53 -0
- data/spec/examples/rotor_spec.rb +132 -0
- data/spec/examples/scope_spec.rb +78 -18
- data/spec/examples/search_spec.rb +26 -0
- data/spec/examples/validation_spec.rb +7 -1
- data/spec/models/author.rb +1 -1
- data/spec/models/blog.rb +2 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/photo.rb +9 -0
- data/spec/models/post.rb +3 -0
- metadata +97 -78
- data/lib/elastictastic/resource.rb +0 -4
- data/spec/examples/active_model_lint_spec.rb +0 -20
@@ -1,172 +1,227 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'fakeweb'
|
3
|
+
rescue LoadError => e
|
4
|
+
raise LoadError, "Elastictastic::TestHelpers requires the 'fakeweb' gem."
|
5
|
+
end
|
2
6
|
|
3
7
|
module Elastictastic
|
4
8
|
module TestHelpers
|
5
9
|
ALPHANUM = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
|
6
10
|
|
7
|
-
def
|
8
|
-
options = args.extract_options!
|
9
|
-
id = args.pop
|
11
|
+
def stub_es_create(index, type, id = nil)
|
10
12
|
if id.nil?
|
11
|
-
id =
|
12
|
-
|
13
|
-
path = "/#{index}/#{type}"
|
13
|
+
id = generate_es_id
|
14
|
+
components = [index, type]
|
14
15
|
method = :post
|
15
16
|
else
|
16
|
-
|
17
|
+
components = [index, type, id, '_create']
|
17
18
|
method = :put
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
+
stub_request_json(
|
21
22
|
method,
|
22
|
-
|
23
|
-
|
24
|
-
'ok' => 'true',
|
25
|
-
'_index' => index,
|
26
|
-
'_type' => type,
|
27
|
-
'_id' => id
|
28
|
-
}.to_json)
|
23
|
+
match_es_resource(components),
|
24
|
+
generate_es_hit(type, :id => id, :index => index).merge('ok' => 'true')
|
29
25
|
)
|
30
26
|
id
|
31
27
|
end
|
32
28
|
|
33
|
-
def
|
34
|
-
|
29
|
+
def stub_es_update(index, type, id, version = 2)
|
30
|
+
stub_request_json(
|
35
31
|
:put,
|
36
|
-
|
37
|
-
:
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
32
|
+
match_es_resource(index, type, id),
|
33
|
+
generate_es_hit(type, :index => index, :id => id, :version => version)
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def stub_es_head(index, type, id, exists)
|
38
|
+
stub_request(
|
39
|
+
:head,
|
40
|
+
match_es_resource(index, type, id),
|
41
|
+
:status => (exists ? 200 : 404),
|
42
|
+
:body => nil
|
43
43
|
)
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
47
|
-
|
46
|
+
def stub_es_get(index, type, id, doc = {}, version = 1)
|
47
|
+
stub_request_json(
|
48
48
|
:get,
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
}.to_json
|
49
|
+
match_es_resource(index, type, id),
|
50
|
+
generate_es_hit(
|
51
|
+
type,
|
52
|
+
:index => index,
|
53
|
+
:id => id,
|
54
|
+
:version => version,
|
55
|
+
:source => doc
|
56
|
+
).merge('exists' => !doc.nil?)
|
58
57
|
)
|
59
58
|
end
|
60
59
|
|
61
|
-
def
|
60
|
+
def stub_es_mget(index, type, *ids)
|
62
61
|
given_ids_with_docs = ids.extract_options!
|
63
62
|
ids_with_docs = {}
|
64
63
|
ids.each { |id| ids_with_docs[id] = {} }
|
65
64
|
ids_with_docs.merge!(given_ids_with_docs)
|
66
65
|
path = index ? "/#{index}/#{type}/_mget" : "/_mget"
|
67
66
|
docs = ids_with_docs.each_pair.map do |id, doc|
|
68
|
-
id, index = *id if Array === id
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
'_id' => id,
|
73
|
-
'exists' => !!doc,
|
74
|
-
'_source' => doc
|
75
|
-
}
|
67
|
+
id, type, index = *id if Array === id
|
68
|
+
generate_es_hit(
|
69
|
+
type, :index => index, :id => id, :source => doc
|
70
|
+
).merge('exists' => !!doc)
|
76
71
|
end
|
77
72
|
|
78
|
-
|
73
|
+
stub_request_json(
|
79
74
|
:post,
|
80
|
-
|
81
|
-
|
82
|
-
'docs' => docs
|
83
|
-
}.to_json
|
75
|
+
match_es_path(path),
|
76
|
+
'docs' => docs
|
84
77
|
)
|
85
78
|
end
|
86
79
|
|
87
|
-
def
|
88
|
-
|
80
|
+
def stub_es_destroy(index, type, id, options = {})
|
81
|
+
stub_request_json(
|
89
82
|
:delete,
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
'_index' => 'test',
|
95
|
-
'_type' => 'test',
|
96
|
-
'_id' => id,
|
97
|
-
'_version' => 1
|
98
|
-
}.to_json)
|
83
|
+
match_es_resource(index, type, id),
|
84
|
+
generate_es_hit(
|
85
|
+
type, :index => index, :id => id
|
86
|
+
).merge('ok' => true, 'found' => true).merge(options)
|
99
87
|
)
|
100
88
|
end
|
101
89
|
|
102
|
-
def
|
103
|
-
|
90
|
+
def stub_es_destroy_all(index, type)
|
91
|
+
stub_request_json(
|
104
92
|
:delete,
|
105
|
-
|
106
|
-
|
93
|
+
match_es_resource(index, type),
|
94
|
+
'ok' => true
|
107
95
|
)
|
108
96
|
end
|
109
97
|
|
110
|
-
def
|
111
|
-
|
98
|
+
def stub_es_bulk(*responses)
|
99
|
+
stub_request_json(
|
112
100
|
:post,
|
113
|
-
|
114
|
-
|
101
|
+
match_es_path('/_bulk'),
|
102
|
+
'took' => 1, 'items' => responses
|
115
103
|
)
|
116
104
|
end
|
117
105
|
|
118
|
-
def
|
119
|
-
|
106
|
+
def stub_es_put_mapping(index, type)
|
107
|
+
stub_request_json(
|
120
108
|
:put,
|
121
|
-
|
122
|
-
|
109
|
+
match_es_resource(index, type, '_mapping'),
|
110
|
+
'ok' => true, 'acknowledged' => true
|
123
111
|
)
|
124
112
|
end
|
125
113
|
|
126
|
-
def
|
114
|
+
def stub_es_search(index, type, data)
|
127
115
|
if Array === data
|
128
116
|
response = data.map do |datum|
|
129
|
-
{ :body => datum
|
117
|
+
{ :body => Elastictastic.json_encode(datum) }
|
130
118
|
end
|
131
119
|
else
|
132
|
-
response = { :body => data
|
120
|
+
response = { :body => Elastictastic.json_encode(data) }
|
133
121
|
end
|
134
122
|
|
135
|
-
|
136
|
-
FakeWeb.register_uri(
|
123
|
+
stub_request(
|
137
124
|
:post,
|
138
|
-
|
125
|
+
match_es_resource(index, type, '_search'),
|
139
126
|
response
|
140
127
|
)
|
141
128
|
end
|
142
129
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
146
|
-
|
130
|
+
def stub_es_msearch(*hits_collections)
|
131
|
+
responses = hits_collections.map do |collection|
|
132
|
+
{ 'hits' => { 'hits' => collection, 'total' => collection.length }}
|
133
|
+
end
|
134
|
+
stub_request_json(
|
147
135
|
:post,
|
148
|
-
|
149
|
-
|
150
|
-
'_scroll_id' => scroll_ids.first,
|
151
|
-
'hits' => { 'total' => hits.length, 'hits' => [] }
|
152
|
-
}.to_json
|
136
|
+
match_es_path('/_msearch'),
|
137
|
+
'responses' => responses
|
153
138
|
)
|
139
|
+
end
|
154
140
|
|
155
|
-
|
156
|
-
|
141
|
+
def stub_es_msearch_count(*counts)
|
142
|
+
responses = counts.map do |count|
|
143
|
+
{ 'hits' => { 'hits' => [], 'total' => count }}
|
157
144
|
end
|
158
|
-
|
159
|
-
scroll_uri = Regexp.escape(TestHelpers.uri_for_path("/_search/scroll").to_s)
|
160
|
-
FakeWeb.register_uri(
|
145
|
+
stub_request_json(
|
161
146
|
:post,
|
162
|
-
|
163
|
-
|
147
|
+
match_es_path('/_msearch'),
|
148
|
+
'responses' => responses
|
164
149
|
)
|
150
|
+
end
|
151
|
+
|
152
|
+
def stub_es_scan(index, type, batch_size, *hits)
|
153
|
+
scroll_ids = Array.new(batch_size + 1) { rand(10**100).to_s(36) }
|
154
|
+
stub_request_json(
|
155
|
+
:post,
|
156
|
+
match_es_resource(index, type, '_search'),
|
157
|
+
'_scroll_id' => scroll_ids.first,
|
158
|
+
'hits' => { 'total' => hits.length, 'hits' => [] }
|
159
|
+
)
|
160
|
+
|
161
|
+
batches = hits.each_slice(batch_size).each_with_index.map do |hit_batch, i|
|
162
|
+
{
|
163
|
+
:body => Elastictastic.json_encode(
|
164
|
+
'_scroll_id' => scroll_ids[i+1], 'hits' => { 'hits' => hit_batch }
|
165
|
+
)
|
166
|
+
}
|
167
|
+
end
|
168
|
+
batches << { :body => Elastictastic.json_encode('hits' => { 'hits' => [] }) }
|
169
|
+
stub_request(:post, match_es_path('/_search/scroll'), batches)
|
165
170
|
scroll_ids
|
166
171
|
end
|
167
172
|
|
168
|
-
def self.
|
169
|
-
|
173
|
+
def self.match_es_path(path)
|
174
|
+
/^#{Regexp.escape(Elastictastic.config.hosts.first)}#{Regexp.escape(path)}(\?.*)?$/
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.match_es_resource(*components)
|
178
|
+
match_es_path("/#{components.flatten.join('/')}")
|
179
|
+
end
|
180
|
+
|
181
|
+
def match_es_path(path)
|
182
|
+
TestHelpers.match_es_path(path)
|
183
|
+
end
|
184
|
+
|
185
|
+
def match_es_resource(*components)
|
186
|
+
TestHelpers.match_es_resource(*components)
|
187
|
+
end
|
188
|
+
|
189
|
+
def stub_request_json(method, uri, *responses)
|
190
|
+
json_responses = responses.map { |response| { :body => Elastictastic.json_encode(response) }}
|
191
|
+
json_responses = json_responses.first if json_responses.length == 1
|
192
|
+
stub_request(method, uri, json_responses)
|
193
|
+
end
|
194
|
+
|
195
|
+
def stub_request(method, url, options = {})
|
196
|
+
FakeWeb.register_uri(method, url, options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def last_request
|
200
|
+
FakeWeb.last_request
|
201
|
+
end
|
202
|
+
|
203
|
+
def last_request_json
|
204
|
+
Elastictastic.json_decode(last_request.body)
|
205
|
+
end
|
206
|
+
|
207
|
+
def last_request_uri
|
208
|
+
URI.parse(last_request.path)
|
209
|
+
end
|
210
|
+
|
211
|
+
def generate_es_id
|
212
|
+
''.tap do |id|
|
213
|
+
22.times { id << ALPHANUM[rand(ALPHANUM.length)] }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def generate_es_hit(type, options = {})
|
218
|
+
{
|
219
|
+
'_id' => options[:id] || generate_es_id,
|
220
|
+
'_type' => type,
|
221
|
+
'_index' => options[:index] || Elastictastic.config.default_index,
|
222
|
+
'_version' => options[:version] || 1,
|
223
|
+
'_source' => options.key?(:source) ? options[:source] : {}
|
224
|
+
}
|
170
225
|
end
|
171
226
|
end
|
172
227
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift Compiler (0.8.0-dev)
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'thrift'
|
8
|
+
require 'elastictastic/thrift/types'
|
9
|
+
|
10
|
+
module Elastictastic
|
11
|
+
module Thrift
|
12
|
+
module Rest
|
13
|
+
class Client
|
14
|
+
include ::Thrift::Client
|
15
|
+
|
16
|
+
def execute(request)
|
17
|
+
send_execute(request)
|
18
|
+
return recv_execute()
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_execute(request)
|
22
|
+
send_message('execute', Execute_args, :request => request)
|
23
|
+
end
|
24
|
+
|
25
|
+
def recv_execute()
|
26
|
+
result = receive_message(Execute_result)
|
27
|
+
return result.success unless result.success.nil?
|
28
|
+
raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'execute failed: unknown result')
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class Processor
|
34
|
+
include ::Thrift::Processor
|
35
|
+
|
36
|
+
def process_execute(seqid, iprot, oprot)
|
37
|
+
args = read_args(iprot, Execute_args)
|
38
|
+
result = Execute_result.new()
|
39
|
+
result.success = @handler.execute(args.request)
|
40
|
+
write_result(result, oprot, 'execute', seqid)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# HELPER FUNCTIONS AND STRUCTURES
|
46
|
+
|
47
|
+
class Execute_args
|
48
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
49
|
+
REQUEST = 1
|
50
|
+
|
51
|
+
FIELDS = {
|
52
|
+
REQUEST => {:type => ::Thrift::Types::STRUCT, :name => 'request', :class => Elastictastic::Thrift::RestRequest}
|
53
|
+
}
|
54
|
+
|
55
|
+
def struct_fields; FIELDS; end
|
56
|
+
|
57
|
+
def validate
|
58
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field request is unset!') unless @request
|
59
|
+
end
|
60
|
+
|
61
|
+
::Thrift::Struct.generate_accessors self
|
62
|
+
end
|
63
|
+
|
64
|
+
class Execute_result
|
65
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
66
|
+
SUCCESS = 0
|
67
|
+
|
68
|
+
FIELDS = {
|
69
|
+
SUCCESS => {:type => ::Thrift::Types::STRUCT, :name => 'success', :class => Elastictastic::Thrift::RestResponse}
|
70
|
+
}
|
71
|
+
|
72
|
+
def struct_fields; FIELDS; end
|
73
|
+
|
74
|
+
def validate
|
75
|
+
end
|
76
|
+
|
77
|
+
::Thrift::Struct.generate_accessors self
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift Compiler (0.8.0-dev)
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
|
8
|
+
module Elastictastic
|
9
|
+
module Thrift
|
10
|
+
module Method
|
11
|
+
GET = 0
|
12
|
+
PUT = 1
|
13
|
+
POST = 2
|
14
|
+
DELETE = 3
|
15
|
+
HEAD = 4
|
16
|
+
OPTIONS = 5
|
17
|
+
VALUE_MAP = {0 => "GET", 1 => "PUT", 2 => "POST", 3 => "DELETE", 4 => "HEAD", 5 => "OPTIONS"}
|
18
|
+
VALID_VALUES = Set.new([GET, PUT, POST, DELETE, HEAD, OPTIONS]).freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
module Status
|
22
|
+
CONT = 100
|
23
|
+
SWITCHING_PROTOCOLS = 101
|
24
|
+
OK = 200
|
25
|
+
CREATED = 201
|
26
|
+
ACCEPTED = 202
|
27
|
+
NON_AUTHORITATIVE_INFORMATION = 203
|
28
|
+
NO_CONTENT = 204
|
29
|
+
RESET_CONTENT = 205
|
30
|
+
PARTIAL_CONTENT = 206
|
31
|
+
MULTI_STATUS = 207
|
32
|
+
MULTIPLE_CHOICES = 300
|
33
|
+
MOVED_PERMANENTLY = 301
|
34
|
+
FOUND = 302
|
35
|
+
SEE_OTHER = 303
|
36
|
+
NOT_MODIFIED = 304
|
37
|
+
USE_PROXY = 305
|
38
|
+
TEMPORARY_REDIRECT = 307
|
39
|
+
BAD_REQUEST = 400
|
40
|
+
UNAUTHORIZED = 401
|
41
|
+
PAYMENT_REQUIRED = 402
|
42
|
+
FORBIDDEN = 403
|
43
|
+
NOT_FOUND = 404
|
44
|
+
METHOD_NOT_ALLOWED = 405
|
45
|
+
NOT_ACCEPTABLE = 406
|
46
|
+
PROXY_AUTHENTICATION = 407
|
47
|
+
REQUEST_TIMEOUT = 408
|
48
|
+
CONFLICT = 409
|
49
|
+
GONE = 410
|
50
|
+
LENGTH_REQUIRED = 411
|
51
|
+
PRECONDITION_FAILED = 412
|
52
|
+
REQUEST_ENTITY_TOO_LARGE = 413
|
53
|
+
REQUEST_URI_TOO_LONG = 414
|
54
|
+
UNSUPPORTED_MEDIA_TYPE = 415
|
55
|
+
REQUESTED_RANGE_NOT_SATISFIED = 416
|
56
|
+
EXPECTATION_FAILED = 417
|
57
|
+
UNPROCESSABLE_ENTITY = 422
|
58
|
+
LOCKED = 423
|
59
|
+
FAILED_DEPENDENCY = 424
|
60
|
+
INTERNAL_SERVER_ERROR = 500
|
61
|
+
NOT_IMPLEMENTED = 501
|
62
|
+
BAD_GATEWAY = 502
|
63
|
+
SERVICE_UNAVAILABLE = 503
|
64
|
+
GATEWAY_TIMEOUT = 504
|
65
|
+
INSUFFICIENT_STORAGE = 506
|
66
|
+
VALUE_MAP = {100 => "CONT", 101 => "SWITCHING_PROTOCOLS", 200 => "OK", 201 => "CREATED", 202 => "ACCEPTED", 203 => "NON_AUTHORITATIVE_INFORMATION", 204 => "NO_CONTENT", 205 => "RESET_CONTENT", 206 => "PARTIAL_CONTENT", 207 => "MULTI_STATUS", 300 => "MULTIPLE_CHOICES", 301 => "MOVED_PERMANENTLY", 302 => "FOUND", 303 => "SEE_OTHER", 304 => "NOT_MODIFIED", 305 => "USE_PROXY", 307 => "TEMPORARY_REDIRECT", 400 => "BAD_REQUEST", 401 => "UNAUTHORIZED", 402 => "PAYMENT_REQUIRED", 403 => "FORBIDDEN", 404 => "NOT_FOUND", 405 => "METHOD_NOT_ALLOWED", 406 => "NOT_ACCEPTABLE", 407 => "PROXY_AUTHENTICATION", 408 => "REQUEST_TIMEOUT", 409 => "CONFLICT", 410 => "GONE", 411 => "LENGTH_REQUIRED", 412 => "PRECONDITION_FAILED", 413 => "REQUEST_ENTITY_TOO_LARGE", 414 => "REQUEST_URI_TOO_LONG", 415 => "UNSUPPORTED_MEDIA_TYPE", 416 => "REQUESTED_RANGE_NOT_SATISFIED", 417 => "EXPECTATION_FAILED", 422 => "UNPROCESSABLE_ENTITY", 423 => "LOCKED", 424 => "FAILED_DEPENDENCY", 500 => "INTERNAL_SERVER_ERROR", 501 => "NOT_IMPLEMENTED", 502 => "BAD_GATEWAY", 503 => "SERVICE_UNAVAILABLE", 504 => "GATEWAY_TIMEOUT", 506 => "INSUFFICIENT_STORAGE"}
|
67
|
+
VALID_VALUES = Set.new([CONT, SWITCHING_PROTOCOLS, OK, CREATED, ACCEPTED, NON_AUTHORITATIVE_INFORMATION, NO_CONTENT, RESET_CONTENT, PARTIAL_CONTENT, MULTI_STATUS, MULTIPLE_CHOICES, MOVED_PERMANENTLY, FOUND, SEE_OTHER, NOT_MODIFIED, USE_PROXY, TEMPORARY_REDIRECT, BAD_REQUEST, UNAUTHORIZED, PAYMENT_REQUIRED, FORBIDDEN, NOT_FOUND, METHOD_NOT_ALLOWED, NOT_ACCEPTABLE, PROXY_AUTHENTICATION, REQUEST_TIMEOUT, CONFLICT, GONE, LENGTH_REQUIRED, PRECONDITION_FAILED, REQUEST_ENTITY_TOO_LARGE, REQUEST_URI_TOO_LONG, UNSUPPORTED_MEDIA_TYPE, REQUESTED_RANGE_NOT_SATISFIED, EXPECTATION_FAILED, UNPROCESSABLE_ENTITY, LOCKED, FAILED_DEPENDENCY, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, INSUFFICIENT_STORAGE]).freeze
|
68
|
+
end
|
69
|
+
|
70
|
+
class RestRequest
|
71
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
72
|
+
METHOD = 1
|
73
|
+
URI = 2
|
74
|
+
PARAMETERS = 3
|
75
|
+
HEADERS = 4
|
76
|
+
BODY = 5
|
77
|
+
|
78
|
+
FIELDS = {
|
79
|
+
METHOD => {:type => ::Thrift::Types::I32, :name => 'method', :enum_class => Elastictastic::Thrift::Method},
|
80
|
+
URI => {:type => ::Thrift::Types::STRING, :name => 'uri'},
|
81
|
+
PARAMETERS => {:type => ::Thrift::Types::MAP, :name => 'parameters', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
|
82
|
+
HEADERS => {:type => ::Thrift::Types::MAP, :name => 'headers', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
|
83
|
+
BODY => {:type => ::Thrift::Types::STRING, :name => 'body', :binary => true, :optional => true}
|
84
|
+
}
|
85
|
+
|
86
|
+
def struct_fields; FIELDS; end
|
87
|
+
|
88
|
+
def validate
|
89
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field method is unset!') unless @method
|
90
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field uri is unset!') unless @uri
|
91
|
+
unless @method.nil? || Elastictastic::Thrift::Method::VALID_VALUES.include?(@method)
|
92
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field method!')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
::Thrift::Struct.generate_accessors self
|
97
|
+
end
|
98
|
+
|
99
|
+
class RestResponse
|
100
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
101
|
+
STATUS = 1
|
102
|
+
HEADERS = 2
|
103
|
+
BODY = 3
|
104
|
+
|
105
|
+
FIELDS = {
|
106
|
+
STATUS => {:type => ::Thrift::Types::I32, :name => 'status', :enum_class => Elastictastic::Thrift::Status},
|
107
|
+
HEADERS => {:type => ::Thrift::Types::MAP, :name => 'headers', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
|
108
|
+
BODY => {:type => ::Thrift::Types::STRING, :name => 'body', :binary => true, :optional => true}
|
109
|
+
}
|
110
|
+
|
111
|
+
def struct_fields; FIELDS; end
|
112
|
+
|
113
|
+
def validate
|
114
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field status is unset!') unless @status
|
115
|
+
unless @status.nil? || Elastictastic::Thrift::Status::VALID_VALUES.include?(@status)
|
116
|
+
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field status!')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
::Thrift::Struct.generate_accessors self
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
begin
|
2
|
+
require 'thrift'
|
3
|
+
rescue LoadError
|
4
|
+
raise LoadError, 'Using the ThriftAdapter requires the "thrift" gem. Please install it.'
|
5
|
+
end
|
6
|
+
require 'cgi'
|
7
|
+
require 'faraday'
|
8
|
+
require 'elastictastic/thrift/rest'
|
9
|
+
|
10
|
+
module Elastictastic
|
11
|
+
class ThriftAdapter < Faraday::Adapter
|
12
|
+
def initialize(app, options = {})
|
13
|
+
super(app)
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
super
|
19
|
+
url = env[:url]
|
20
|
+
req = env[:request]
|
21
|
+
|
22
|
+
request = Elastictastic::Thrift::RestRequest.new
|
23
|
+
request.method =
|
24
|
+
Elastictastic::Thrift::Method.const_get(env[:method].to_s.upcase)
|
25
|
+
request.body = env[:body] if env[:body]
|
26
|
+
request.uri = url.path
|
27
|
+
parameters = {}
|
28
|
+
request.parameters = url.query_values
|
29
|
+
|
30
|
+
response = thrift_request(url.host, url.inferred_port, request)
|
31
|
+
|
32
|
+
save_response(env, response.status, response.body) do |response_headers|
|
33
|
+
if response.headers
|
34
|
+
headers.each_pair do |key, value|
|
35
|
+
response_headers[key] = value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@app.call(env)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def thrift_request(host, port, request)
|
46
|
+
@thrift_clients ||= {}
|
47
|
+
client = @thrift_clients[[host, port]] ||=
|
48
|
+
begin
|
49
|
+
transport = ::Thrift::BufferedTransport.new(::Thrift::Socket.new(host, port, @options[:timeout]))
|
50
|
+
protocol = ::Thrift::BinaryProtocol.new(transport)
|
51
|
+
Elastictastic::Thrift::Rest::Client.new(protocol).tap do
|
52
|
+
transport.open
|
53
|
+
end
|
54
|
+
end
|
55
|
+
client.execute(request)
|
56
|
+
rescue ::Thrift::TransportException, IOError => e
|
57
|
+
@thrift_clients.delete([host, port])
|
58
|
+
raise Faraday::Error::ConnectionFailed, e.message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Elastictastic
|
2
|
+
|
3
|
+
module TransportMethods
|
4
|
+
|
5
|
+
def head(path)
|
6
|
+
request(:head, path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(path)
|
10
|
+
request(:get, path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def post(path, body)
|
14
|
+
request(:post, path, body)
|
15
|
+
end
|
16
|
+
|
17
|
+
def put(path, body)
|
18
|
+
request(:put, path, body)
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete(path)
|
22
|
+
request(:delete, path)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -15,22 +15,20 @@ module Elastictastic
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
false
|
25
|
-
end
|
18
|
+
def save(options = {})
|
19
|
+
if options[:validate] == false || valid?
|
20
|
+
super
|
21
|
+
true
|
22
|
+
else
|
23
|
+
false
|
26
24
|
end
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
self
|
27
|
+
def save!
|
28
|
+
if !save
|
29
|
+
raise Elastictastic::RecordInvalid, errors.full_messages.to_sentence
|
33
30
|
end
|
31
|
+
self
|
34
32
|
end
|
35
33
|
|
36
34
|
class NestedValidator < ActiveModel::EachValidator
|