sis_ruby 1.0.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 +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
@@ -0,0 +1,117 @@
|
|
1
|
+
module SisRuby
|
2
|
+
class Params
|
3
|
+
|
4
|
+
# These methods behave as simple accessors when called with no arguments.
|
5
|
+
# When called with an argument, that argument is used to set the value corresponding to the method name.
|
6
|
+
|
7
|
+
# The conventional 'attribute=' method forms for this class are supported
|
8
|
+
# in addition to the methods below, as an alternate way to set values.
|
9
|
+
|
10
|
+
attr_writer :fields, :filter, :limit, :offset, :sort
|
11
|
+
|
12
|
+
def fields(*args)
|
13
|
+
if args.none?
|
14
|
+
return @fields
|
15
|
+
elsif args.one?
|
16
|
+
@fields = Array(args.first)
|
17
|
+
else
|
18
|
+
@fields = args
|
19
|
+
end
|
20
|
+
@fields = @fields.to_set # order shouldn't matter, and no dups
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def filter(*args)
|
26
|
+
if args.any?
|
27
|
+
@filter = args.first
|
28
|
+
self
|
29
|
+
else
|
30
|
+
@filter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def limit(*args)
|
36
|
+
if args.any?
|
37
|
+
@limit = args.first
|
38
|
+
self
|
39
|
+
else
|
40
|
+
@limit
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def offset(*args)
|
46
|
+
if args.any?
|
47
|
+
@offset = args.first
|
48
|
+
self
|
49
|
+
else
|
50
|
+
@offset
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def sort(*args)
|
56
|
+
if args.any?
|
57
|
+
@sort = args.first
|
58
|
+
self
|
59
|
+
else
|
60
|
+
@sort
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def to_hash
|
66
|
+
h = {}
|
67
|
+
h['limit'] = limit if limit
|
68
|
+
h['offset'] = offset if offset
|
69
|
+
h['fields'] = fields.to_a.join(',') if fields
|
70
|
+
h['q'] = filter if filter
|
71
|
+
h['sort'] = sort if sort
|
72
|
+
h
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def self.from_hash(other)
|
77
|
+
instance = self.new
|
78
|
+
instance.limit(other['limit']) if other['limit']
|
79
|
+
instance.fields(other['fields'].split(',').to_set) if other['fields']
|
80
|
+
instance.offset(other['offset']) if other['offset']
|
81
|
+
instance.filter(other['q']) if other['q']
|
82
|
+
instance.sort(other['sort']) if other['sort']
|
83
|
+
instance
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def clone
|
88
|
+
other = Params.new
|
89
|
+
other.limit(self.limit) if self.limit
|
90
|
+
other.fields(self.fields.clone) if self.fields
|
91
|
+
other.offset(self.offset) if self.offset
|
92
|
+
other.filter(self.filter.clone) if self.filter
|
93
|
+
other.sort(self.sort) if self.sort
|
94
|
+
other
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def to_h
|
99
|
+
to_hash
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def hash
|
104
|
+
to_h.hash
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def ==(other)
|
109
|
+
other.is_a?(self.class) && other.to_h == self.to_h
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def <=>(other)
|
114
|
+
self.to_h <=> other.to_h
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'trick_bag'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'typhoeus'
|
4
|
+
require_relative 'get_helper'
|
5
|
+
require_relative 'params'
|
6
|
+
|
7
|
+
|
8
|
+
module SisRuby
|
9
|
+
|
10
|
+
|
11
|
+
# This class is an enumerator over a result set of SIS objects, as specified
|
12
|
+
# in a hash-like object (that is, any object that implements to_hash,
|
13
|
+
# such as a SisParams instance). It can be used as any Enumerable;
|
14
|
+
# that is, methods such as each, map, select, etc. can be called;
|
15
|
+
# an array of all results can be produced by calling to_a,
|
16
|
+
# and an Enumerator can be gotten by calling each without a chunk.
|
17
|
+
#
|
18
|
+
# Internally it fetches chunks of records only when needed to serve the values
|
19
|
+
# to the caller. The chunk size can be specified by the caller.
|
20
|
+
#
|
21
|
+
# By calling each or each_slice you can access some of the data before fetching
|
22
|
+
# all of it. This can be handy if:
|
23
|
+
#
|
24
|
+
# 1) there may not be enough available memory to process the entire result set
|
25
|
+
# at once
|
26
|
+
# 2) you want to process some records while others are being fetched, by
|
27
|
+
# using multiple threads, for example.
|
28
|
+
#
|
29
|
+
class ResultEnumerable < TrickBag::Enumerables::BufferedEnumerable
|
30
|
+
|
31
|
+
include GetHelper
|
32
|
+
include Enumerable
|
33
|
+
|
34
|
+
DEFAULT_CHUNK_RECORD_COUNT = 5_000
|
35
|
+
|
36
|
+
|
37
|
+
# @param endpoint_url the SIS endpoint URL string or an Endpoint instance
|
38
|
+
# @param params any object that implements to_hash, e.g. a Params instance
|
39
|
+
# @param chunk_size the maximum number of records to fetch from the server in a request
|
40
|
+
def initialize(endpoint_url, params = {}, chunk_size = DEFAULT_CHUNK_RECORD_COUNT)
|
41
|
+
super(chunk_size)
|
42
|
+
@endpoint_url = endpoint_url.is_a?(Endpoint) ? endpoint_url.url : endpoint_url
|
43
|
+
@outer_params = params.is_a?(Params) ? params : Params.from_hash(params.to_hash)
|
44
|
+
|
45
|
+
inner_limit = @outer_params.limit ? [chunk_size, @outer_params.limit].min : chunk_size
|
46
|
+
@inner_params = @outer_params.clone.limit(inner_limit)
|
47
|
+
|
48
|
+
@inner_params.offset ||= 0
|
49
|
+
@position = 0
|
50
|
+
@total_count = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def fetch
|
55
|
+
# This exits the fetching when the desired number of records has been fetched.
|
56
|
+
if @outer_params.limit && @yield_count >= @outer_params.limit
|
57
|
+
self.data = []
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
request = Typhoeus::Request.new(@endpoint_url, params: @inner_params.to_hash, headers: create_headers(true))
|
62
|
+
response = request.run
|
63
|
+
validate_response_success(response)
|
64
|
+
self.data = JSON.parse(response.body)
|
65
|
+
@total_count = response.headers['x-total-count'].to_i
|
66
|
+
@inner_params.offset(@inner_params.offset + chunk_size)
|
67
|
+
|
68
|
+
# TODO: Deal with this
|
69
|
+
# if @total_count > chunk_size && @inner_params.sort.nil?
|
70
|
+
# raise "Total count (#{@total_count}) exceeds chunk size (#{chunk_size})." +
|
71
|
+
# "When this is the case, a sort order must be specified, or chunk size increased."
|
72
|
+
# end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def total_count
|
77
|
+
fetch unless @total_count
|
78
|
+
@total_count
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def fetch_notify
|
83
|
+
# puts "Fetch at #{Time.now}: chunk size: #{chunk_size}, yield count: #{yield_count}, total count = #{@total_count}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/sis_ruby.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Load all *.rb files in lib/sis_ruby and below.
|
2
|
+
# Use a lambda so that the intermediate variables do not survive this file.
|
3
|
+
->() {
|
4
|
+
start_dir = File.join(File.dirname(__FILE__), 'sis_ruby') # the lib directory
|
5
|
+
file_mask = "#{start_dir}/**/*.rb"
|
6
|
+
Dir[file_mask].each { |file| require file }
|
7
|
+
}.()
|
8
|
+
|
data/samples/sample-1.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sis_ruby'
|
4
|
+
require 'awesome_print'
|
5
|
+
|
6
|
+
include SisRuby
|
7
|
+
|
8
|
+
raise "Must specify SIS server URL in SIS_URL environment variable." unless ENV['SIS_URL']
|
9
|
+
client = Client.new(ENV['SIS_URL'])
|
10
|
+
host_endpoint = client.entities('host')
|
11
|
+
|
12
|
+
records = host_endpoint.list(Params.new.fields('hostname').limit(3))
|
13
|
+
puts "Fetched #{records.size} host records."
|
14
|
+
puts "Last one is: #{records.last}, host name is #{records.last['hostname']}"
|
15
|
+
|
16
|
+
|
17
|
+
full_record = host_endpoint.list(Params.new.limit(1)).first
|
18
|
+
puts 'Host record keys:'
|
19
|
+
ap full_record.keys
|
20
|
+
|
21
|
+
puts "\n\n"
|
22
|
+
query = Params.new.limit(1).fields('hostname', 'fqdn', 'status')
|
23
|
+
result = host_endpoint.list_as_openstructs(query).first
|
24
|
+
puts "Host: #{result.hostname}, FQDN: #{result.fqdn}, Status: #{result.status}"
|
25
|
+
puts "\nEntire record:"
|
26
|
+
ap result
|
data/samples/sample-2.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'awesome_print'
|
4
|
+
require 'pry'
|
5
|
+
require 'sis_ruby'
|
6
|
+
|
7
|
+
raise "Must specify SIS server URL in SIS_URL environment variable." unless ENV['SIS_URL']
|
8
|
+
client = Client.new(ENV['SIS_URL'])
|
9
|
+
HOSTS = client.entities('host')
|
10
|
+
# puts "Total host count is #{hosts.count}"
|
11
|
+
# puts "Total qa host count is #{hosts.count('environment' => 'qa')}"
|
12
|
+
|
13
|
+
# p1 = SisRuby::Params.new.sort('host').offset(9000).limit(2)
|
14
|
+
# p1 = SisRuby::Params.new.sort('host').offset(9000).limit(2).fields('hostname', 'environment')
|
15
|
+
# records = hosts.list(p1)
|
16
|
+
# ap records.first
|
17
|
+
|
18
|
+
|
19
|
+
def fetch(count, offset)
|
20
|
+
HOSTS.list(SisRuby::Params.new.fields('hostname').limit(count).offset(offset))
|
21
|
+
end
|
22
|
+
|
23
|
+
arrays = (0..10).to_a.map { |i| fetch(1000, i * 1000) }
|
24
|
+
a_combined = arrays.flatten
|
25
|
+
a_uniq = a_combined.uniq
|
26
|
+
host_names = a_combined.map { |h| h['hostname'] }
|
27
|
+
|
28
|
+
puts "Combined count is #{a_combined.size}"
|
29
|
+
puts "Unique count is #{a_uniq.size}"
|
30
|
+
puts "Host name count is #{host_names.size}"
|
31
|
+
puts "Unique host name count is #{host_names.uniq.size}"
|
32
|
+
binding.pry
|
33
|
+
|
data/sis_ruby.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib', 'sis_ruby', 'version')
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'sis_ruby'
|
5
|
+
s.version = SisRuby::VERSION
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ['Keith Bennett', 'Neel Goyal']
|
8
|
+
s.email = ['keithrbennett@gmail.com', 'sis-dev@verisign.com']
|
9
|
+
s.homepage = 'https://github.com/keithrbennett/sis_ruby'
|
10
|
+
s.summary = 'SIS Ruby Client'
|
11
|
+
s.description = 'Ruby client for SIS web service (https://github.com/sis-cmdb/sis-api)'
|
12
|
+
s.files = `git ls-files`.split($/)
|
13
|
+
s.require_path = 'lib'
|
14
|
+
s.license = 'BSD 3-Clause'
|
15
|
+
|
16
|
+
s.add_dependency 'awesome_print', '>= 1.6.0'
|
17
|
+
s.add_dependency 'rake'
|
18
|
+
s.add_dependency 'trick_bag', '>= 0.63.1'
|
19
|
+
s.add_dependency 'typhoeus', '>= 0.8.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'rspec', '>= 3.0'
|
22
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/sis_ruby/exceptions/missing_id_error'
|
3
|
+
|
4
|
+
# These tests test client behavior that does not require a server.
|
5
|
+
|
6
|
+
module SisRuby
|
7
|
+
|
8
|
+
describe Client do
|
9
|
+
|
10
|
+
let(:server_url) { 'https://fictitious-domain.com' }
|
11
|
+
let(:id_fieldname) { '_id_' }
|
12
|
+
let(:client) { Client.new(server_url) }
|
13
|
+
let(:host_endpoint) { client.entities('host', id_fieldname) }
|
14
|
+
|
15
|
+
context 'constructor parameters' do
|
16
|
+
|
17
|
+
specify 'omitting version gets default version' do
|
18
|
+
expect(client.api_version).to eq(Client::DEFAULT_API_VERSION)
|
19
|
+
end
|
20
|
+
|
21
|
+
specify 'overriding default version works' do
|
22
|
+
api_v = 'v_not_default'
|
23
|
+
expect(Client.new(server_url, api_version: api_v).api_version).to eq(api_v)
|
24
|
+
end
|
25
|
+
|
26
|
+
specify 'setting auth_token works' do
|
27
|
+
auth_token = 'abcdefgxyz'
|
28
|
+
expect(Client.new(server_url, auth_token: auth_token).auth_token).to eq(auth_token)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'precreated endpoints' do
|
34
|
+
|
35
|
+
specify 'are precreated' do
|
36
|
+
endpoints = [client.hooks, client.schemas, client.hiera]
|
37
|
+
expect(endpoints.all? { |ep| ep.is_a?(Endpoint) }).to eq(true)
|
38
|
+
end
|
39
|
+
|
40
|
+
specify 'have URLs that end in the right name' do
|
41
|
+
endpoints = [client.hooks, client.schemas, client.hiera]
|
42
|
+
endpoints_and_names = endpoints.zip(%w(hooks schemas hiera))
|
43
|
+
expect(endpoints_and_names.all? { |ep, name| ep.url.end_with?(name) }).to eq(true)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
context 'tokens' do
|
49
|
+
|
50
|
+
specify 'the URL is correct' do
|
51
|
+
username = 'david_bisbal'
|
52
|
+
token = client.tokens(username)
|
53
|
+
expect(token.url.end_with?("users/#{username}/tokens")).to eq(true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/sis_ruby/exceptions/missing_id_error'
|
3
|
+
|
4
|
+
module SisRuby
|
5
|
+
|
6
|
+
describe Endpoint do
|
7
|
+
|
8
|
+
let(:id_fieldname) { '_id_' }
|
9
|
+
let(:server_url) { 'https://fictitious-domain.com' }
|
10
|
+
let(:client) { Client.new(server_url) }
|
11
|
+
let(:host_endpoint) { client.entities('host', id_fieldname) }
|
12
|
+
|
13
|
+
context '#id_from_param' do
|
14
|
+
|
15
|
+
it 'works with an object other than hash' do
|
16
|
+
expect(host_endpoint.id_from_param(3)).to eq(3)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'works with a hash' do
|
20
|
+
expect(host_endpoint.id_from_param({ id_fieldname => 3 } )).to eq(3)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises an error on nil' do
|
24
|
+
expect { host_endpoint.id_from_param(nil) }.to raise_error(MissingIdError)
|
25
|
+
expect { host_endpoint.id_from_param( { id_fieldname => nil } ) }.to raise_error((MissingIdError))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
context '#==' do
|
31
|
+
|
32
|
+
specify '2 endpoints created with the same parameters are ==' do
|
33
|
+
create_endpoint = -> { Endpoint.new(client, 'abcxyz', 'foo') }
|
34
|
+
endpoint_0 = create_endpoint.()
|
35
|
+
endpoint_1 = create_endpoint.()
|
36
|
+
expect(endpoint_1).to eq(endpoint_0)
|
37
|
+
end
|
38
|
+
|
39
|
+
specify '2 endpoints created with different clients are not ==' do
|
40
|
+
client1 = Client.new('url1')
|
41
|
+
client2 = Client.new('url2')
|
42
|
+
expect(Endpoint.new(client1, 'foo')).not_to eq(Endpoint.new(client2, 'foo'))
|
43
|
+
end
|
44
|
+
|
45
|
+
specify '2 endpoints created with different endpoint names are not ==' do
|
46
|
+
expect(Endpoint.new(client, 'foo')).not_to eq(Endpoint.new(client, 'bar'))
|
47
|
+
end
|
48
|
+
|
49
|
+
specify '2 endpoints created with different id fields are not ==' do
|
50
|
+
expect(Endpoint.new(client, 'foo', 'x')).not_to eq(Endpoint.new(client, 'foo', 'y'))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe HashBuilder do
|
4
|
+
|
5
|
+
PERMITTED_ENTITY_ENDPOINT_TOP_LEVEL_KEYS = %w(
|
6
|
+
fields
|
7
|
+
id
|
8
|
+
limit
|
9
|
+
offset
|
10
|
+
q
|
11
|
+
)
|
12
|
+
|
13
|
+
QUERY_HASH = {
|
14
|
+
'environment' => 'prod',
|
15
|
+
'status' => 'live', # not maintenance
|
16
|
+
'role.product.name' => 'mdns',
|
17
|
+
}
|
18
|
+
|
19
|
+
specify 'will create a limit key/value pair correctly' do
|
20
|
+
builder = HashBuilder.new
|
21
|
+
expect(builder.limit(10).to_h).to eq({ 'limit' => 10 })
|
22
|
+
end
|
23
|
+
|
24
|
+
specify 'subsequent calls will replace an existing value' do
|
25
|
+
builder = HashBuilder.new
|
26
|
+
builder.limit(10)
|
27
|
+
expect(builder.limit(11).to_h).to eq({ 'limit' => 11 })
|
28
|
+
end
|
29
|
+
|
30
|
+
specify 'a query hash will be correctly inserted' do
|
31
|
+
builder = HashBuilder.new
|
32
|
+
builder.q(QUERY_HASH)
|
33
|
+
expect(builder.to_h).to eq({ 'q' => QUERY_HASH})
|
34
|
+
end
|
35
|
+
|
36
|
+
specify '2 key/value pairs are no problem' do
|
37
|
+
builder = HashBuilder.new
|
38
|
+
builder.q(QUERY_HASH).limit(20)
|
39
|
+
expect(builder.to_h).to eq({ 'q' => QUERY_HASH, 'limit' => 20 })
|
40
|
+
end
|
41
|
+
|
42
|
+
specify 'keys are all Strings when key type of String is specified' do
|
43
|
+
builder = HashBuilder.new(String)
|
44
|
+
expect(builder.foo(1).bar(2).to_h.keys.all? { |key| key.is_a?(String) }).to eq(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
specify 'keys are all Symbols when key type of Symbol is specified' do
|
48
|
+
builder = HashBuilder.new(Symbol)
|
49
|
+
expect(builder.foo(1).bar(2).to_h.keys.all? { |key| key.is_a?(Symbol) }).to eq(true)
|
50
|
+
end
|
51
|
+
|
52
|
+
specify 'respond_to_missing? always returns true' do
|
53
|
+
builder = HashBuilder.new
|
54
|
+
random_name = 'x' + Random.rand(1_000_000).to_s
|
55
|
+
expect(builder.respond_to?(random_name, false)).to eq(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Disable permitted methods feature for now; it was not fully implemented,
|
59
|
+
# and should be a predicate lambda rather than an array anyway.
|
60
|
+
|
61
|
+
# context 'permitted methods' do
|
62
|
+
#
|
63
|
+
# specify 'when not specified, all are allowed' do
|
64
|
+
# builder = HashBuilder.new
|
65
|
+
# expect(builder.respond_to?('foo')).to eq(true)
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# specify 'when empty, none are allowed' do
|
69
|
+
# builder = HashBuilder.new(String, [])
|
70
|
+
# expect(builder.new(String).respond_to?('foo')).to eq(false)
|
71
|
+
# builder.something(123)
|
72
|
+
# expect { builder.something(123) }.to raise_error
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# specify 'when non-empty, only permitted are permitted' do
|
76
|
+
# builder = HashBuilder.new(String, [:foo])
|
77
|
+
# expect(builder.new(String).respond_to?('foo')).to eq(true)
|
78
|
+
# expect(builder.new(String).respond_to?('bar')).to eq(false)
|
79
|
+
#
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
end
|
83
|
+
|
data/spec/params_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
module SisRuby
|
4
|
+
|
5
|
+
describe Params do
|
6
|
+
|
7
|
+
it 'results in an empty hash when nothing is set' do
|
8
|
+
expect(Params.new.to_hash).to eq({})
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'correctly concatenate fields' do
|
12
|
+
fields = %w(foo bar).to_set
|
13
|
+
params = Params.new.fields(fields)
|
14
|
+
expect(params.fields).to eq(fields)
|
15
|
+
expect(params.to_hash).to eq({ 'fields' => 'foo,bar'})
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sets limit correctly' do
|
19
|
+
params = Params.new.limit(3)
|
20
|
+
expect(params.limit).to eq(3)
|
21
|
+
expect(params.to_hash).to eq({ 'limit' => 3 })
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'sets offset correctly' do
|
25
|
+
params = Params.new.offset(4)
|
26
|
+
expect(params.offset).to eq(4)
|
27
|
+
expect(params.to_hash).to eq({ 'offset' => 4 })
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'sets filter correctly' do
|
31
|
+
filter = { 'my_numeric_field' => { '$gt' => 10 }}
|
32
|
+
params = Params.new.filter(filter)
|
33
|
+
expect(params.filter).to eq(filter)
|
34
|
+
expect(params.to_hash).to eq({ 'q' => filter })
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'sets sort correctly' do
|
38
|
+
params = Params.new.sort('-count')
|
39
|
+
expect(params.sort).to eq('-count')
|
40
|
+
expect(params.to_hash).to eq({ 'sort' => '-count' })
|
41
|
+
end
|
42
|
+
|
43
|
+
specify 'to_h and to_hash return equal hashes' do
|
44
|
+
params = Params.new.sort('-count')
|
45
|
+
expect(params.to_h).to eq(params.to_hash)
|
46
|
+
end
|
47
|
+
|
48
|
+
specify '2 identical objects are ==, and <=> returns 0' do
|
49
|
+
params1 = Params.new.sort('-count')
|
50
|
+
params2 = Params.new.sort('-count')
|
51
|
+
expect(params1).to eq(params2)
|
52
|
+
expect(params1 <=> params2).to eq(0)
|
53
|
+
end
|
54
|
+
|
55
|
+
specify '2 different objects are not ==, and <=> returns non-0' do
|
56
|
+
params1 = Params.new.sort('-count')
|
57
|
+
params2 = Params.new.sort('-date')
|
58
|
+
expect(params1).not_to eq(params2)
|
59
|
+
expect(params1 <=> params2).not_to eq(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
specify 'clone returns an equal copy' do
|
64
|
+
params_orig = Params.new.sort('-count').limit(300).filter({key: 'value'}).offset(100).fields('a', 'b')
|
65
|
+
params_clone = params_orig.clone
|
66
|
+
expect(params_clone).to eq(params_orig)
|
67
|
+
end
|
68
|
+
|
69
|
+
specify 'from_hash returns an equal copy' do
|
70
|
+
params_orig = Params.new.sort('-count').limit(300).filter({key: 'value'}).offset(100).fields('a', 'b')
|
71
|
+
params_copy = Params.from_hash(params_orig.to_hash)
|
72
|
+
expect(params_copy).to eq(params_orig)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This script drops the Mongo DB and adds the test user.
|
4
|
+
# It assumes the existence of a ~/opt/sis-api directory
|
5
|
+
# that is a clone of the sis-api git repo, and a 'test-user.json'
|
6
|
+
# data file that is a copy of the file by that name in this directory.
|
7
|
+
#
|
8
|
+
# The script should be run from the directory in which it exists.
|
9
|
+
|
10
|
+
mongo drop-db.js
|
11
|
+
#echo "use sis;\n db.dropDatabase();\n exit" | mongo
|
12
|
+
cd ~/opt/sis-api
|
13
|
+
node tools/useradmin.js update test-user.json
|
14
|
+
cd -
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../lib/sis_ruby'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
{
|
2
|
+
"entity_type": "entity_test",
|
3
|
+
"id_field": "_id",
|
4
|
+
"required_schema": {
|
5
|
+
"name": "entity_test",
|
6
|
+
"owner": "schema_owner",
|
7
|
+
"definition": {
|
8
|
+
"name": {
|
9
|
+
"type": "String",
|
10
|
+
"required": true,
|
11
|
+
"unique": true
|
12
|
+
},
|
13
|
+
"number": {
|
14
|
+
"type": "Number",
|
15
|
+
"unique": true,
|
16
|
+
"required": true
|
17
|
+
}
|
18
|
+
},
|
19
|
+
"_sis": {
|
20
|
+
"owner": "test"
|
21
|
+
}
|
22
|
+
},
|
23
|
+
"valid_items": [
|
24
|
+
{
|
25
|
+
"name": "entity1",
|
26
|
+
"number": 0
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"name": "entity2",
|
30
|
+
"number": 1
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"name": "entity3",
|
34
|
+
"number": 2
|
35
|
+
}
|
36
|
+
],
|
37
|
+
"invalid_items": [
|
38
|
+
{
|
39
|
+
"foo": "bar"
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"name": "entity1",
|
43
|
+
"number": 4
|
44
|
+
},
|
45
|
+
{
|
46
|
+
"name": "non_unique_num",
|
47
|
+
"number": 0
|
48
|
+
}
|
49
|
+
]
|
50
|
+
}
|