sis_ruby 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +41 -0
- data/Gemfile +3 -0
- data/LICENSE +28 -0
- data/README.md +188 -0
- data/RELEASE_NOTES.md +3 -0
- data/Rakefile +35 -0
- data/lib/sis_ruby/client.rb +85 -0
- data/lib/sis_ruby/common.rb +5 -0
- data/lib/sis_ruby/endpoint.rb +108 -0
- data/lib/sis_ruby/exceptions/bad_response_error.rb +35 -0
- data/lib/sis_ruby/exceptions/missing_id_error.rb +12 -0
- data/lib/sis_ruby/get_helper.rb +41 -0
- data/lib/sis_ruby/hash_builder.rb +31 -0
- data/lib/sis_ruby/params.rb +117 -0
- data/lib/sis_ruby/result_enumerable.rb +86 -0
- data/lib/sis_ruby/version.rb +3 -0
- data/lib/sis_ruby.rb +8 -0
- data/samples/sample-1.rb +26 -0
- data/samples/sample-2.rb +33 -0
- data/sis_ruby.gemspec +22 -0
- data/spec/client_spec.rb +57 -0
- data/spec/endpoint_spec.rb +54 -0
- data/spec/hash_builder_spec.rb +83 -0
- data/spec/params_spec.rb +75 -0
- data/spec/setup/drop-db.js +4 -0
- data/spec/setup/recreate-db.sh +14 -0
- data/spec/setup/test-user.json +6 -0
- data/spec/spec_helper.rb +1 -0
- data/spec-writing/entity.json +50 -0
- data/spec-writing/hiera.json +58 -0
- data/spec-writing/hook.json +64 -0
- data/spec-writing/schema.json +131 -0
- data/spec-writing/write_spec.rb +105 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 584c4065727e96b0b4c0172fe31b6ab7d8a1d39a
|
4
|
+
data.tar.gz: 99dd517363e6e832f3e2c910f2b7a36ecb925a9e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 001f9546fc5f481bb3b4677071f9d4dd924975810ab34ee25c83f3e749fb7ecfe367d5d74104bf04f76852fa05fb605f37c56c1e371ce9d3b7f063b623b75f91
|
7
|
+
data.tar.gz: 08a35bf1b678a39e0cb5b0826ee5c2c391c97fdec62df27e7c5833a61830ce3924fcb76f47228ff3757f9d0d7d1c71ab796104301e2885f6c041e4ff5d997e6f
|
data/.gitignore
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
### Ruby template
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
/.config
|
5
|
+
/coverage/
|
6
|
+
/InstalledFiles
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/spec/examples.txt
|
10
|
+
/test/tmp/
|
11
|
+
/test/version_tmp/
|
12
|
+
/tmp/
|
13
|
+
|
14
|
+
## Specific to RubyMotion:
|
15
|
+
.dat*
|
16
|
+
.repl_history
|
17
|
+
build/
|
18
|
+
|
19
|
+
## Documentation cache and generated files:
|
20
|
+
/.yardoc/
|
21
|
+
/_yardoc/
|
22
|
+
/doc/
|
23
|
+
/rdoc/
|
24
|
+
|
25
|
+
## Environment normalisation:
|
26
|
+
/.bundle/
|
27
|
+
/vendor/bundle
|
28
|
+
/lib/bundler/man/
|
29
|
+
|
30
|
+
# for a library or gem, you might want to ignore these files since the code is
|
31
|
+
# intended to run in multiple environments; otherwise, check them in:
|
32
|
+
# Gemfile.lock
|
33
|
+
# .ruby-version
|
34
|
+
# .ruby-gemset
|
35
|
+
|
36
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
37
|
+
.rvmrc
|
38
|
+
|
39
|
+
# Created by .ignore support plugin (hsz.mobi)
|
40
|
+
|
41
|
+
.idea/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
This software is licensed under the BSD 3-Clause license.
|
2
|
+
|
3
|
+
Copyright (c) 2016, Verisign, Inc. All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
9
|
+
list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
this list of conditions and the following disclaimer in the documentation and/or
|
13
|
+
other materials provided with the distribution.
|
14
|
+
|
15
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
16
|
+
may be used to endorse or promote products derived from this software without
|
17
|
+
specific prior written permission.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
23
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
24
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
25
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
26
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
28
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# sis_ruby
|
2
|
+
|
3
|
+
A Ruby client to talk to a SIS server (https://github.com/sis-cmdb/sis-api).
|
4
|
+
|
5
|
+
Currently only the v1.1 API is supported.
|
6
|
+
|
7
|
+
Initial commit represents work by Neel Goyal, formerly at Verisign,
|
8
|
+
and Keith Bennett, currently at Verisign (http://www.verisign.com/).
|
9
|
+
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Example code:
|
14
|
+
|
15
|
+
```
|
16
|
+
require 'sis_ruby'
|
17
|
+
|
18
|
+
# Create a client
|
19
|
+
client = SisRuby::Client.new(sis_server_url)
|
20
|
+
|
21
|
+
# Get the host information endpoint
|
22
|
+
hosts = client.entities('host')
|
23
|
+
|
24
|
+
# Use all possible Params options to list records of a given entity:
|
25
|
+
params = SisRuby::Params.new.sort('hostname').offset(9000).limit(2).fields('hostname', 'environment')
|
26
|
+
records = hosts.list(params)
|
27
|
+
|
28
|
+
# Get record counts:
|
29
|
+
puts "Total host count is #{hosts.count}"
|
30
|
+
puts "Total qa host count is #{hosts.count('environment' => 'qa')}"
|
31
|
+
|
32
|
+
# Get up to 10 hooks
|
33
|
+
hook_list = client.hooks.list(SisRuby::Params.new.limit(10))
|
34
|
+
|
35
|
+
# Get 1 schema
|
36
|
+
schema_list = client.schemas.list(SisRuby::Params.new.limit(1)).first
|
37
|
+
|
38
|
+
# Hiera Entry Creation
|
39
|
+
hiera_entry = client.hiera.create({ 'name' => 'entry', 'hieradata' => { 'key1' => 'value1' }});
|
40
|
+
|
41
|
+
# Entity Deletion: Delete the entity of type 'entity_name' with id 'foo':
|
42
|
+
was_deleted = client.entities('entity_name').delete('foo');
|
43
|
+
```
|
44
|
+
|
45
|
+
## Client Initialization
|
46
|
+
|
47
|
+
The client constructor takes the following parameters:
|
48
|
+
|
49
|
+
* (required) a URL indicating the base URL of the SIS endpoints
|
50
|
+
* (optional) a hash containing neither, one, or both of:
|
51
|
+
* **:auth_token** - an authorization token field to be sent in the `x-auth-token` header
|
52
|
+
* **:api_version** - a version string to use in the request URL's instead of the default API version
|
53
|
+
|
54
|
+
```
|
55
|
+
client = SisRuby::Client.new(sis_server_url)
|
56
|
+
client = SisRuby::Client.new(sis_server_url, auth_token: auth_token)
|
57
|
+
```
|
58
|
+
|
59
|
+
## Client Authentication
|
60
|
+
|
61
|
+
The client may also acquire and use a temporary token to use against the SIS endpoint
|
62
|
+
via the `authenticate` method. Below are examples:
|
63
|
+
|
64
|
+
```
|
65
|
+
# This method will raise an SisRuby::Client::AuthenticationError if authentication fails.
|
66
|
+
token = client.authenticate(userid, password)
|
67
|
+
|
68
|
+
# You can also combine the client creation and authentication into a single expression
|
69
|
+
# since the authenticate method returns the client:
|
70
|
+
client = SisRuby::Client.new(sis_server_url).authenticate(userid, password)
|
71
|
+
```
|
72
|
+
|
73
|
+
Although `authenticate` returns the token, there is no need to save it;
|
74
|
+
it is stored in the client instance for subsequent requests.
|
75
|
+
|
76
|
+
## Entity, Hook, Schema, and Hiera Methods
|
77
|
+
|
78
|
+
The object returned by `client.hooks`, `client.schemas`, `client.hiera`, and
|
79
|
+
`client.entities(entity_name)` all interact with the appropriate endpoints
|
80
|
+
and expose the following interface:
|
81
|
+
|
82
|
+
|
83
|
+
### list(query)
|
84
|
+
|
85
|
+
This maps to a GET `/` request against the appropriate endpoint for the default or specified API version.
|
86
|
+
|
87
|
+
List parameters can be specified in the form of any object responding to
|
88
|
+
`to_hash` and returning a hash (including, of course, a `Hash`).
|
89
|
+
|
90
|
+
Values in the hash can contain:
|
91
|
+
|
92
|
+
* **sort** - field name(s) on which to sort the records, precede fieldname with `-` for descending
|
93
|
+
* **limit** - limit the result set size to the specified number of records
|
94
|
+
* **fields** - only return the fields passed to this method; however:
|
95
|
+
* currently there is a bug that results in array type fields being returned even if they
|
96
|
+
are not included in the field list
|
97
|
+
* the _id field will always be returned even if it is not specified
|
98
|
+
* **offset** - the offset into the sorted results at which to start adding records to the result set
|
99
|
+
(Note: offset is not reliable unless a sort order is specified; there is no default sort order
|
100
|
+
and the random order may change from call to call.
|
101
|
+
|
102
|
+
For your convenience, a `Params` class has been provided with chainable methods (see code example).
|
103
|
+
|
104
|
+
Also, there is a `count` method that will return the total record count. If passed a filter, it will
|
105
|
+
return the count of records that meet the filter criteria.
|
106
|
+
|
107
|
+
An array of hashes is returned on success. For your convenience, there is also a
|
108
|
+
`list_as_openstructs` method that returns each record as an `OpenStruct` instance for easier access.
|
109
|
+
OpenStruct instances allow you to call methods whose names correspond to the original hash's keys,
|
110
|
+
e.g. `host.site` instead of `host['site']`.
|
111
|
+
This approach is not recommended for large numbers of records, as it requires more memory and processing.
|
112
|
+
|
113
|
+
|
114
|
+
### get(id)
|
115
|
+
|
116
|
+
This maps to a GET `/id` request against the approprivate endpoint for the default or specified API version.
|
117
|
+
|
118
|
+
* id : a string representing the ID of the object to receive. For schemas, hooks, and hiera, this is the `name`.
|
119
|
+
For entities, it is the `_id`.
|
120
|
+
|
121
|
+
A single hash representing the object is returned on success.
|
122
|
+
|
123
|
+
|
124
|
+
### create(obj)
|
125
|
+
|
126
|
+
This maps to a POST `/` request against the appropriate endpoint for the default or specified API version.
|
127
|
+
|
128
|
+
* obj : a valid Hash conforming to the endpoint specification
|
129
|
+
|
130
|
+
The created object as a hash is returned on success.
|
131
|
+
|
132
|
+
|
133
|
+
### update(obj)
|
134
|
+
|
135
|
+
This maps to a PUT '/' request against the appropriate endpoint for the default or specified API version.
|
136
|
+
|
137
|
+
* obj : a valid hash conforming to the endpoint specification. Typically retrieved from `list` or `get`.
|
138
|
+
|
139
|
+
The updated hash representing the object is returned on success.
|
140
|
+
|
141
|
+
|
142
|
+
### delete(obj)
|
143
|
+
|
144
|
+
This maps to a DELETE '/id' request against the appropriate endpoint for the default or specified API version.
|
145
|
+
|
146
|
+
* obj : either a string id or an object retrieved from `list` or `get`
|
147
|
+
|
148
|
+
The boolean `true` is returned on success.
|
149
|
+
|
150
|
+
|
151
|
+
## Error handling
|
152
|
+
|
153
|
+
An instance of `SisRuby::Client` will raise an exception if an HTTP request returns a status code above and including 400.
|
154
|
+
|
155
|
+
|
156
|
+
## Running the Tests
|
157
|
+
|
158
|
+
Test code is divided into different top-level directories:
|
159
|
+
|
160
|
+
### `spec`
|
161
|
+
|
162
|
+
These are tests not requiring a SIS server.
|
163
|
+
|
164
|
+
### `spec_reading`
|
165
|
+
|
166
|
+
These are tests requiring a SIS server containing a 'host' entity populated with records
|
167
|
+
|
168
|
+
### `spec_writing`
|
169
|
+
|
170
|
+
These are tests requiring a SIS server that can be used for writing; for these
|
171
|
+
you'll probably want to set up a local server. Instructions for this are at
|
172
|
+
https://github.com/sis-cmdb/sis-api#building-and-testing.
|
173
|
+
|
174
|
+
If the tests should ever fail and the data base is not correctly restored to
|
175
|
+
its original empty state, you can delete the Mongo data
|
176
|
+
by running the Mongo shell ('mongo') and issuing the following commands:
|
177
|
+
|
178
|
+
```
|
179
|
+
use test
|
180
|
+
db.dropDatabase();
|
181
|
+
```
|
182
|
+
|
183
|
+
To verify the user id and password, you can issue the following command, replacing 'test' and
|
184
|
+
'abc123' with the real user id and password:
|
185
|
+
|
186
|
+
```
|
187
|
+
curl -D- -u test:abc123 -X POST -H "Content-Type: application/json" http://localhost:3000/api/v1.1/users/auth_token
|
188
|
+
```
|
data/RELEASE_NOTES.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# From https://github.com/goncalossilva/gem_template
|
2
|
+
begin
|
3
|
+
require "bundler"
|
4
|
+
Bundler.setup
|
5
|
+
rescue LoadError
|
6
|
+
$stderr.puts "You need to have Bundler installed to be able build this gem."
|
7
|
+
end
|
8
|
+
|
9
|
+
gemspec = eval(File.read('sis_ruby.gemspec'))
|
10
|
+
|
11
|
+
desc "Validate the gemspec"
|
12
|
+
task :gemspec do
|
13
|
+
gemspec.validate
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Build gem locally"
|
17
|
+
task :build => :gemspec do
|
18
|
+
system "gem build #{gemspec.name}.gemspec"
|
19
|
+
FileUtils.mkdir_p "pkg"
|
20
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "pkg"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Install gem locally"
|
24
|
+
task :install => :build do
|
25
|
+
system "gem install pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Clean automatically generated files"
|
29
|
+
task :clean do
|
30
|
+
FileUtils.rm_rf "pkg"
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'rspec/core/rake_task'
|
34
|
+
|
35
|
+
RSpec::Core::RakeTask.new('spec')
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
require 'sis_ruby/endpoint'
|
3
|
+
require 'typhoeus'
|
4
|
+
|
5
|
+
module SisRuby
|
6
|
+
|
7
|
+
class Client
|
8
|
+
|
9
|
+
attr_reader :api_version, :auth_token, :base_url, :entity_endpoints, :hiera, :hooks, :hosts, :schemas
|
10
|
+
|
11
|
+
DEFAULT_API_VERSION = '1.1'
|
12
|
+
|
13
|
+
# @param url the base URL of the service (excluding ''/api/v...'')
|
14
|
+
# @param options (can include :version, :auth_token)
|
15
|
+
def initialize(url, options = {})
|
16
|
+
@base_url = url
|
17
|
+
@api_version = options[:api_version] || DEFAULT_API_VERSION
|
18
|
+
@auth_token = options[:auth_token]
|
19
|
+
|
20
|
+
@entity_endpoints = {}
|
21
|
+
@hooks = create_endpoint('hooks', 'name')
|
22
|
+
@schemas = create_endpoint('schemas', 'name')
|
23
|
+
@hiera = create_endpoint('hiera', 'name')
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def create_endpoint(endpoint_suffix, id_fieldname = :default)
|
28
|
+
Endpoint.new(self, endpoint_suffix, id_fieldname)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def entities(name, id_fieldname = DEFAULT_ID_FIELDNAME)
|
33
|
+
@entity_endpoints[name] ||= create_endpoint("entities/#{name}", id_fieldname)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def tokens(username)
|
38
|
+
create_endpoint("users/#{username}/tokens", 'name')
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Returns the schema for the specified collection_name, or nil if it's not found.
|
43
|
+
def schema_for(collection_name)
|
44
|
+
params = Params.new.limit(1).filter('name' => collection_name)
|
45
|
+
schemas.list(params).first
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Authenticates the username and password. Get the token by calling client.auth_token.
|
50
|
+
# @return self for chaining this method after the constructor
|
51
|
+
def authenticate(username, password)
|
52
|
+
dest = "#{base_url}/api/v#{api_version}/users/auth_token"
|
53
|
+
options = {
|
54
|
+
userpwd: username + ':' + password,
|
55
|
+
headers: { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
|
56
|
+
}
|
57
|
+
response = Typhoeus.post(dest, options)
|
58
|
+
unless response.options[:response_code] == 201
|
59
|
+
raise AuthenticationError.new(response)
|
60
|
+
end
|
61
|
+
|
62
|
+
@auth_token = JSON.parse(response.response_body)['name']
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
self.class.name + ": base_url = #{@base_url}"
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
class AuthenticationError < Exception
|
74
|
+
|
75
|
+
def initialize(response)
|
76
|
+
@response = response
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
@response.options[:response_headers].split("\r\n").first
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require_relative 'client'
|
5
|
+
require_relative 'common'
|
6
|
+
require_relative 'get_helper'
|
7
|
+
require_relative 'result_enumerable'
|
8
|
+
require_relative 'exceptions/missing_id_error'
|
9
|
+
|
10
|
+
module SisRuby
|
11
|
+
|
12
|
+
class Endpoint
|
13
|
+
|
14
|
+
include GetHelper
|
15
|
+
|
16
|
+
attr_reader :client, :id_field, :url
|
17
|
+
|
18
|
+
|
19
|
+
def initialize(client, endpoint_name, id_field = DEFAULT_ID_FIELDNAME)
|
20
|
+
@client = client
|
21
|
+
@url = "#{client.base_url}/api/v#{client.api_version}/#{endpoint_name}"
|
22
|
+
@id_field = id_field
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# This method is used to allow callers to pass either the id itself,
|
27
|
+
# or the record including an id key/value pair.
|
28
|
+
def id_from_param(object)
|
29
|
+
id = object.is_a?(Hash) ? object[@id_field] : object
|
30
|
+
id ? id : raise(MissingIdError.new("Missing required id field #{@id_field}}"))
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def create_enumerable(params = {}, chunk_size = ResultEnumerable::DEFAULT_CHUNK_RECORD_COUNT)
|
35
|
+
ResultEnumerable.new(self, params, chunk_size)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Gets the total count of records, with optional filter
|
40
|
+
def count(filter = {})
|
41
|
+
params = Params.new.filter(filter).limit(1).to_hash
|
42
|
+
response = Typhoeus::Request.new(@url, params: params, headers: create_headers(true)).run
|
43
|
+
response.headers['x-total-count'].to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# Anything implementing a to_hash method can be passed as the query.
|
48
|
+
# This enables the passing in of SisParams objects.
|
49
|
+
def list(params = {})
|
50
|
+
create_enumerable(params).each.to_a
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Anything implementing a to_hash method can be passed as the query.
|
55
|
+
# This enables the passing in of SisParams objects.
|
56
|
+
def list_as_openstructs(query = {})
|
57
|
+
list(query).map { |h| OpenStruct.new(h) }
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def get(id)
|
62
|
+
request = Typhoeus::Request.new("#{url}/#{id}", headers: create_headers(true))
|
63
|
+
response = request.run
|
64
|
+
validate_response_success(response)
|
65
|
+
JSON.parse(response.body)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def create(obj)
|
70
|
+
http_response = Typhoeus.post(@url, { body: obj.to_json, headers: get_headers(true) } )
|
71
|
+
validate_response_success(http_response)
|
72
|
+
JSON.parse(http_response.body)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def delete(id)
|
77
|
+
id = id_from_param(id)
|
78
|
+
http_response = Typhoeus.delete("#{@url}/#{id}", headers: get_headers(false))
|
79
|
+
validate_response_success(http_response)
|
80
|
+
JSON.parse(http_response.body)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def update(obj)
|
85
|
+
id = id_from_param(obj)
|
86
|
+
http_response = self.class.put("#{@url}/#{id}", {body: obj.to_json, headers: get_headers(true) })
|
87
|
+
validate_response_success(http_response)
|
88
|
+
JSON.parse(http_response.body)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
self.class.name + ": endpoint = #{@url}, client = #{@client}"
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def ==(other)
|
98
|
+
client.equal?(other.client) && url == other.url && id_field == other.id_field
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def get_headers(specify_content_type)
|
104
|
+
create_headers(specify_content_type, @client.auth_token)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module SisRuby
|
2
|
+
|
3
|
+
class BadResponseError < RuntimeError
|
4
|
+
|
5
|
+
attr_reader :response
|
6
|
+
|
7
|
+
def initialize(response)
|
8
|
+
@response = response
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
|
14
|
+
first_header_line = if response && response.options[:response_headers]
|
15
|
+
header_lines = response.options[:response_headers].split("\r\n")
|
16
|
+
header_lines.any? ? header_lines.first : nil
|
17
|
+
else
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
body = if response && response.options[:response_body]
|
22
|
+
response.options[:response_body].rstrip
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
code = response.code
|
28
|
+
|
29
|
+
string = "#{self.class.name}: #{code}"
|
30
|
+
string << ": #{body}" if body
|
31
|
+
string << (first_header_line ? " (#{first_header_line})" : '')
|
32
|
+
string
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'exceptions/bad_response_error'
|
2
|
+
|
3
|
+
|
4
|
+
# Methods for helping get data from SIS.
|
5
|
+
module SisRuby
|
6
|
+
module GetHelper
|
7
|
+
|
8
|
+
# Creates the header for a request.
|
9
|
+
def create_headers(specify_content_type, auth_token = nil)
|
10
|
+
headers = {
|
11
|
+
'Accept' => 'application/json'
|
12
|
+
}
|
13
|
+
|
14
|
+
if auth_token
|
15
|
+
headers['x-auth-token'] = auth_token
|
16
|
+
end
|
17
|
+
|
18
|
+
if specify_content_type
|
19
|
+
headers['Content-Type'] = 'application/json'
|
20
|
+
end
|
21
|
+
|
22
|
+
headers
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Raises an error on response failure.
|
27
|
+
def validate_response_success(response)
|
28
|
+
unless response.code.between?(200, 299)
|
29
|
+
raise BadResponseError.new(response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Returns a Typhoeus response.
|
35
|
+
def typhoeus_get(query)
|
36
|
+
# TODO: Simplify w/Typhoeus.get ?
|
37
|
+
Typhoeus::Request.new(url, params: query, headers: get_headers(true) ).run
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
# Takes methods and builds an internal hash where the method calls
|
3
|
+
# are keys, and their parameter is the value.
|
4
|
+
class HashBuilder
|
5
|
+
|
6
|
+
attr_reader :key_type
|
7
|
+
|
8
|
+
def initialize(key_type = String)
|
9
|
+
raise ArgumentError.new("Invalid key type '#{key_type}'") unless [String, Symbol].include?(key_type)
|
10
|
+
@key_type = key_type
|
11
|
+
@data = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def method_missing(method_name, *args)
|
16
|
+
value = args.first
|
17
|
+
key = (@key_type == String) ? method_name.to_s : method_name.to_sym
|
18
|
+
@data[key] = value
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def respond_to_missing?(method_name, include_private)
|
24
|
+
true # TODO: exclude ancestor methods?
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
@data
|
30
|
+
end
|
31
|
+
end
|