etre-client 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d7ee88bbc4eaef62c80b7fe886975cc09f6fe3fa
4
+ data.tar.gz: 474060e882a53a6274772f2b21854651295d43f5
5
+ SHA512:
6
+ metadata.gz: 5e3ec873dbc34c866833116196fce1b87a463a9f45360786bb48e6afb5c306bc1d7911a3c1d944736e81dee8dba6679793bf31afbeb63a2ad406b91930ea49d9
7
+ data.tar.gz: b993c6f228ac3bc21f941d4a2cbd2b86eb0f43058a8e3e9a27da4082555507668312bcf446a257ce7eb0b9d1b19bb8cdbbd2fa3fee9fadbd19c38fef80e0413d
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ # ignore gems
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rest-client'
4
+
5
+ group :test, :development do
6
+ gem 'rspec-rails'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,97 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ actionpack (5.1.4)
5
+ actionview (= 5.1.4)
6
+ activesupport (= 5.1.4)
7
+ rack (~> 2.0)
8
+ rack-test (>= 0.6.3)
9
+ rails-dom-testing (~> 2.0)
10
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
11
+ actionview (5.1.4)
12
+ activesupport (= 5.1.4)
13
+ builder (~> 3.1)
14
+ erubi (~> 1.4)
15
+ rails-dom-testing (~> 2.0)
16
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
17
+ activesupport (5.1.4)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (~> 0.7)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ builder (3.2.3)
23
+ concurrent-ruby (1.0.5)
24
+ crass (1.0.2)
25
+ diff-lcs (1.3)
26
+ domain_name (0.5.20170404)
27
+ unf (>= 0.0.5, < 1.0.0)
28
+ erubi (1.7.0)
29
+ http-cookie (1.0.3)
30
+ domain_name (~> 0.5)
31
+ i18n (0.9.0)
32
+ concurrent-ruby (~> 1.0)
33
+ loofah (2.1.1)
34
+ crass (~> 1.0.2)
35
+ nokogiri (>= 1.5.9)
36
+ method_source (0.9.0)
37
+ mime-types (3.1)
38
+ mime-types-data (~> 3.2015)
39
+ mime-types-data (3.2016.0521)
40
+ mini_portile2 (2.3.0)
41
+ minitest (5.10.3)
42
+ netrc (0.11.0)
43
+ nokogiri (1.8.1)
44
+ mini_portile2 (~> 2.3.0)
45
+ rack (2.0.3)
46
+ rack-test (0.7.0)
47
+ rack (>= 1.0, < 3)
48
+ rails-dom-testing (2.0.3)
49
+ activesupport (>= 4.2.0)
50
+ nokogiri (>= 1.6)
51
+ rails-html-sanitizer (1.0.3)
52
+ loofah (~> 2.0)
53
+ railties (5.1.4)
54
+ actionpack (= 5.1.4)
55
+ activesupport (= 5.1.4)
56
+ method_source
57
+ rake (>= 0.8.7)
58
+ thor (>= 0.18.1, < 2.0)
59
+ rake (12.2.1)
60
+ rest-client (2.0.2)
61
+ http-cookie (>= 1.0.2, < 2.0)
62
+ mime-types (>= 1.16, < 4.0)
63
+ netrc (~> 0.8)
64
+ rspec-core (3.7.0)
65
+ rspec-support (~> 3.7.0)
66
+ rspec-expectations (3.7.0)
67
+ diff-lcs (>= 1.2.0, < 2.0)
68
+ rspec-support (~> 3.7.0)
69
+ rspec-mocks (3.7.0)
70
+ diff-lcs (>= 1.2.0, < 2.0)
71
+ rspec-support (~> 3.7.0)
72
+ rspec-rails (3.7.1)
73
+ actionpack (>= 3.0)
74
+ activesupport (>= 3.0)
75
+ railties (>= 3.0)
76
+ rspec-core (~> 3.7.0)
77
+ rspec-expectations (~> 3.7.0)
78
+ rspec-mocks (~> 3.7.0)
79
+ rspec-support (~> 3.7.0)
80
+ rspec-support (3.7.0)
81
+ thor (0.20.0)
82
+ thread_safe (0.3.6)
83
+ tzinfo (1.2.4)
84
+ thread_safe (~> 0.1)
85
+ unf (0.1.4)
86
+ unf_ext
87
+ unf_ext (0.0.7.4)
88
+
89
+ PLATFORMS
90
+ ruby
91
+
92
+ DEPENDENCIES
93
+ rest-client
94
+ rspec-rails
95
+
96
+ BUNDLED WITH
97
+ 1.15.4
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ Description
2
+ ------
3
+ etre-client is a gem that can be used to interact with an [Etre](https://github.com/square/etre) instance.
4
+
5
+ Installation
6
+ ------
7
+ etre-client is hosted on rubygems.org. To install it
8
+ 1. Add "etre-client" to your Gemfile
9
+ 2. Run "bunndle install"
10
+
11
+ Alternatively, you can just "gem install etre-client".
12
+
13
+ Usage
14
+ ------
15
+ Create a new client
16
+ ```
17
+ e = Etre::Client.new(entity_type: "node", url: "http://127.0.0.1:8080")
18
+ ```
19
+
20
+ Insert entities
21
+ ```
22
+ entities = [{"foo" => "bar"}, {"foo" => "abc"}]
23
+ e.insert(entities)
24
+ => [{"id"=>"59f90caadd1b176f02eddcd8", "uri"=>"127.0.0.1:8080/api/v1/entity/59f90caadd1b176f02eddcd8"}, {"id"=>"59f90caadd1b176f02eddcda", "uri"=>"127.0.0.1:8080/api/v1/entity/59f90caadd1b176f02eddcda"}]
25
+ ```
26
+
27
+ Update entities
28
+ ```
29
+ query = "foo=bar"
30
+ patch = {"foo" => "newbar"}
31
+ e.update(query, patch)
32
+ => [{"id"=>"59f90caadd1b176f02eddcd8", "uri"=>"127.0.0.1:8080/api/v1/entity/59f90caadd1b176f02eddcd8", "diff"=>{"_id"=>"59f90caadd1b176f02eddcd8", "_rev"=>0, "_type"=>"node", "foo"=>"bar"}}]
33
+ ```
34
+
35
+ Update a single entity
36
+ ```
37
+ id = "59f90caadd1b176f02eddcda"
38
+ patch = {"foo" => "newbar"}
39
+ e.update_one(id, patch)
40
+
41
+ => {"id"=>"59f90caadd1b176f02eddcda", "uri"=>"127.0.0.1:8080/api/v1/entity/59f90caadd1b176f02eddcda", "diff"=>{"_id"=>"59f90caadd1b176f02eddcda", "_rev"=>0, "_type"=>"node", "foo"=>"abc"}}
42
+ ```
43
+
44
+ Delete entities
45
+ ```
46
+ query = "foo=bar"
47
+ e.delete(query)
48
+ ```
49
+
50
+ Delete a single entity
51
+ ```
52
+ id = "abc"
53
+ e.delete_one(id)
54
+ => {"_id" => "abc", "foo" => "bar"}
55
+ ```
56
+
57
+ List the labels for an entity
58
+ ```
59
+ id = "abc"
60
+ e.labels(id)
61
+ => ["foo"]
62
+ ```
63
+
64
+ Development
65
+ ------
66
+ Run the tests
67
+ ```
68
+ bundle exec rake spec
69
+ ```
70
+
71
+ ## License
72
+
73
+ Copyright (c) 2017 Square Inc. Distributed under the Apache 2.0 License.
74
+ See LICENSE file for further details.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ desc "run specs"
4
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'etre-client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'etre-client'
8
+ spec.version = Etre::Client::VERSION
9
+ spec.version = '0.8.0'
10
+ spec.license = 'Apache-2.0'
11
+ spec.authors = ['Michael Finch']
12
+ spec.email = ['mfinch@squareup.com']
13
+ spec.summary = 'Client gem for interacting with Etre'
14
+ spec.homepage = 'https://github.com/square/etre-client-ruby'
15
+ spec.required_ruby_version = '>= 2.3.0'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'json'
22
+ spec.add_runtime_dependency 'rest-client', '~> 2.0'
23
+
24
+ spec.add_development_dependency 'rake', '~> 12.2', '>= 12.2.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ end
@@ -0,0 +1,264 @@
1
+ require 'etre-client/errors'
2
+ require 'rest-client'
3
+ require 'json'
4
+
5
+ module Etre
6
+ class Client
7
+ attr_reader :entity_type, :url
8
+
9
+ API_ROOT = "/api/v1"
10
+ META_LABEL_ID = "_id"
11
+ META_LABEL_TYPE = "_type"
12
+
13
+ def initialize(entity_type:, url:, ssl_cert: nil, ssl_key: nil, ssl_ca: nil, insecure: true)
14
+ @entity_type = entity_type
15
+ @url = url
16
+
17
+ @ssl_cert = ssl_cert
18
+ @ssl_key = ssl_key
19
+ @ssl_ca = ssl_ca
20
+ @insecure = insecure
21
+ end
22
+
23
+ # query returns an array of entities that satisfy a query.
24
+ def query(query, filter = nil)
25
+ if query.nil? || query.empty?
26
+ raise QueryNotProvided
27
+ end
28
+
29
+ # @todo: translate filter to query params
30
+
31
+ # Do the normal GET /entities?query unless the query is ~2k characters
32
+ # becuase that brings the entire URL length close to the max supported
33
+ # limit across most HTTP servers. In that case, switch to alternate
34
+ # endpoint to POST the long query.
35
+ if query.length < 2000
36
+ # Always escape the query.
37
+ query = CGI::escape(query)
38
+
39
+ resp = etre_get("/entities/#{@entity_type}?query=#{query}")
40
+ else
41
+ # DO NOT ESCAPE THE QUERY! It's not sent via URL, so no escaping needed.
42
+ resp = etre_post("/query/#{@entity_type}", query)
43
+ end
44
+
45
+ if resp.code != 200
46
+ raise UnexpectedResponseCode, "expected 200, got #{resp.code}"
47
+ end
48
+
49
+ return JSON.parse(resp.body)
50
+ end
51
+
52
+ # insert inserts an array of entities.
53
+ def insert(entities)
54
+ if entities.nil? || !entities.any?
55
+ raise EntityNotProvided
56
+ end
57
+
58
+ entities.each do |e|
59
+ if e.key?(META_LABEL_ID)
60
+ raise EntityIdSet, "entity: #{e}"
61
+ end
62
+
63
+ if e.key?(META_LABEL_TYPE) && e[META_LABEL_TYPE] != @entity_type
64
+ raise EntityTypeMismatch, "only valid type is '#{@entity_type}', but " +
65
+ "entity has type '#{e[META_LABEL_TYPE]}'"
66
+ end
67
+ end
68
+
69
+ resp = etre_post("/entities/#{@entity_type}", entities)
70
+ if ![200, 201].include?(resp.code)
71
+ raise UnexpectedResponseCode, "expected 200 or 201, got #{resp.code}"
72
+ end
73
+
74
+ return JSON.parse(resp.body)
75
+ end
76
+
77
+ # update updates entities with the given patch that satisfy the given query.
78
+ def update(query, patch)
79
+ if query.nil? || query.empty?
80
+ raise QueryNotProvided
81
+ end
82
+
83
+ # Always escape the query.
84
+ query = CGI::escape(query)
85
+
86
+ if patch.nil? || !patch.any?
87
+ raise PatchNotProvided
88
+ end
89
+
90
+ if patch.key?(META_LABEL_ID)
91
+ raise PatchIdSet, "patch: #{patch}"
92
+ end
93
+
94
+ if patch.key?(META_LABEL_TYPE) && patch[META_LABEL_TYPE] != @entity_type
95
+ raise EntityTypeMismatch, "only valid type is '#{@entity_type}', but " +
96
+ "patch has type '#{patch[META_LABEL_TYPE]}'"
97
+ end
98
+
99
+ resp = etre_put("/entities/#{@entity_type}?query=#{query}", patch)
100
+ if ![200, 201].include?(resp.code)
101
+ raise UnexpectedResponseCode, "expected 200 or 201, got #{resp.code}"
102
+ end
103
+
104
+ return JSON.parse(resp.body)
105
+ end
106
+
107
+ # update_one updates the given entity id with the provided patch.
108
+ def update_one(id, patch)
109
+ if id.nil? || id.empty?
110
+ raise IdNotProvided
111
+ end
112
+
113
+ if patch.nil? || !patch.any?
114
+ raise PatchNotProvided
115
+ end
116
+
117
+ if patch.key?(META_LABEL_ID)
118
+ raise PatchIdSet, "patch: #{patch}"
119
+ end
120
+
121
+ if patch.key?(META_LABEL_TYPE) && patch[META_LABEL_TYPE] != @entity_type
122
+ raise EntityTypeMismatch, "only valid type is '#{@entity_type}', but " +
123
+ "patch has type '#{patch[META_LABEL_TYPE]}'"
124
+ end
125
+
126
+ resp = etre_put("/entity/#{@entity_type}/#{id}", patch)
127
+ if ![200, 201].include?(resp.code)
128
+ raise UnexpectedResponseCode, "expected 200 or 201, got #{resp.code}"
129
+ end
130
+
131
+ return JSON.parse(resp.body)
132
+ end
133
+
134
+ # delete deletes the entities that satisfy the given query.
135
+ def delete(query)
136
+ if query.nil? || query.empty?
137
+ raise QueryNotProvided
138
+ end
139
+
140
+ # Always escape the query.
141
+ query = CGI::escape(query)
142
+
143
+ resp = etre_delete("/entities/#{@entity_type}?query=#{query}")
144
+ if resp.code != 200
145
+ raise UnexpectedResponseCode, "expected 200, got #{resp.code}"
146
+ end
147
+
148
+ return JSON.parse(resp.body)
149
+ end
150
+
151
+ # delete_one deletes the entity with the given id.
152
+ def delete_one(id)
153
+ if id.nil? || id.empty?
154
+ raise IdNotProvided
155
+ end
156
+
157
+ resp = etre_delete("/entity/#{@entity_type}/#{id}")
158
+ if resp.code != 200
159
+ raise UnexpectedResponseCode, "expected 200, got #{resp.code}"
160
+ end
161
+
162
+ return JSON.parse(resp.body)
163
+ end
164
+
165
+ # labels returns an array of labels for the given entity id.
166
+ def labels(id)
167
+ if id.nil? || id.empty?
168
+ raise IdNotProvided
169
+ end
170
+
171
+ resp = etre_get("/entity/#{@entity_type}/#{id}/labels")
172
+
173
+ if resp.code != 200
174
+ raise UnexpectedResponseCode, "expected 200, got #{resp.code}"
175
+ end
176
+
177
+ return JSON.parse(resp.body)
178
+ end
179
+
180
+ # delete_label deletes the given label on the provided entity id.
181
+ def delete_label(id, label)
182
+ if id.nil? || id.empty?
183
+ raise IdNotProvided
184
+ end
185
+
186
+ if label.nil? || label.empty?
187
+ raise LabelNotSet
188
+ end
189
+
190
+ resp = etre_delete("/entity/#{@entity_type}/#{id}/labels/#{label}")
191
+ if resp.code != 200
192
+ raise UnexpectedResponseCode, "expected 200, got #{resp.code}"
193
+ end
194
+
195
+ return JSON.parse(resp.body)
196
+ end
197
+
198
+ private
199
+
200
+ def etre_get(route)
201
+ resource_for_route(route).get(
202
+ get_headers,
203
+ )
204
+ end
205
+
206
+ def etre_post(route, params = nil)
207
+ resource_for_route(route).post(
208
+ params.to_json,
209
+ post_headers,
210
+ )
211
+ end
212
+
213
+ def etre_put(route, params = nil)
214
+ resource_for_route(route).put(
215
+ params.to_json,
216
+ put_headers,
217
+ )
218
+ end
219
+
220
+ def etre_delete(route)
221
+ resource_for_route(route).delete(
222
+ delete_headers,
223
+ )
224
+ end
225
+
226
+ def get_headers
227
+ {:accept => 'application/json'}
228
+ end
229
+
230
+ def post_headers
231
+ get_headers.merge!(:content_type => 'application/json')
232
+ end
233
+
234
+ def put_headers
235
+ post_headers
236
+ end
237
+
238
+ def delete_headers
239
+ get_headers
240
+ end
241
+
242
+ def resource_for_route(route)
243
+ opts = {}
244
+ opts.merge!(ssl_options) unless @insecure
245
+ RestClient::Resource.new(
246
+ @url + API_ROOT + route,
247
+ opts
248
+ )
249
+ end
250
+
251
+ def ssl_options
252
+ {
253
+ :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read(@ssl_cert)),
254
+ :ssl_client_key => OpenSSL::PKey::RSA.new(File.read(@ssl_key)),
255
+ :ssl_ca_file => @ssl_ca,
256
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
257
+ }
258
+ end
259
+
260
+ def parse_response(response)
261
+ JSON.parse(response)
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,13 @@
1
+ module Etre
2
+ class Client
3
+ class EntityIdSet < StandardError; end
4
+ class EntityNotProvided < StandardError; end
5
+ class EntityTypeMismatch < StandardError; end
6
+ class IdNotProvided < StandardError; end
7
+ class LabelNotSet < StandardError; end
8
+ class PatchIdSet < StandardError; end
9
+ class PatchNotProvided < StandardError; end
10
+ class QueryNotProvided < StandardError; end
11
+ class UnexpectedResponseCode < StandardError; end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Etre
2
+ class Client
3
+ VERSION = "0.8.0"
4
+ end
5
+ end
@@ -0,0 +1,328 @@
1
+ require 'etre-client'
2
+ require 'ostruct'
3
+
4
+ describe Etre::Client do
5
+ let(:etre_client) { Etre::Client.new(entity_type: "node", url: "http://localhost:3000", insecure: true) }
6
+ let(:get_headers) { {:accept => "application/json"} }
7
+ let(:post_headers) { get_headers.merge({:content_type => "application/json"}) }
8
+ let(:put_headers) { post_headers }
9
+ let(:delete_headers) { get_headers }
10
+ let(:migration_id) { 3 }
11
+ let(:cluster) { "cluster-001" }
12
+ let(:entity1) { {"_id" => "abc", "foo" => "bar"} }
13
+ let(:entity2) { {"oof" => "rab"} }
14
+ let(:entity3) { {"blah" => "slug"} }
15
+ let(:entity4) { {"_type" => "host", "a" => "b"} }
16
+ let(:response_double) { instance_double(RestClient::Response) }
17
+ let(:resource_double) { instance_double(RestClient::Resource) }
18
+
19
+ describe '#query' do
20
+ it "makes GET request when query is short" do
21
+ q = "foo=bar"
22
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}?query=#{CGI::escape(q)}"
23
+
24
+ expect(response_double).to receive(:code).and_return(200)
25
+ expect(response_double).to receive(:body).and_return([entity1].to_json)
26
+ expect(resource_double).to receive(:get).with(get_headers).and_return(response_double)
27
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
28
+ expect(etre_client.query(q)).to eq([entity1])
29
+ end
30
+
31
+ it "makes POST request when query is too long" do
32
+ q = "foo=bar," * 300
33
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/query/#{etre_client.entity_type}"
34
+
35
+ expect(response_double).to receive(:code).and_return(200)
36
+ expect(response_double).to receive(:body).and_return([entity1].to_json)
37
+ expect(resource_double).to receive(:post).with(q.to_json, post_headers).and_return(response_double)
38
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
39
+ expect(etre_client.query(q)).to eq([entity1])
40
+ end
41
+
42
+ it "raises if query is empty" do
43
+ q = ""
44
+
45
+ expect{etre_client.query(q)}.to raise_error(Etre::Client::QueryNotProvided)
46
+ end
47
+
48
+ it "raises if it gets an unexpected response code" do
49
+ q = "foo=bar"
50
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}?query=#{CGI::escape(q)}"
51
+
52
+ expect(response_double).to receive(:code).twice.and_return(400)
53
+ expect(resource_double).to receive(:get).with(get_headers).and_return(response_double)
54
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
55
+ expect{etre_client.query(q)}.to raise_error(Etre::Client::UnexpectedResponseCode)
56
+ end
57
+ end
58
+
59
+ describe "#insert" do
60
+ before :each do
61
+ @path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}"
62
+ end
63
+
64
+ it "inserts entities" do
65
+ entities = [entity2, entity3]
66
+
67
+ expect(response_double).to receive(:code).and_return(200)
68
+ expect(response_double).to receive(:body).and_return(entities.to_json)
69
+ expect(resource_double).to receive(:post).with(entities.to_json, post_headers).and_return(response_double)
70
+ expect(RestClient::Resource).to receive(:new).with(@path, {}).and_return(resource_double)
71
+ expect(etre_client.insert(entities)).to eq(entities)
72
+ end
73
+
74
+ it "raises if an entity has _id set" do
75
+ entities = [entity1, entity2]
76
+
77
+ expect{etre_client.insert(entities)}.to raise_error(Etre::Client::EntityIdSet)
78
+ end
79
+
80
+ it "raises if an entity has the wrong _type set" do
81
+ entities = [entity2, entity4]
82
+
83
+ expect{etre_client.insert(entities)}.to raise_error(Etre::Client::EntityTypeMismatch)
84
+ end
85
+
86
+ it "raises if it gets an unexpected response code" do
87
+ entities = [entity2, entity3]
88
+
89
+ expect(response_double).to receive(:code).twice.and_return(400)
90
+ expect(resource_double).to receive(:post).with(entities.to_json, post_headers).and_return(response_double)
91
+ expect(RestClient::Resource).to receive(:new).with(@path, {}).and_return(resource_double)
92
+ expect{etre_client.insert(entities)}.to raise_error(Etre::Client::UnexpectedResponseCode)
93
+ end
94
+ end
95
+
96
+ describe "#update" do
97
+ it "updates entities" do
98
+ query = "foo=bar"
99
+ patch = {"foo" => "new"}
100
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}?query=#{CGI::escape(query)}"
101
+
102
+ expect(response_double).to receive(:code).and_return(200)
103
+ expect(response_double).to receive(:body).and_return({}.to_json)
104
+ expect(resource_double).to receive(:put).with(patch.to_json, put_headers).and_return(response_double)
105
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
106
+ expect(etre_client.update(query, patch)).to eq({})
107
+ end
108
+
109
+ it "raises if query is empty" do
110
+ query = ""
111
+ patch = {"foo" => "new"}
112
+
113
+ expect{etre_client.update(query, patch)}.to raise_error(Etre::Client::QueryNotProvided)
114
+ end
115
+
116
+ it "raises if patch is empty" do
117
+ query = "foo=bar"
118
+ patch = {}
119
+
120
+ expect{etre_client.update(query, patch)}.to raise_error(Etre::Client::PatchNotProvided)
121
+ end
122
+
123
+ it "raises if _id is set in patch" do
124
+ query = "foo=bar"
125
+ patch = {"_id" => "abc"}
126
+
127
+ expect{etre_client.update(query, patch)}.to raise_error(Etre::Client::PatchIdSet)
128
+ end
129
+
130
+ it "raises if patch has the wrong _type set" do
131
+ query = "foo=bar"
132
+ patch = {"_type" => "host"}
133
+
134
+ expect{etre_client.update(query, patch)}.to raise_error(Etre::Client::EntityTypeMismatch)
135
+ end
136
+
137
+ it "raises if it gets an unexpected response code" do
138
+ query = "foo=bar"
139
+ patch = {"foo" => "new"}
140
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}?query=#{CGI::escape(query)}"
141
+
142
+ expect(response_double).to receive(:code).twice.and_return(400)
143
+ expect(resource_double).to receive(:put).with(patch.to_json, put_headers).and_return(response_double)
144
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
145
+ expect{etre_client.update(query, patch)}.to raise_error(Etre::Client::UnexpectedResponseCode)
146
+ end
147
+ end
148
+
149
+ describe "#update_one" do
150
+ it "updates an entity" do
151
+ id = "abc"
152
+ patch = {"foo" => "new"}
153
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}"
154
+
155
+ expect(response_double).to receive(:code).and_return(200)
156
+ expect(response_double).to receive(:body).and_return({}.to_json)
157
+ expect(resource_double).to receive(:put).with(patch.to_json, put_headers).and_return(response_double)
158
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
159
+ expect(etre_client.update_one(id, patch)).to eq({})
160
+ end
161
+
162
+ it "raises if id is empty" do
163
+ id = ""
164
+ patch = {"foo" => "new"}
165
+
166
+ expect{etre_client.update_one(id, patch)}.to raise_error(Etre::Client::IdNotProvided)
167
+ end
168
+
169
+ it "raises if patch is empty" do
170
+ id = "abc"
171
+ patch = {}
172
+
173
+ expect{etre_client.update_one(id, patch)}.to raise_error(Etre::Client::PatchNotProvided)
174
+ end
175
+
176
+ it "raises if _id is set in patch" do
177
+ id = "abc"
178
+ patch = {"_id" => "abc"}
179
+
180
+ expect{etre_client.update_one(id, patch)}.to raise_error(Etre::Client::PatchIdSet)
181
+ end
182
+
183
+ it "raises if patch has the wrong _type set" do
184
+ id = "abc"
185
+ patch = {"_type" => "host"}
186
+
187
+ expect{etre_client.update_one(id, patch)}.to raise_error(Etre::Client::EntityTypeMismatch)
188
+ end
189
+
190
+ it "raises if it gets an unexpected response code" do
191
+ id = "abc"
192
+ patch = {"foo" => "new"}
193
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}"
194
+
195
+ expect(response_double).to receive(:code).twice.and_return(400)
196
+ expect(resource_double).to receive(:put).with(patch.to_json, put_headers).and_return(response_double)
197
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
198
+ expect{etre_client.update_one(id, patch)}.to raise_error(Etre::Client::UnexpectedResponseCode)
199
+ end
200
+ end
201
+
202
+ describe "#delete" do
203
+ it "deletes entities" do
204
+ query = "foo=bar"
205
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}?query=#{CGI::escape(query)}"
206
+
207
+ expect(response_double).to receive(:code).and_return(200)
208
+ expect(response_double).to receive(:body).and_return({}.to_json)
209
+ expect(resource_double).to receive(:delete).with(delete_headers).and_return(response_double)
210
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
211
+ expect(etre_client.delete(query)).to eq({})
212
+ end
213
+
214
+ it "raises if query is empty" do
215
+ query = ""
216
+
217
+ expect{etre_client.delete(query)}.to raise_error(Etre::Client::QueryNotProvided)
218
+ end
219
+
220
+ it "raises if it gets an unexpected response code" do
221
+ query = "foo=bar"
222
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entities/#{etre_client.entity_type}?query=#{CGI::escape(query)}"
223
+
224
+ expect(response_double).to receive(:code).twice.and_return(400)
225
+ expect(resource_double).to receive(:delete).with(delete_headers).and_return(response_double)
226
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
227
+ expect{etre_client.delete(query)}.to raise_error(Etre::Client::UnexpectedResponseCode)
228
+ end
229
+ end
230
+
231
+ describe "#delete_one" do
232
+ it "deletes an entity" do
233
+ id = "abc"
234
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}"
235
+
236
+ expect(response_double).to receive(:code).and_return(200)
237
+ expect(response_double).to receive(:body).and_return({}.to_json)
238
+ expect(resource_double).to receive(:delete).with(delete_headers).and_return(response_double)
239
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
240
+ expect(etre_client.delete_one(id)).to eq({})
241
+ end
242
+
243
+ it "raises if id is empty" do
244
+ id = ""
245
+
246
+ expect{etre_client.delete_one(id)}.to raise_error(Etre::Client::IdNotProvided)
247
+ end
248
+
249
+ it "raises if it gets an unexpected response code" do
250
+ id = "abc"
251
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}"
252
+
253
+ expect(response_double).to receive(:code).twice.and_return(400)
254
+ expect(resource_double).to receive(:delete).with(delete_headers).and_return(response_double)
255
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
256
+ expect{etre_client.delete_one(id)}.to raise_error(Etre::Client::UnexpectedResponseCode)
257
+ end
258
+ end
259
+
260
+ describe "#labels" do
261
+ it "lists the lables for an entity" do
262
+ id = "abc"
263
+ labels = ["foo1", "foo2"]
264
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}/labels"
265
+
266
+ expect(response_double).to receive(:code).and_return(200)
267
+ expect(response_double).to receive(:body).and_return(labels.to_json)
268
+ expect(resource_double).to receive(:get).with(get_headers).and_return(response_double)
269
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
270
+ expect(etre_client.labels(id)).to eq(labels)
271
+ end
272
+
273
+ it "raises if id is empty" do
274
+ id = ""
275
+
276
+ expect{etre_client.labels(id)}.to raise_error(Etre::Client::IdNotProvided)
277
+ end
278
+
279
+ it "raises if it gets an unexpected response code" do
280
+ id = "abc"
281
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}/labels"
282
+
283
+ expect(response_double).to receive(:code).twice.and_return(400)
284
+ expect(resource_double).to receive(:get).with(get_headers).and_return(response_double)
285
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
286
+ expect{etre_client.labels(id)}.to raise_error(Etre::Client::UnexpectedResponseCode)
287
+ end
288
+ end
289
+
290
+ describe "#delete_label" do
291
+ it "deletes the label on an entity" do
292
+ id = "abc"
293
+ label = "foo"
294
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}/labels/#{label}"
295
+
296
+ expect(response_double).to receive(:code).and_return(200)
297
+ expect(response_double).to receive(:body).and_return({}.to_json)
298
+ expect(resource_double).to receive(:delete).with(delete_headers).and_return(response_double)
299
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
300
+ expect(etre_client.delete_label(id, label)).to eq({})
301
+ end
302
+
303
+ it "raises if id is empty" do
304
+ id = ""
305
+ label = "foo"
306
+
307
+ expect{etre_client.delete_label(id, label)}.to raise_error(Etre::Client::IdNotProvided)
308
+ end
309
+
310
+ it "raises if label is empty" do
311
+ id = ""
312
+ label = ""
313
+
314
+ expect{etre_client.delete_label(id, label)}.to raise_error(Etre::Client::IdNotProvided)
315
+ end
316
+
317
+ it "raises if it gets an unexpected response code" do
318
+ id = "abc"
319
+ label = "foo"
320
+ path = "#{etre_client.url}#{Etre::Client::API_ROOT}/entity/#{etre_client.entity_type}/#{id}/labels/#{label}"
321
+
322
+ expect(response_double).to receive(:code).twice.and_return(400)
323
+ expect(resource_double).to receive(:delete).with(delete_headers).and_return(response_double)
324
+ expect(RestClient::Resource).to receive(:new).with(path, {}).and_return(resource_double)
325
+ expect{etre_client.delete_label(id, label)}.to raise_error(Etre::Client::UnexpectedResponseCode)
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+
3
+ RSpec.configure do |config|
4
+ config.mock_with :rspec do |mocks|
5
+ mocks.verify_doubled_constant_names = true
6
+ mocks.verify_partial_doubles = true
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: etre-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Finch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.2'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 12.2.0
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '12.2'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 12.2.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ description:
76
+ email:
77
+ - mfinch@squareup.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - ".gitignore"
83
+ - Gemfile
84
+ - Gemfile.lock
85
+ - README.md
86
+ - Rakefile
87
+ - etre-client.gemspec
88
+ - lib/etre-client.rb
89
+ - lib/etre-client/errors.rb
90
+ - lib/etre-client/version.rb
91
+ - spec/client_spec.rb
92
+ - spec/unit_helper.rb
93
+ homepage: https://github.com/square/etre-client-ruby
94
+ licenses:
95
+ - Apache-2.0
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 2.3.0
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.5.2
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Client gem for interacting with Etre
117
+ test_files:
118
+ - spec/client_spec.rb
119
+ - spec/unit_helper.rb