elastictastic 0.5.0 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|