vines-services 0.1.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.
- data/LICENSE +19 -0
- data/README +40 -0
- data/Rakefile +130 -0
- data/bin/vines-services +95 -0
- data/conf/config.rb +25 -0
- data/lib/vines/services/command/init.rb +209 -0
- data/lib/vines/services/command/restart.rb +14 -0
- data/lib/vines/services/command/start.rb +30 -0
- data/lib/vines/services/command/stop.rb +20 -0
- data/lib/vines/services/command/views.rb +26 -0
- data/lib/vines/services/component.rb +26 -0
- data/lib/vines/services/config.rb +105 -0
- data/lib/vines/services/connection.rb +120 -0
- data/lib/vines/services/controller/attributes_controller.rb +19 -0
- data/lib/vines/services/controller/base_controller.rb +99 -0
- data/lib/vines/services/controller/disco_info_controller.rb +61 -0
- data/lib/vines/services/controller/labels_controller.rb +17 -0
- data/lib/vines/services/controller/members_controller.rb +44 -0
- data/lib/vines/services/controller/messages_controller.rb +66 -0
- data/lib/vines/services/controller/probes_controller.rb +45 -0
- data/lib/vines/services/controller/services_controller.rb +81 -0
- data/lib/vines/services/controller/subscriptions_controller.rb +39 -0
- data/lib/vines/services/controller/systems_controller.rb +45 -0
- data/lib/vines/services/controller/transfers_controller.rb +58 -0
- data/lib/vines/services/controller/uploads_controller.rb +62 -0
- data/lib/vines/services/controller/users_controller.rb +127 -0
- data/lib/vines/services/core_ext/blather.rb +46 -0
- data/lib/vines/services/core_ext/couchrest.rb +33 -0
- data/lib/vines/services/indexer.rb +195 -0
- data/lib/vines/services/priority_queue.rb +94 -0
- data/lib/vines/services/roster.rb +70 -0
- data/lib/vines/services/storage/couchdb/fragment.rb +23 -0
- data/lib/vines/services/storage/couchdb/service.rb +170 -0
- data/lib/vines/services/storage/couchdb/system.rb +141 -0
- data/lib/vines/services/storage/couchdb/upload.rb +66 -0
- data/lib/vines/services/storage/couchdb/user.rb +137 -0
- data/lib/vines/services/storage/couchdb/vcard.rb +13 -0
- data/lib/vines/services/storage/couchdb.rb +157 -0
- data/lib/vines/services/storage.rb +33 -0
- data/lib/vines/services/throttle.rb +26 -0
- data/lib/vines/services/version.rb +7 -0
- data/lib/vines/services/vql/compiler.rb +94 -0
- data/lib/vines/services/vql/vql.citrus +115 -0
- data/lib/vines/services/vql/vql.rb +186 -0
- data/lib/vines/services.rb +71 -0
- data/test/config_test.rb +242 -0
- data/test/priority_queue_test.rb +23 -0
- data/test/storage/couchdb_test.rb +30 -0
- data/test/vql/compiler_test.rb +96 -0
- data/test/vql/vql_test.rb +233 -0
- data/web/coffeescripts/api.coffee +51 -0
- data/web/coffeescripts/commands.coffee +18 -0
- data/web/coffeescripts/files.coffee +315 -0
- data/web/coffeescripts/init.coffee +21 -0
- data/web/coffeescripts/services.coffee +356 -0
- data/web/coffeescripts/setup.coffee +503 -0
- data/web/coffeescripts/systems.coffee +371 -0
- data/web/images/default-service.png +0 -0
- data/web/images/linux.png +0 -0
- data/web/images/mac.png +0 -0
- data/web/images/run.png +0 -0
- data/web/images/windows.png +0 -0
- data/web/index.html +17 -0
- data/web/stylesheets/common.css +52 -0
- data/web/stylesheets/files.css +218 -0
- data/web/stylesheets/services.css +181 -0
- data/web/stylesheets/setup.css +117 -0
- data/web/stylesheets/systems.css +142 -0
- metadata +230 -0
data/test/config_test.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'vines/services'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class ConfigTest < MiniTest::Unit::TestCase
|
7
|
+
def teardown
|
8
|
+
FileUtils.rm(Dir.glob('localhost-*.db'))
|
9
|
+
%w[data uploads].each do |dir|
|
10
|
+
FileUtils.remove_dir(dir) if File.exist?(dir)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_missing_host_raises
|
15
|
+
assert_raises(RuntimeError) do
|
16
|
+
Vines::Services::Config.new do
|
17
|
+
# missing domain
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_multiple_domains_raises
|
23
|
+
assert_raises(RuntimeError) do
|
24
|
+
Vines::Services::Config.new do
|
25
|
+
host 'vines.wonderland.lit', 'vines.wonderland.lit' do
|
26
|
+
upstream 'localhost', 5347, 'secr3t'
|
27
|
+
storage 'couchdb' do
|
28
|
+
host 'localhost'
|
29
|
+
port 5984
|
30
|
+
database 'wonderland_lit'
|
31
|
+
tls false
|
32
|
+
username ''
|
33
|
+
password ''
|
34
|
+
index_dir '.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_configure
|
42
|
+
config = Vines::Services::Config.configure do
|
43
|
+
host 'vines.wonderland.lit' do
|
44
|
+
upstream 'localhost', 5347, 'secr3t'
|
45
|
+
storage 'couchdb' do
|
46
|
+
host 'localhost'
|
47
|
+
port 5984
|
48
|
+
database 'wonderland_lit'
|
49
|
+
tls false
|
50
|
+
username ''
|
51
|
+
password ''
|
52
|
+
index_dir '.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
refute_nil config
|
57
|
+
assert_same config, Vines::Services::Config.instance
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_default_upload_directory
|
61
|
+
config = Vines::Services::Config.configure do
|
62
|
+
host 'vines.wonderland.lit' do
|
63
|
+
upstream 'localhost', 5347, 'secr3t'
|
64
|
+
storage 'couchdb' do
|
65
|
+
host 'localhost'
|
66
|
+
port 5984
|
67
|
+
database 'wonderland_lit'
|
68
|
+
tls false
|
69
|
+
username ''
|
70
|
+
password ''
|
71
|
+
index_dir '.'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
assert File.exist?('data/upload')
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_custom_upload_directory
|
79
|
+
config = Vines::Services::Config.configure do
|
80
|
+
host 'vines.wonderland.lit' do
|
81
|
+
upstream 'localhost', 5347, 'secr3t'
|
82
|
+
uploads 'uploads'
|
83
|
+
storage 'couchdb' do
|
84
|
+
host 'localhost'
|
85
|
+
port 5984
|
86
|
+
database 'wonderland_lit'
|
87
|
+
tls false
|
88
|
+
username ''
|
89
|
+
password ''
|
90
|
+
index_dir '.'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
assert File.exist?('uploads')
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_missing_upstream_raises
|
98
|
+
assert_raises(RuntimeError) do
|
99
|
+
Vines::Services::Config.new do
|
100
|
+
host 'vines.wonderland.lit' do
|
101
|
+
storage 'couchdb' do
|
102
|
+
host 'localhost'
|
103
|
+
port 5984
|
104
|
+
database 'wonderland_lit'
|
105
|
+
tls false
|
106
|
+
username ''
|
107
|
+
password ''
|
108
|
+
index_dir '.'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_invalid_upstream_raises
|
116
|
+
assert_raises(RuntimeError) do
|
117
|
+
Vines::Services::Config.new do
|
118
|
+
host 'vines.wonderland.lit' do
|
119
|
+
upstream 'localhost', 5347, nil
|
120
|
+
storage 'couchdb' do
|
121
|
+
host 'localhost'
|
122
|
+
port 5984
|
123
|
+
database 'wonderland_lit'
|
124
|
+
tls false
|
125
|
+
username ''
|
126
|
+
password ''
|
127
|
+
index_dir '.'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_missing_storage_raises
|
135
|
+
assert_raises(RuntimeError) do
|
136
|
+
Vines::Services::Config.new do
|
137
|
+
host 'vines.wonderland.lit' do
|
138
|
+
upstream 'localhost', 5347, 'secr3t'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_duplicate_storage_raises
|
145
|
+
assert_raises(RuntimeError) do
|
146
|
+
Vines::Services::Config.new do
|
147
|
+
host 'vines.wonderland.lit' do
|
148
|
+
upstream 'localhost', 5347, 'secr3t'
|
149
|
+
storage 'couchdb' do
|
150
|
+
host 'localhost'
|
151
|
+
port 5984
|
152
|
+
database 'wonderland_lit'
|
153
|
+
tls false
|
154
|
+
username ''
|
155
|
+
password ''
|
156
|
+
index_dir '.'
|
157
|
+
end
|
158
|
+
storage 'couchdb' do
|
159
|
+
host 'localhost'
|
160
|
+
port 5984
|
161
|
+
database 'wonderland_lit'
|
162
|
+
tls false
|
163
|
+
username ''
|
164
|
+
password ''
|
165
|
+
index_dir '.'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_multiple_domains
|
173
|
+
config = Vines::Services::Config.new do
|
174
|
+
host 'vines.wonderland.lit' do
|
175
|
+
upstream 'localhost', 5347, 'secr3t'
|
176
|
+
storage 'couchdb' do
|
177
|
+
host 'localhost'
|
178
|
+
port 5984
|
179
|
+
database 'wonderland_lit'
|
180
|
+
tls false
|
181
|
+
username ''
|
182
|
+
password ''
|
183
|
+
index_dir '.'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
host 'vines.verona.lit' do
|
187
|
+
upstream 'localhost', 5347, 'secr3t'
|
188
|
+
storage 'couchdb' do
|
189
|
+
host 'localhost'
|
190
|
+
port 5984
|
191
|
+
database 'verona_lit'
|
192
|
+
tls false
|
193
|
+
username ''
|
194
|
+
password ''
|
195
|
+
index_dir '.'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
assert_equal 2, config.vhosts.size
|
200
|
+
assert_equal 'vines.wonderland.lit', config.vhosts['vines.wonderland.lit'].name
|
201
|
+
assert_equal 'vines.verona.lit', config.vhosts['vines.verona.lit'].name
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_invalid_log_level
|
205
|
+
assert_raises(RuntimeError) do
|
206
|
+
config = Vines::Services::Config.new do
|
207
|
+
log 'bogus'
|
208
|
+
host 'vines.wonderland.lit' do
|
209
|
+
upstream 'localhost', 5347, 'secr3t'
|
210
|
+
storage 'couchdb' do
|
211
|
+
host 'localhost'
|
212
|
+
port 5984
|
213
|
+
database 'wonderland_lit'
|
214
|
+
tls false
|
215
|
+
username ''
|
216
|
+
password ''
|
217
|
+
index_dir '.'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_valid_log_level
|
225
|
+
config = Vines::Services::Config.new do
|
226
|
+
log :error
|
227
|
+
host 'vines.wonderland.lit' do
|
228
|
+
upstream 'localhost', 5347, 'secr3t'
|
229
|
+
storage 'couchdb' do
|
230
|
+
host 'localhost'
|
231
|
+
port 5984
|
232
|
+
database 'wonderland_lit'
|
233
|
+
tls false
|
234
|
+
username ''
|
235
|
+
password ''
|
236
|
+
index_dir '.'
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
assert_equal Logger::ERROR, Class.new.extend(Vines::Log).log.level
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'vines/services'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class PriorityQueueTest < MiniTest::Unit::TestCase
|
7
|
+
def test_queue_push_sorts
|
8
|
+
queue = Vines::Services::PriorityQueue.new
|
9
|
+
nums = (0..10_000).map {|i| rand(10_000) }
|
10
|
+
popped = []
|
11
|
+
EM.run do
|
12
|
+
nums.each {|num| queue.push(num) }
|
13
|
+
assert_equal nums.size, queue.size
|
14
|
+
(nums.size - 1).times do
|
15
|
+
queue.pop {|item| popped << item }
|
16
|
+
end
|
17
|
+
queue.pop {|item| popped << item; EM.stop }
|
18
|
+
end
|
19
|
+
assert queue.empty?
|
20
|
+
assert_equal 0, queue.size
|
21
|
+
assert_equal nums.sort {|a, b| -(a <=> b) }, popped
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'vines/services'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class CouchDBTest < MiniTest::Unit::TestCase
|
7
|
+
def teardown
|
8
|
+
FileUtils.rm(Dir.glob('localhost-*.db'))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_init
|
12
|
+
assert_raises(RuntimeError) { Vines::Services::Storage::CouchDB.new {} }
|
13
|
+
assert_raises(RuntimeError) { Vines::Services::Storage::CouchDB.new { host 'localhost' } }
|
14
|
+
assert_raises(RuntimeError) do
|
15
|
+
Vines::Services::Storage::CouchDB.new do
|
16
|
+
host 'localhost'
|
17
|
+
port '5984'
|
18
|
+
database 'test'
|
19
|
+
index_dir "./bogus/#{rand(1_000_000)}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# shouldn't raise an error
|
23
|
+
Vines::Services::Storage::CouchDB.new do
|
24
|
+
host 'localhost'
|
25
|
+
port '5984'
|
26
|
+
database 'test'
|
27
|
+
index_dir '.'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'vines/services'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class CompilerTest < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@compiler = Vines::Services::VQL::Compiler.new
|
9
|
+
@query = "foo.bar like ' spam ' and (age < 42 or age > 99)"
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_to_js_raises
|
13
|
+
assert_raises(ArgumentError) { @compiler.to_js(nil) }
|
14
|
+
assert_raises(ArgumentError) { @compiler.to_js(' ') }
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_to_sql_raises
|
18
|
+
assert_raises(ArgumentError) { @compiler.to_sql(nil) }
|
19
|
+
assert_raises(ArgumentError) { @compiler.to_sql(' ') }
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_to_js
|
23
|
+
js = @compiler.to_js(@query)
|
24
|
+
expected = %q{
|
25
|
+
function(doc) {
|
26
|
+
if (doc.type != 'System') return;
|
27
|
+
try {
|
28
|
+
var match = (doc.ohai.foo.bar.indexOf(' spam ') !== -1) && ((doc.ohai.age < 42) || (doc.ohai.age > 99));
|
29
|
+
if (match) {
|
30
|
+
var name = doc['_id'].replace('system:', '');
|
31
|
+
var os = doc.ohai.kernel.os.toLowerCase().replace('gnu/', '');
|
32
|
+
emit(name, os);
|
33
|
+
}
|
34
|
+
} catch(e) {
|
35
|
+
log(e.message);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
assert_equal strip(expected), strip(js)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_to_full_js
|
43
|
+
service = Struct.new(:id, :code)
|
44
|
+
services = [service.new(42, 'fqdn is "www"'), service.new(1, 'age > 42')]
|
45
|
+
js = @compiler.to_full_js(services)
|
46
|
+
expected = %q{
|
47
|
+
function(doc) {
|
48
|
+
if (doc.type != 'System') return;
|
49
|
+
var name = doc['_id'].replace('system:', '');
|
50
|
+
var os = doc.ohai.kernel.os.toLowerCase().replace('gnu/', '');
|
51
|
+
|
52
|
+
try {
|
53
|
+
var match = (doc.ohai.fqdn === "www");
|
54
|
+
if (match) {
|
55
|
+
emit([0, '42'], {name: name, os: os});
|
56
|
+
emit([1, name], '42');
|
57
|
+
}
|
58
|
+
} catch(e) {
|
59
|
+
log(e.message);
|
60
|
+
}
|
61
|
+
|
62
|
+
try {
|
63
|
+
var match = (doc.ohai.age > 42);
|
64
|
+
if (match) {
|
65
|
+
emit([0, '1'], {name: name, os: os});
|
66
|
+
emit([1, name], '1');
|
67
|
+
}
|
68
|
+
} catch(e) {
|
69
|
+
log(e.message);
|
70
|
+
}
|
71
|
+
|
72
|
+
}
|
73
|
+
}
|
74
|
+
assert_equal strip(expected), strip(js)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_to_sql
|
78
|
+
sql, params = @compiler.to_sql(@query)
|
79
|
+
expected = %q{
|
80
|
+
select name, os from systems
|
81
|
+
inner join attributes a0 on id=a0.system_id and a0.key=?
|
82
|
+
inner join attributes a1 on id=a1.system_id and a1.key=?
|
83
|
+
inner join attributes a2 on id=a2.system_id and a2.key=?
|
84
|
+
where a0.value like ? and (cast(a1.value as number) < ? or cast(a2.value as number) > ?)
|
85
|
+
order by name
|
86
|
+
}
|
87
|
+
assert_equal strip(expected), strip(sql)
|
88
|
+
assert_equal ['foo.bar', 'age', 'age', '% spam %', '42', '99'], params
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def strip(str)
|
94
|
+
str.strip.gsub(/[ ]{2,}/, '')
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'vines/services'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class VinesQLTest < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
Citrus.load(File.expand_path('../../../lib/vines/services/vql/vql.citrus', __FILE__))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_negative_number
|
12
|
+
match = VinesQL.parse('foo < -42 ')
|
13
|
+
assert_equal '(doc.ohai.foo < -42)', match.js
|
14
|
+
assert_equal 'cast(value as number) < ?', match.sql
|
15
|
+
assert_equal ['foo', '-42'], match.params
|
16
|
+
|
17
|
+
match = VinesQL.parse('foo < -0.5 ')
|
18
|
+
assert_equal '(doc.ohai.foo < -0.5)', match.js
|
19
|
+
assert_equal 'cast(value as number) < ?', match.sql
|
20
|
+
assert_equal ['foo', '-0.5'], match.params
|
21
|
+
|
22
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 < -') }
|
23
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 < --1') }
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_less_than
|
27
|
+
match = VinesQL.parse('foo < 1 ')
|
28
|
+
assert_equal '(doc.ohai.foo < 1)', match.js
|
29
|
+
assert_equal 'cast(value as number) < ?', match.sql
|
30
|
+
assert_equal ['foo', '1'], match.params
|
31
|
+
|
32
|
+
match = VinesQL.parse('foo<=1 ')
|
33
|
+
assert_equal '(doc.ohai.foo <= 1)', match.js
|
34
|
+
assert_equal 'cast(value as number) <= ?', match.sql
|
35
|
+
assert_equal ['foo', '1'], match.params
|
36
|
+
|
37
|
+
match = VinesQL.parse('foo < 1.5 ')
|
38
|
+
assert_equal '(doc.ohai.foo < 1.5)', match.js
|
39
|
+
assert_equal 'cast(value as number) < ?', match.sql
|
40
|
+
assert_equal ['foo', '1.5'], match.params
|
41
|
+
|
42
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 < 42') }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_greater_than
|
46
|
+
match = VinesQL.parse('foo > 1')
|
47
|
+
assert_equal '(doc.ohai.foo > 1)', match.js
|
48
|
+
assert_equal 'cast(value as number) > ?', match.sql
|
49
|
+
assert_equal ['foo', '1'], match.params
|
50
|
+
|
51
|
+
match = VinesQL.parse('foo>=1')
|
52
|
+
assert_equal '(doc.ohai.foo >= 1)', match.js
|
53
|
+
assert_equal 'cast(value as number) >= ?', match.sql
|
54
|
+
assert_equal ['foo', '1'], match.params
|
55
|
+
|
56
|
+
match = VinesQL.parse('foo > 1.5')
|
57
|
+
assert_equal '(doc.ohai.foo > 1.5)', match.js
|
58
|
+
assert_equal 'cast(value as number) > ?', match.sql
|
59
|
+
assert_equal ['foo', '1.5'], match.params
|
60
|
+
|
61
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 > 42') }
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_is
|
65
|
+
match = VinesQL.parse('foo.bar is 13')
|
66
|
+
assert_equal '(doc.ohai.foo.bar === 13)', match.js
|
67
|
+
assert_equal 'value=?', match.sql
|
68
|
+
assert_equal ['foo.bar', '13'], match.params
|
69
|
+
|
70
|
+
match = VinesQL.parse('foo.bar is 13.5')
|
71
|
+
assert_equal '(doc.ohai.foo.bar === 13.5)', match.js
|
72
|
+
assert_equal 'value=?', match.sql
|
73
|
+
assert_equal ['foo.bar', '13.5'], match.params
|
74
|
+
|
75
|
+
match = VinesQL.parse('foo.bar is true')
|
76
|
+
assert_equal '(doc.ohai.foo.bar === true)', match.js
|
77
|
+
assert_equal 'value=?', match.sql
|
78
|
+
assert_equal ['foo.bar', 'true'], match.params
|
79
|
+
|
80
|
+
match = VinesQL.parse('foo.bar is false')
|
81
|
+
assert_equal '(doc.ohai.foo.bar === false)', match.js
|
82
|
+
assert_equal 'value=?', match.sql
|
83
|
+
assert_equal ['foo.bar', 'false'], match.params
|
84
|
+
|
85
|
+
match = VinesQL.parse('foo.bar is null')
|
86
|
+
assert_equal '(doc.ohai.foo.bar === null)', match.js
|
87
|
+
assert_equal 'value is null', match.sql
|
88
|
+
assert_equal ['foo.bar'], match.params
|
89
|
+
|
90
|
+
match = VinesQL.parse('foo.bar is " bar "')
|
91
|
+
assert_equal '(doc.ohai.foo.bar === " bar ")', match.js
|
92
|
+
assert_equal 'value=?', match.sql
|
93
|
+
assert_equal ['foo.bar', ' bar '], match.params
|
94
|
+
|
95
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 is 42') }
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_is_not
|
99
|
+
match = VinesQL.parse('foo.bar is not 13')
|
100
|
+
assert_equal '(doc.ohai.foo.bar !== 13)', match.js
|
101
|
+
assert_equal 'value <> ?', match.sql
|
102
|
+
assert_equal ['foo.bar', '13'], match.params
|
103
|
+
|
104
|
+
match = VinesQL.parse('foo.bar is not 13.5')
|
105
|
+
assert_equal '(doc.ohai.foo.bar !== 13.5)', match.js
|
106
|
+
assert_equal 'value <> ?', match.sql
|
107
|
+
assert_equal ['foo.bar', '13.5'], match.params
|
108
|
+
|
109
|
+
match = VinesQL.parse('foo.bar is not true')
|
110
|
+
assert_equal '(doc.ohai.foo.bar !== true)', match.js
|
111
|
+
assert_equal 'value <> ?', match.sql
|
112
|
+
assert_equal ['foo.bar', 'true'], match.params
|
113
|
+
|
114
|
+
match = VinesQL.parse('foo.bar is not false')
|
115
|
+
assert_equal '(doc.ohai.foo.bar !== false)', match.js
|
116
|
+
assert_equal 'value <> ?', match.sql
|
117
|
+
assert_equal ['foo.bar', 'false'], match.params
|
118
|
+
|
119
|
+
match = VinesQL.parse('foo.bar is not null')
|
120
|
+
assert_equal '(doc.ohai.foo.bar !== null)', match.js
|
121
|
+
assert_equal 'value is not null', match.sql
|
122
|
+
assert_equal ['foo.bar'], match.params
|
123
|
+
|
124
|
+
match = VinesQL.parse('foo.bar is not " bar "')
|
125
|
+
assert_equal '(doc.ohai.foo.bar !== " bar ")', match.js
|
126
|
+
assert_equal 'value <> ?', match.sql
|
127
|
+
assert_equal ['foo.bar', ' bar '], match.params
|
128
|
+
|
129
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 is not 42') }
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_like
|
133
|
+
match = VinesQL.parse('foo.bar like " bar "')
|
134
|
+
assert_equal '(doc.ohai.foo.bar.indexOf(" bar ") !== -1)', match.js
|
135
|
+
assert_equal 'value like ?', match.sql
|
136
|
+
assert_equal ['foo.bar', '% bar %'], match.params
|
137
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 like 42') }
|
138
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('foo.bar like 42') }
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_not_like
|
142
|
+
match = VinesQL.parse('foo.bar not like " bar "')
|
143
|
+
assert_equal '(doc.ohai.foo.bar.indexOf(" bar ") === -1)', match.js
|
144
|
+
assert_equal 'value not like ?', match.sql
|
145
|
+
assert_equal ['foo.bar', '% bar %'], match.params
|
146
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 not like 42') }
|
147
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('foo.bar not like 42') }
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_starts_with
|
151
|
+
match = VinesQL.parse('foo.bar starts with " bar "')
|
152
|
+
assert_equal '(doc.ohai.foo.bar.lastIndexOf(" bar ", 0) === 0)', match.js
|
153
|
+
assert_equal 'value like ?', match.sql
|
154
|
+
assert_equal ['foo.bar', ' bar %'], match.params
|
155
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 starts with 42') }
|
156
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('foo.bar starts with 42') }
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_ends_with
|
160
|
+
match = VinesQL.parse('foo.bar ends with " bar "')
|
161
|
+
assert_equal '(doc.ohai.foo.bar.match(" bar " + \'$\'))', match.js
|
162
|
+
assert_equal 'value like ?', match.sql
|
163
|
+
assert_equal ['foo.bar', '% bar '], match.params
|
164
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('12 ends with 42') }
|
165
|
+
assert_raises(Citrus::ParseError) { VinesQL.parse('foo.bar ends with 42') }
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_group
|
169
|
+
match = VinesQL.parse('(foo is 12 or bar is 42) and spam > 0')
|
170
|
+
assert_equal '((doc.ohai.foo === 12) || (doc.ohai.bar === 42)) && (doc.ohai.spam > 0)', match.js
|
171
|
+
assert_equal '(value=? or value=?) and cast(value as number) > ?', match.sql
|
172
|
+
assert_equal ['foo', '12', 'bar', '42', 'spam', '0'], match.params
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_complex_syntax_with_extra_whitespace
|
176
|
+
syntax = %q{
|
177
|
+
name like 'abc' and
|
178
|
+
(
|
179
|
+
address.city is 'LA' or
|
180
|
+
address.city is not 'NYC'
|
181
|
+
) or
|
182
|
+
ssn not like '123'
|
183
|
+
and status starts with "valid"
|
184
|
+
and status ends with " test "
|
185
|
+
or ( age > 42 )
|
186
|
+
}.strip
|
187
|
+
|
188
|
+
js =
|
189
|
+
"(doc.ohai.name.indexOf('abc') !== -1) && " +
|
190
|
+
"((doc.ohai.address.city === 'LA') || (doc.ohai.address.city !== 'NYC')) || " +
|
191
|
+
"(doc.ohai.ssn.indexOf('123') === -1) && " +
|
192
|
+
"(doc.ohai.status.lastIndexOf(\"valid\", 0) === 0) && " +
|
193
|
+
"(doc.ohai.status.match(\" test \" + '$')) || " +
|
194
|
+
"((doc.ohai.age > 42))"
|
195
|
+
|
196
|
+
sql =
|
197
|
+
"value like ? and " +
|
198
|
+
"(value=? or value <> ?) or " +
|
199
|
+
"value not like ? and " +
|
200
|
+
"value like ? and " +
|
201
|
+
"value like ? or " +
|
202
|
+
"(cast(value as number) > ?)"
|
203
|
+
|
204
|
+
params = [
|
205
|
+
'name', '%abc%',
|
206
|
+
'address.city', 'LA',
|
207
|
+
'address.city', 'NYC',
|
208
|
+
'ssn', '%123%',
|
209
|
+
'status', 'valid%',
|
210
|
+
'status', '% test ',
|
211
|
+
'age', '42']
|
212
|
+
|
213
|
+
match = VinesQL.parse(syntax)
|
214
|
+
assert_equal js, match.js
|
215
|
+
assert_equal sql, match.sql
|
216
|
+
assert_equal params, match.params
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_invalid_syntax
|
220
|
+
assert_raises(Citrus::ParseError) do
|
221
|
+
VinesQL.parse('some bogus syntax!')
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_params_are_strings_not_matches
|
226
|
+
match = VinesQL.parse('foo < 42 and bar is "foo" and spam is not true')
|
227
|
+
assert_equal ['foo', '42', 'bar', 'foo', 'spam', 'true'], match.params
|
228
|
+
# were incorrectly returned as Citrus::Match objects
|
229
|
+
match.params.each do |p|
|
230
|
+
assert_equal String, p.class
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Api
|
2
|
+
constructor: (@session) ->
|
3
|
+
|
4
|
+
jid: -> "vines.#{@session.bareJid().split('@')[1]}"
|
5
|
+
|
6
|
+
get: (ns, criteria, callback) ->
|
7
|
+
node = @session.xml """
|
8
|
+
<iq id="#{@session.uniqueId()}" to="#{this.jid()}" type="get">
|
9
|
+
<query xmlns="#{ns}"/>
|
10
|
+
</iq>
|
11
|
+
"""
|
12
|
+
$('query', node).attr key, value for key, value of criteria
|
13
|
+
|
14
|
+
@session.sendIQ node, (result) =>
|
15
|
+
ok = $(result).attr('type') == 'result'
|
16
|
+
return unless ok
|
17
|
+
callback JSON.parse $('query', result).text()
|
18
|
+
|
19
|
+
get2: (ns, body, callback) ->
|
20
|
+
node = @session.xml """
|
21
|
+
<iq id="#{@session.uniqueId()}" to="#{this.jid()}" type="get">
|
22
|
+
<query xmlns="#{ns}"/>
|
23
|
+
</iq>
|
24
|
+
"""
|
25
|
+
$('query', node).text body
|
26
|
+
@session.sendIQ node, (result) =>
|
27
|
+
ok = $(result).attr('type') == 'result'
|
28
|
+
return unless ok
|
29
|
+
callback JSON.parse $('query', result).text()
|
30
|
+
|
31
|
+
remove: (ns, id, callback) ->
|
32
|
+
node = @session.xml """
|
33
|
+
<iq id="#{@session.uniqueId()}" to="#{this.jid()}" type="set">
|
34
|
+
<query xmlns="#{ns}" action="delete" id=""/>
|
35
|
+
</iq>
|
36
|
+
"""
|
37
|
+
$('query', node).attr 'id', id
|
38
|
+
@session.sendIQ node, callback
|
39
|
+
|
40
|
+
save: (ns, obj, callback) ->
|
41
|
+
node = @session.xml """
|
42
|
+
<iq id="#{@session.uniqueId()}" to="#{this.jid()}" type="set">
|
43
|
+
<query xmlns="#{ns}"/>
|
44
|
+
</iq>
|
45
|
+
"""
|
46
|
+
$('query', node).text JSON.stringify obj
|
47
|
+
|
48
|
+
@session.sendIQ node, (result) =>
|
49
|
+
ok = $(result).attr('type') == 'result'
|
50
|
+
return unless ok
|
51
|
+
callback JSON.parse $('query', result).text()
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Commands
|
2
|
+
constructor: ->
|
3
|
+
@buf = []
|
4
|
+
@index = 0
|
5
|
+
|
6
|
+
prev: ->
|
7
|
+
val = @buf[--@index]
|
8
|
+
@index = -1 unless val
|
9
|
+
val || ''
|
10
|
+
|
11
|
+
next: ->
|
12
|
+
val = @buf[++@index]
|
13
|
+
@index = @buf.length unless val
|
14
|
+
val || ''
|
15
|
+
|
16
|
+
push: (cmd) ->
|
17
|
+
@buf.push cmd
|
18
|
+
@index = @buf.length
|