kijirest-client 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +89 -0
- data/Rakefile +1 -0
- data/kijirest-client.gemspec +40 -0
- data/lib/kijirest/client.rb +265 -0
- data/lib/kijirest/server.rb +159 -0
- data/lib/kijirest/version.rb +20 -0
- metadata +81 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: !binary |-
|
|
4
|
+
ZjM4ZWM0ZDMzZTQzZTNmNzI4MzBlMzE4OGI0Y2ZjMWViZjY0NzFkYQ==
|
|
5
|
+
data.tar.gz: !binary |-
|
|
6
|
+
NGE3MjdiZjMxMDFlMzllYmE2NTk0MmQ3OWQyMjZhOGI4NTQ4ZTdlNw==
|
|
7
|
+
!binary "U0hBNTEy":
|
|
8
|
+
metadata.gz: !binary |-
|
|
9
|
+
OGFiY2U1YzE5MTQ2MmQ5YWU2Yzc1NzY1Y2NhMmJiNGY2ODUwNjJiNTM0ODEx
|
|
10
|
+
NzgxNjk1MDc1YzNmMzRlODA0MjIxMTBhNjMzM2I4YzI4ZDBkYmE4YzliMTA2
|
|
11
|
+
ZWE4ZjU0NjRhMmFiMzc4YjhmZmVkZjBlODQyMmU3ZDQ0OThlNTk=
|
|
12
|
+
data.tar.gz: !binary |-
|
|
13
|
+
YzBjNDAyOTFkMmVhMTUwYTliNzQ0NzEzODNmMWFhOTYyYWRmMmVlNGJjYzdm
|
|
14
|
+
ZDgzOTVlMmZkZTZkZjUyYmUzNDRkOTc4NzRhMTQ1ZDM0MDg2MWY5ODQ3NmVj
|
|
15
|
+
NWVjMzEwYTQyNTVkNGVmNjZkNWNhNDAwN2YxNzA0MmU0N2VmMzU=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 amit
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# kijirest-client gem
|
|
2
|
+
|
|
3
|
+
This gem provides wrapper classes around accessing and managing a KijiREST server. There
|
|
4
|
+
are two modules:
|
|
5
|
+
|
|
6
|
+
* KijiRest::Client - Provides a client interface for accessing KijiREST
|
|
7
|
+
* KijiRest::Server - Provides a server side interface for starting/stopping a local KijiREST
|
|
8
|
+
server assuming that the user running this code is the same user who can start/stop the server.
|
|
9
|
+
|
|
10
|
+
For more information about the KijiREST project, please visit
|
|
11
|
+
[https://github.com/kijiproject/kiji-rest](https://github.com/kijiproject/kiji-rest)
|
|
12
|
+
|
|
13
|
+
# Installation
|
|
14
|
+
|
|
15
|
+
Add this line to your application's Gemfile:
|
|
16
|
+
|
|
17
|
+
gem 'kijirest-client'
|
|
18
|
+
|
|
19
|
+
And then execute:
|
|
20
|
+
|
|
21
|
+
$ bundle install
|
|
22
|
+
|
|
23
|
+
Or install it yourself as:
|
|
24
|
+
|
|
25
|
+
$ gem install kijirest-client
|
|
26
|
+
|
|
27
|
+
# Usage
|
|
28
|
+
|
|
29
|
+
## KijiRest::Client
|
|
30
|
+
|
|
31
|
+
require 'kijirest/client'
|
|
32
|
+
|
|
33
|
+
# Construct a new client pointing at localhost:8080
|
|
34
|
+
c=KijiRest::Client.new("http://localhost:8080")
|
|
35
|
+
|
|
36
|
+
# List instances
|
|
37
|
+
instances = c.instances
|
|
38
|
+
|
|
39
|
+
# List tables for a known instance ("dota2")
|
|
40
|
+
tables = c.tables("dota2")
|
|
41
|
+
|
|
42
|
+
# Fetch the rowKey
|
|
43
|
+
rowkey = c.rowkey("dota2","matches",8675309)
|
|
44
|
+
|
|
45
|
+
# Let's create a new entity with
|
|
46
|
+
new_eid=KijiRest::Client.format_entity_id(8675310)
|
|
47
|
+
|
|
48
|
+
# Fetch the row by the actual rowkey
|
|
49
|
+
row_hash=c.row("dota2","matches",rowkey)
|
|
50
|
+
|
|
51
|
+
# Change the value of the cell whose columnQualifier is "cluster" to 300
|
|
52
|
+
row_hash["cells"].each {|cell|
|
|
53
|
+
if cell["columnQualifier"] == "cluster"
|
|
54
|
+
cell["value"] = 300
|
|
55
|
+
break
|
|
56
|
+
end
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Set the entity_id key to the new entity_id we wish to create
|
|
60
|
+
row_hash[KijiRest::Client::ENTITY_ID] = new_eid
|
|
61
|
+
|
|
62
|
+
# Write the new row. The final argument is what dictates whether or not to strip
|
|
63
|
+
# timestamps from an existing row_hash or not. This is handy if you are using the results
|
|
64
|
+
# from the GET of another row to write a new row.
|
|
65
|
+
puts c.write_row("dota2","matches",row_hash,true)
|
|
66
|
+
|
|
67
|
+
## KijiRest::Server
|
|
68
|
+
|
|
69
|
+
require 'kijirest/server'
|
|
70
|
+
|
|
71
|
+
# Construct a new server object passing in the location where the kiji-rest application
|
|
72
|
+
# resides. Note: The owner of the kiji-rest folder must be the same user who is running
|
|
73
|
+
# this code else an exception will be raised.
|
|
74
|
+
s = KijiRest::Server.new("/path/to/kiji-rest")
|
|
75
|
+
|
|
76
|
+
# Launch the KijiREST server setting the cluster to .env and the array of visible instances
|
|
77
|
+
# to ["default"]. The final argument indicates to wait for the server to finish launching
|
|
78
|
+
# before returning.
|
|
79
|
+
s.start(".env",["default"], true)
|
|
80
|
+
|
|
81
|
+
# Get a reference to a new client pointing at
|
|
82
|
+
# http://localhost:<port set in conf/configuration.yml>
|
|
83
|
+
c = s.new_client
|
|
84
|
+
|
|
85
|
+
# Get a list of instances.
|
|
86
|
+
puts c.instances
|
|
87
|
+
|
|
88
|
+
# Let's stop the server and wait for the server to shutdown before returning.
|
|
89
|
+
s.stop(true)
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# (c) Copyright 2013 WibiData, Inc.
|
|
2
|
+
#
|
|
3
|
+
# See the NOTICE file distributed with this work for additional
|
|
4
|
+
# information regarding copyright ownership.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
# coding: utf-8
|
|
19
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
20
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
21
|
+
require 'kijirest/version'
|
|
22
|
+
|
|
23
|
+
Gem::Specification.new do |spec|
|
|
24
|
+
spec.name = "kijirest-client"
|
|
25
|
+
spec.version = KijiRest::VERSION
|
|
26
|
+
spec.authors = ["wibidata"]
|
|
27
|
+
spec.email = ["dev@kiji.org"]
|
|
28
|
+
spec.description = %q{Client for the KIJI REST server}
|
|
29
|
+
spec.summary = %q{Client for the KIJI REST server}
|
|
30
|
+
spec.homepage = "http://www.kiji.org"
|
|
31
|
+
spec.license = "Apache"
|
|
32
|
+
|
|
33
|
+
spec.files = `git ls-files`.split($/)
|
|
34
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
35
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
36
|
+
spec.require_paths = ["lib"]
|
|
37
|
+
|
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
39
|
+
spec.add_development_dependency "rake"
|
|
40
|
+
end
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# (c) Copyright 2013 WibiData, Inc.
|
|
2
|
+
#
|
|
3
|
+
# See the NOTICE file distributed with this work for additional
|
|
4
|
+
# information regarding copyright ownership.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
require "kijirest/version"
|
|
19
|
+
require 'net/http'
|
|
20
|
+
require 'json'
|
|
21
|
+
require 'cgi'
|
|
22
|
+
|
|
23
|
+
module KijiRest
|
|
24
|
+
|
|
25
|
+
# Provides a simple wrapper interface around the KijiREST server. The return of most
|
|
26
|
+
# of the methods are parsed JSON.
|
|
27
|
+
class Client
|
|
28
|
+
|
|
29
|
+
# Some global helpful constants for clients.
|
|
30
|
+
ENTITY_ID = "entityId"
|
|
31
|
+
|
|
32
|
+
# Error class that wraps exceptions thrown by the server
|
|
33
|
+
class KijiRestClientError < StandardError
|
|
34
|
+
attr_reader :json_error_message
|
|
35
|
+
def initialize(json_error)
|
|
36
|
+
@json_error_message = json_error
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_s
|
|
40
|
+
json_error_message.to_json
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Constructor for the KijiRest::Client. The only optional argument is the base_uri
|
|
46
|
+
# defining the location of the server.
|
|
47
|
+
def initialize(base_uri= "http://localhost:8080")
|
|
48
|
+
@base_uri = base_uri
|
|
49
|
+
@version = "v1"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# Returns a list of visible instances
|
|
54
|
+
def instances
|
|
55
|
+
get_json(instances_endpoint)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#
|
|
59
|
+
# Returns the instance description
|
|
60
|
+
# param: instance_name is the name of the instance to describe.
|
|
61
|
+
def instance(instance_name)
|
|
62
|
+
get_json(instance_endpoint(instance_name))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns a single row from the given table.
|
|
66
|
+
# param: instance_name is the name of the instance
|
|
67
|
+
# param: table_name is the name of the table
|
|
68
|
+
# param: rowkey is the rowkey to fetch
|
|
69
|
+
# param: filters is a hash containing any filters to apply which will get translated as
|
|
70
|
+
# query parameters in the REST call.
|
|
71
|
+
def row(instance_name, table_name, rowkey, filters= {})
|
|
72
|
+
get_json("#{rows_endpoint(instance_name, table_name)}/#{rowkey}", filters)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns a list of the tables in the given instance.
|
|
76
|
+
# param: instance_name is the name of the instance
|
|
77
|
+
def tables(instance_name)
|
|
78
|
+
get_json(tables_endpoint(instance_name))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns the table layout for a given table.
|
|
82
|
+
# param: instance_name is the name of the instance
|
|
83
|
+
# param: table_name is the name of the table
|
|
84
|
+
def table(instance_name, table_name)
|
|
85
|
+
get_json(table_endpoint(instance_name, table_name))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Performs a scan (via HTTP streaming) yielding each row to the given block.
|
|
89
|
+
# param: instance_name is the name of the instance
|
|
90
|
+
# param: table_name is the name of the table
|
|
91
|
+
# param: filters is a hash containing any filters to apply which will get translated as
|
|
92
|
+
# query parameters in the REST call.
|
|
93
|
+
# param: block is an anonymous block of code that will be given a hash representing the
|
|
94
|
+
# current row.
|
|
95
|
+
def rows(instance_name, table_name, filters= {}, &block)
|
|
96
|
+
if !block
|
|
97
|
+
raise "No block given!"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
url_query_params = filters.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join("&")
|
|
101
|
+
uri = URI(@base_uri)
|
|
102
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
103
|
+
http.request_get("#{rows_endpoint(instance_name, table_name)}?#{url_query_params}") \
|
|
104
|
+
do |response|
|
|
105
|
+
case response
|
|
106
|
+
when Net::HTTPSuccess then
|
|
107
|
+
remainder_json_line = ""
|
|
108
|
+
response.read_body { |chunk|
|
|
109
|
+
if chunk.size > 0
|
|
110
|
+
# Few possible situations: Not sure what can actually happen let's prepare for all
|
|
111
|
+
# 1) chunk is a line that ends with \r\n
|
|
112
|
+
# 2) chunk could be multiple lines and also ends with \r\n
|
|
113
|
+
# 3) chunk has multiples lines but an incomplete last line.
|
|
114
|
+
remainder_json_line = remainder_json_line + chunk
|
|
115
|
+
if remainder_json_line[-2..-1] == "\r\n"
|
|
116
|
+
json_lines = remainder_json_line.split("\r\n")
|
|
117
|
+
json_lines.each {|line|
|
|
118
|
+
yield JSON.parse(line)
|
|
119
|
+
}
|
|
120
|
+
remainder_json_line = ""
|
|
121
|
+
else
|
|
122
|
+
json_lines = remainder_json_line.split("\r\n")
|
|
123
|
+
json_lines.slice(0..-2).each {|line|
|
|
124
|
+
yield JSON.parse(line)
|
|
125
|
+
}
|
|
126
|
+
remainder_json_line = json_lines.last
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
}
|
|
130
|
+
else
|
|
131
|
+
raise_exception(response.body)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def self.format_entity_id(*components)
|
|
137
|
+
# Take non-strings and quote them
|
|
138
|
+
json_entity_id = components.map {|comp|
|
|
139
|
+
if comp.is_a?(Numeric)
|
|
140
|
+
comp.to_s
|
|
141
|
+
else
|
|
142
|
+
"'#{comp}'"
|
|
143
|
+
end
|
|
144
|
+
}.join(",")
|
|
145
|
+
"[#{json_entity_id}]"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the rowkey for a given list of components. Note: string components do NOT need
|
|
149
|
+
# to be escaped strings or specially quoted. When the endpoint is invoked, they will be
|
|
150
|
+
# enclosed in single quotes to satisfy the server-side requirements.
|
|
151
|
+
# param: components are the individual components that comprise the rowkey
|
|
152
|
+
def rowkey(instance_name, table_name, *components)
|
|
153
|
+
return_obj = get_json(entityid_endpoint(instance_name, table_name),
|
|
154
|
+
"eid" => Client::format_entity_id(*components))
|
|
155
|
+
if return_obj
|
|
156
|
+
return_obj["rowKey"]
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Creates or updates a new row by POSTing the row_hash to the server. Will create/update
|
|
161
|
+
# the row specified by the entityId key in the row_hash.
|
|
162
|
+
# param: instance_name is the instance
|
|
163
|
+
# param: table_name is the table
|
|
164
|
+
# param: row_hash is a hash containing the data to POST. Must be of the format:
|
|
165
|
+
# { "entityId" : "[//Component Array]", "cells" : [//Array of cells] }
|
|
166
|
+
# cell looks like:
|
|
167
|
+
# {"columnFamily" : "name",
|
|
168
|
+
# "columnQualifier" : "qualifier",
|
|
169
|
+
# "timestamp" : //timestamp,
|
|
170
|
+
# "value": //value
|
|
171
|
+
# }
|
|
172
|
+
# param: strip_timestamp will remove any timestamp fields from the cells allowing the server
|
|
173
|
+
# to set the timestamp automatically (default false).
|
|
174
|
+
def write_row(instance_name, table_name, row_hash, strip_timestamp= false)
|
|
175
|
+
if strip_timestamp
|
|
176
|
+
local_row_hash = row_hash.clone
|
|
177
|
+
if local_row_hash.include?("cells")
|
|
178
|
+
local_row_hash["cells"].each {|cell|
|
|
179
|
+
# TODO: Remove condition when there's a better way to represent cell level error
|
|
180
|
+
# conditions on GETs.
|
|
181
|
+
cell.delete("timestamp") unless cell["timestamp"] == -1
|
|
182
|
+
}
|
|
183
|
+
end
|
|
184
|
+
row_json = local_row_hash.to_json
|
|
185
|
+
else
|
|
186
|
+
row_json = row_hash.to_json
|
|
187
|
+
end
|
|
188
|
+
uri= URI(@base_uri)
|
|
189
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
190
|
+
response = http.post(rows_endpoint(instance_name, table_name), row_json,
|
|
191
|
+
"Content-Type" => "application/json")
|
|
192
|
+
case response
|
|
193
|
+
when Net::HTTPSuccess then
|
|
194
|
+
JSON.parse(response.body)
|
|
195
|
+
else
|
|
196
|
+
raise_exception(response.body)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
# BEGIN endpoint definitions
|
|
203
|
+
def instances_endpoint
|
|
204
|
+
"/#{@version}/instances"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def instance_endpoint(instance_name)
|
|
208
|
+
"#{instances_endpoint}/#{instance_name}"
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def tables_endpoint(instance_name)
|
|
212
|
+
"#{instance_endpoint(instance_name)}/tables"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def table_endpoint(instance_name, table_name)
|
|
216
|
+
"#{tables_endpoint(instance_name)}/#{table_name}"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def rows_endpoint(instance_name, table_name)
|
|
220
|
+
"#{table_endpoint(instance_name, table_name)}/rows"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def entityid_endpoint(instance_name, table_name)
|
|
224
|
+
"#{table_endpoint(instance_name, table_name)}/entityId"
|
|
225
|
+
end
|
|
226
|
+
# END endpoint definitions
|
|
227
|
+
|
|
228
|
+
# Generic method that will raise a KijiRestClientError that contains a JSON object
|
|
229
|
+
# with the error in question. In case the exception from the server is not JSON encoded,
|
|
230
|
+
# return a generic JSON message with the message sent by the server so that the client
|
|
231
|
+
# has a consistent interface.
|
|
232
|
+
#
|
|
233
|
+
# param: response_body is the contents of the response.
|
|
234
|
+
def raise_exception(response_body)
|
|
235
|
+
begin
|
|
236
|
+
raise KijiRestClientError.new(JSON.parse(response_body))
|
|
237
|
+
rescue
|
|
238
|
+
# If the exception doesn't parse to JSON properly, then return this generic exception
|
|
239
|
+
json_error= {}
|
|
240
|
+
json_error["exception"] = response_body
|
|
241
|
+
json_error["status"] = 500
|
|
242
|
+
json_error["exceptionName"]= "internal_error"
|
|
243
|
+
|
|
244
|
+
raise KijiRestClientError.new(json_error)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Returns a parsed JSON object for the given endpoint and query parameters
|
|
249
|
+
# param: endpoint is the HTTP endpoint
|
|
250
|
+
# param: query_params are query parameters to pass along to the endpoint
|
|
251
|
+
def get_json(endpoint, query_params= {})
|
|
252
|
+
url_query_params = query_params.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join("&")
|
|
253
|
+
uri = URI(@base_uri)
|
|
254
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
255
|
+
response = http.get("#{endpoint}?#{url_query_params}")
|
|
256
|
+
result = nil
|
|
257
|
+
case response
|
|
258
|
+
when Net::HTTPSuccess then
|
|
259
|
+
JSON.parse(response.body)
|
|
260
|
+
else
|
|
261
|
+
raise_exception(response.body)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# (c) Copyright 2013 WibiData, Inc.
|
|
2
|
+
#
|
|
3
|
+
# See the NOTICE file distributed with this work for additional
|
|
4
|
+
# information regarding copyright ownership.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
require "kijirest/version"
|
|
19
|
+
require "kijirest/client"
|
|
20
|
+
require 'yaml'
|
|
21
|
+
require 'open-uri'
|
|
22
|
+
|
|
23
|
+
module KijiRest
|
|
24
|
+
|
|
25
|
+
# Class that wraps the starting/stopping of a KijiREST server.
|
|
26
|
+
# assumes that this is running as a non-privileged user that
|
|
27
|
+
# has permissions to read/write to the location where KIJI_REST is
|
|
28
|
+
# installed
|
|
29
|
+
class Server
|
|
30
|
+
KIJI_REST_HOME = "/opt/wibi/kiji-rest"
|
|
31
|
+
def initialize(kiji_rest_home = KIJI_REST_HOME)
|
|
32
|
+
@kiji_server_location = kiji_rest_home
|
|
33
|
+
unless authorized_to_run?
|
|
34
|
+
raise "#{ENV['USER']} not authorized to run KijiREST server!"
|
|
35
|
+
end
|
|
36
|
+
@http_port = 8080
|
|
37
|
+
reload_configuration!
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Start a server given a zookeeper quorum and array of visible
|
|
41
|
+
# instances. This will modify the configuration.yml and launch the server.
|
|
42
|
+
# param: zk_quorum is the list of zookeeper servers. Default is .env
|
|
43
|
+
# param: visible_instances is an array of instance names that are to be visible by clients
|
|
44
|
+
# of the REST server.
|
|
45
|
+
# param: wait_for_load specifies whether or not to block until the server has come up. If the
|
|
46
|
+
# server fails to come up after some period of time, an exception will be raised.
|
|
47
|
+
def start(zk_quorum = ".env", visible_instances = ["default"], wait_for_load = false)
|
|
48
|
+
if running?
|
|
49
|
+
raise "KijiREST appears to be running."
|
|
50
|
+
else
|
|
51
|
+
#Update the configuration file to reflect the desired options
|
|
52
|
+
dropwizard_config = YAML.load_file(qualify_file("/conf/configuration.yml"))
|
|
53
|
+
|
|
54
|
+
#Do a bit of string checking so that we aren't adding kiji:// prefix twice.
|
|
55
|
+
prefix="kiji://"
|
|
56
|
+
prefix = "" if zk_quorum[0..6] == "kiji://"
|
|
57
|
+
|
|
58
|
+
kiji_uri = "#{prefix}#{zk_quorum}"
|
|
59
|
+
dropwizard_config["cluster"] = kiji_uri
|
|
60
|
+
dropwizard_config["instances"] = visible_instances
|
|
61
|
+
f = File.new(qualify_file("/conf/configuration.yml"), "w")
|
|
62
|
+
f.puts(dropwizard_config.to_yaml)
|
|
63
|
+
f.close
|
|
64
|
+
#Now start the service
|
|
65
|
+
launch_command = qualify_file("/bin/kiji-rest")
|
|
66
|
+
%x{#{launch_command}}
|
|
67
|
+
if wait_for_load
|
|
68
|
+
(1..20).each {|i|
|
|
69
|
+
break if app_running?
|
|
70
|
+
sleep 2
|
|
71
|
+
}
|
|
72
|
+
unless app_running?
|
|
73
|
+
raise "App failed to start!"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Stops the server optionally waiting for the server to shutdown
|
|
80
|
+
# param: wait_for_shutdown determines whether or not to wait for the server to have shutdown
|
|
81
|
+
# before returning.
|
|
82
|
+
def stop(wait_for_shutdown = false)
|
|
83
|
+
pid_file = qualify_file("kiji-rest.pid")
|
|
84
|
+
if File.exist?(pid_file)
|
|
85
|
+
pid_file_obj = File.new(pid_file, "r")
|
|
86
|
+
pid = pid_file_obj.gets
|
|
87
|
+
pid_file_obj.close
|
|
88
|
+
#Kill the pid cleanly first
|
|
89
|
+
%x{kill #{pid}}
|
|
90
|
+
if wait_for_shutdown
|
|
91
|
+
(1..20).each {|i|
|
|
92
|
+
break unless app_running?
|
|
93
|
+
sleep 2
|
|
94
|
+
}
|
|
95
|
+
if app_running?
|
|
96
|
+
%x{kill -9 #{pid}}
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
File.delete(pid_file)
|
|
100
|
+
else
|
|
101
|
+
raise "KijiREST not running!"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns a new client instance.
|
|
106
|
+
def new_client
|
|
107
|
+
Client.new("http://localhost:#{@http_port}")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# Reloads the configuration.yml storing the configuration in a private member
|
|
113
|
+
# variable.
|
|
114
|
+
def reload_configuration!
|
|
115
|
+
@dropwizard_config = YAML.load_file(qualify_file("/conf/configuration.yml"))
|
|
116
|
+
@http_port = 8080
|
|
117
|
+
if @dropwizard_config.include?("http") && @dropwizard_config["http"].include?("port")
|
|
118
|
+
@http_port = @dropwizard_config["http"]["port"]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Checks if this current user is authorized to run the server. A simple check
|
|
123
|
+
# is to ensure that the current user is the owner of a known file (conf/configuration.yml)
|
|
124
|
+
def authorized_to_run?
|
|
125
|
+
File.owned?(qualify_file("/conf/configuration.yml"))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Checks if the application is running by hitting a known URI
|
|
129
|
+
def app_running?
|
|
130
|
+
begin
|
|
131
|
+
f = open("http://localhost:#{@http_port}/v1/instances")
|
|
132
|
+
f.close
|
|
133
|
+
true
|
|
134
|
+
rescue
|
|
135
|
+
false
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Checks if the pid file exists. This will check for the pid file generated
|
|
140
|
+
# when the server is launched by a non-privileged user as well as the pid file
|
|
141
|
+
# generated when this server is launched by /sbin/service.
|
|
142
|
+
def pid_exists?
|
|
143
|
+
local_pid_file = qualify_file("kiji-rest.pid")
|
|
144
|
+
global_pid_file = "/var/run/kiji-rest/kiji-rest.pid"
|
|
145
|
+
File.exists?(local_pid_file) || File.exists?(global_pid_file)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Checks if the server is running by checking the process file (kiji-rest.pid)
|
|
149
|
+
# or the global/root run /var/run/kiji-rest.pid
|
|
150
|
+
def running?
|
|
151
|
+
app_running? || pid_exists?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Returns a fully qualified filename prefixed by the location of kiji_rest_server
|
|
155
|
+
def qualify_file(filename)
|
|
156
|
+
"#{@kiji_server_location}/#{filename}"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# (c) Copyright 2013 WibiData, Inc.
|
|
2
|
+
#
|
|
3
|
+
# See the NOTICE file distributed with this work for additional
|
|
4
|
+
# information regarding copyright ownership.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
module KijiRest
|
|
19
|
+
VERSION = "0.0.1"
|
|
20
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kijirest-client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- wibidata
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2013-05-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.3'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.3'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ! '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ! '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: Client for the KIJI REST server
|
|
42
|
+
email:
|
|
43
|
+
- dev@kiji.org
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- .gitignore
|
|
49
|
+
- Gemfile
|
|
50
|
+
- LICENSE.txt
|
|
51
|
+
- README.md
|
|
52
|
+
- Rakefile
|
|
53
|
+
- kijirest-client.gemspec
|
|
54
|
+
- lib/kijirest/client.rb
|
|
55
|
+
- lib/kijirest/server.rb
|
|
56
|
+
- lib/kijirest/version.rb
|
|
57
|
+
homepage: http://www.kiji.org
|
|
58
|
+
licenses:
|
|
59
|
+
- Apache
|
|
60
|
+
metadata: {}
|
|
61
|
+
post_install_message:
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ! '>='
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ! '>='
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
requirements: []
|
|
76
|
+
rubyforge_project:
|
|
77
|
+
rubygems_version: 2.0.3
|
|
78
|
+
signing_key:
|
|
79
|
+
specification_version: 4
|
|
80
|
+
summary: Client for the KIJI REST server
|
|
81
|
+
test_files: []
|