samanage 2.1.08 → 2.1.09
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +6 -2
- data/changelog.md +9 -0
- data/lib/samanage.rb +1 -0
- data/lib/samanage/api.rb +11 -10
- data/lib/samanage/api/attachments.rb +21 -0
- data/lib/samanage/api/incidents.rb +3 -3
- data/lib/samanage/api/purchase_orders.rb +62 -0
- data/lib/samanage/api/utils.rb +11 -0
- data/lib/samanage/utils.rb +24 -0
- data/lib/samanage/version.rb +1 -1
- data/spec/api/samanage_change_spec.rb +0 -1
- data/spec/api/samanage_department_spec.rb +1 -1
- data/spec/api/samanage_group_spec.rb +1 -1
- data/spec/api/samanage_purchase_order_spec.rb +82 -0
- data/spec/api/samanage_site_spec.rb +2 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b27ba531435e04046290dc044b89fc0e2f51125
|
4
|
+
data.tar.gz: 203d7b74e6757cc0e8e97344e0e79b7fc97851de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e44d497c9ff424afe653e29344904dae7290cec74af9c6f3695c00ae376db34136b47f179f83ad9025f46c35e82a31e342deee7a9216640618627fef1112da3b
|
7
|
+
data.tar.gz: 63f2235a968316f2c4617401c15f5e7fef12cf3e463c00621a7baa8e3d2c9d13b37ba89d4331ed4f3daa8853c4154c18e5164ff696c590d80ca6a4995e331a69
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -22,7 +22,8 @@ GEM
|
|
22
22
|
guard (~> 2.1)
|
23
23
|
guard-compat (~> 1.1)
|
24
24
|
rspec (>= 2.99.0, < 4.0)
|
25
|
-
httparty (0.
|
25
|
+
httparty (0.16.4)
|
26
|
+
mime-types (~> 3.0)
|
26
27
|
multi_xml (>= 0.5.2)
|
27
28
|
i18n (1.1.0)
|
28
29
|
concurrent-ruby (~> 1.0)
|
@@ -32,6 +33,9 @@ GEM
|
|
32
33
|
ruby_dep (~> 1.2)
|
33
34
|
lumberjack (1.0.13)
|
34
35
|
method_source (0.9.0)
|
36
|
+
mime-types (3.2.2)
|
37
|
+
mime-types-data (~> 3.2015)
|
38
|
+
mime-types-data (3.2018.0812)
|
35
39
|
multi_xml (0.6.0)
|
36
40
|
nenv (0.3.0)
|
37
41
|
notiffany (0.1.1)
|
@@ -67,7 +71,7 @@ DEPENDENCIES
|
|
67
71
|
faker
|
68
72
|
ffi (= 1.9.24)
|
69
73
|
guard-rspec
|
70
|
-
httparty (= 0.
|
74
|
+
httparty (= 0.16.4)
|
71
75
|
rspec
|
72
76
|
|
73
77
|
BUNDLED WITH
|
data/changelog.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
### 2.1.09
|
2
|
+
- Updated HTTParty to 0.16.4
|
3
|
+
- Optimize heavy audit+layout incident collection
|
4
|
+
- Adding purchase order support
|
5
|
+
- Added support for attachments
|
6
|
+
- Fix content type inheritance
|
7
|
+
- Use body instead of query params
|
8
|
+
- Handle HTTPTooManyRequests
|
9
|
+
|
1
10
|
### 2.1.08
|
2
11
|
- Correct Message
|
3
12
|
|
data/lib/samanage.rb
CHANGED
@@ -16,6 +16,7 @@ require 'samanage/api/incidents'
|
|
16
16
|
require 'samanage/api/mobiles'
|
17
17
|
require 'samanage/api/other_assets'
|
18
18
|
require 'samanage/api/problems'
|
19
|
+
require 'samanage/api/purchase_orders'
|
19
20
|
require 'samanage/api/requester'
|
20
21
|
require 'samanage/api/sites'
|
21
22
|
require 'samanage/api/solutions'
|
data/lib/samanage/api.rb
CHANGED
@@ -7,17 +7,18 @@ module Samanage
|
|
7
7
|
MAX_RETRIES = 3
|
8
8
|
PATHS = {
|
9
9
|
category: 'categories.json',
|
10
|
-
contract: 'contracts.json',
|
11
10
|
change: 'changes.json',
|
11
|
+
contract: 'contracts.json',
|
12
12
|
custom_fields: 'custom_fields.json',
|
13
13
|
custom_forms: 'custom_forms.json',
|
14
14
|
department: 'departments.json',
|
15
15
|
group: 'groups.json',
|
16
16
|
hardware: 'hardwares.json',
|
17
|
-
problem: 'problems.json',
|
18
17
|
incident: 'incidents.json',
|
19
18
|
mobile: 'mobiles.json',
|
20
19
|
other_asset: 'other_assets.json',
|
20
|
+
problem: 'problems.json',
|
21
|
+
purchase_order: 'purchase_orders.json',
|
21
22
|
site: 'sites.json',
|
22
23
|
solution: 'solutions.json',
|
23
24
|
user: 'users.json',
|
@@ -31,7 +32,7 @@ module Samanage
|
|
31
32
|
end
|
32
33
|
self.datacenter ||= datacenter.to_s.downcase
|
33
34
|
self.base_url = "https://api#{self.datacenter.to_s.downcase}.samanage.com/"
|
34
|
-
self.content_type = 'json'
|
35
|
+
self.content_type = content_type || 'json'
|
35
36
|
self.admins = []
|
36
37
|
self.max_retries = max_retries
|
37
38
|
if development_mode
|
@@ -55,9 +56,9 @@ module Samanage
|
|
55
56
|
|
56
57
|
# Calling execute without a method defaults to GET
|
57
58
|
def execute(http_method: 'get', path: nil, payload: nil, verbose: nil, headers: {})
|
58
|
-
if payload.class ==
|
59
|
+
if payload.class == Hash && self.content_type == 'json'
|
59
60
|
begin
|
60
|
-
payload =
|
61
|
+
payload = payload.to_json
|
61
62
|
rescue => e
|
62
63
|
puts "Invalid JSON: #{payload.inspect}"
|
63
64
|
raise Samanage::Error.new(error: e, response: nil)
|
@@ -84,17 +85,17 @@ module Samanage
|
|
84
85
|
when 'get'
|
85
86
|
api_call = self.class.get(full_path, headers: headers)
|
86
87
|
when 'post'
|
87
|
-
api_call = self.class.post(full_path,
|
88
|
+
api_call = self.class.post(full_path, body: payload, headers: headers)
|
88
89
|
when 'put'
|
89
|
-
api_call = self.class.put(full_path,
|
90
|
+
api_call = self.class.put(full_path, body: payload, headers: headers)
|
90
91
|
when 'delete'
|
91
|
-
api_call = self.class.delete(full_path,
|
92
|
+
api_call = self.class.delete(full_path, body: payload, headers: headers)
|
92
93
|
else
|
93
94
|
raise Samanage::Error.new(response: {response: 'Unknown HTTP method'})
|
94
95
|
end
|
95
|
-
rescue Errno::ECONNREFUSED, Net::OpenTimeout, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError, Errno::ENETDOWN, Errno::ECONNRESET, Errno::ENOENT, EOFError => e
|
96
|
+
rescue Errno::ECONNREFUSED, Net::OpenTimeout, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError, Errno::ENETDOWN, Errno::ECONNRESET, Errno::ENOENT, EOFError, Net::HTTPTooManyRequests => e
|
96
97
|
puts "[Warning] #{e.class}: #{e} - Retry: #{retries}/#{self.max_retries}"
|
97
|
-
sleep
|
98
|
+
sleep 5
|
98
99
|
retries += 1
|
99
100
|
retry if retries < self.max_retries
|
100
101
|
error = e
|
@@ -17,5 +17,26 @@ module Samanage
|
|
17
17
|
end
|
18
18
|
downloaded_attachment
|
19
19
|
end
|
20
|
+
|
21
|
+
def create_attachment(filepath: , attachable_type: , attachable_id: )
|
22
|
+
if !File.exists?(filepath)
|
23
|
+
puts "Cannot find filepath: '#{filepath.inspect}'"
|
24
|
+
return
|
25
|
+
end
|
26
|
+
self.class.post(
|
27
|
+
self.base_url + 'attachments.json',
|
28
|
+
body: {
|
29
|
+
'file[attachable_type]' => attachable_type,
|
30
|
+
'file[attachable_id]' => attachable_id,
|
31
|
+
'file[attachment]' => File.open(filepath, 'r')
|
32
|
+
},
|
33
|
+
headers: {
|
34
|
+
'Content-Type' => 'multipart/form-data',
|
35
|
+
'X-Samanage-Authorization' => 'Bearer ' + self.token
|
36
|
+
}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
20
41
|
end
|
21
42
|
end
|
@@ -15,17 +15,17 @@ module Samanage
|
|
15
15
|
# - layout: 'long'
|
16
16
|
def collect_incidents(options: {})
|
17
17
|
incidents = Array.new
|
18
|
-
total_pages = self.get_incidents(options: options)[:total_pages]
|
18
|
+
total_pages = self.get_incidents(options: options.except(:audit_archives,:audit_archive,:layout))[:total_pages]
|
19
19
|
puts "Pulling Incidents with Audit Archives (this may take a while)" if options[:audit_archives] && options[:verbose]
|
20
20
|
1.upto(total_pages) do |page|
|
21
21
|
puts "Collecting Incidents page: #{page}/#{total_pages}" if options[:verbose]
|
22
22
|
if options[:audit_archives]
|
23
23
|
options[:page] = page
|
24
|
-
params = URI.encode_www_form(options.except
|
24
|
+
params = URI.encode_www_form(options.except(:audit_archives,:audit_archive,:layout)) # layout not needed as audit only on individual record
|
25
25
|
paginated_path = "incidents.json?" + params
|
26
26
|
paginated_incidents = self.execute(path: paginated_path)[:data]
|
27
27
|
paginated_incidents.map do |incident|
|
28
|
-
params = self.set_params(options: options)
|
28
|
+
params = self.set_params(options: options.except(:audit_archives,:audit_archive,:layout))
|
29
29
|
archive_uri = "incidents/#{incident['id']}.json?layout=long&audit_archive=true"
|
30
30
|
incident_with_archive = self.execute(path: archive_uri)[:data]
|
31
31
|
if block_given?
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Samanage
|
2
|
+
class Api
|
3
|
+
|
4
|
+
# Default get purchase_order path
|
5
|
+
def get_purchase_orders(path: PATHS[:purchase_order], options: {})
|
6
|
+
params = self.set_params(options: options)
|
7
|
+
path = 'purchase_orders.json?' + params
|
8
|
+
self.execute(path: path)
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# Returns all purchase_orders.
|
13
|
+
# Options:
|
14
|
+
# - audit_archives: true
|
15
|
+
# - layout: 'long'
|
16
|
+
def collect_purchase_orders(options: {})
|
17
|
+
purchase_orders = Array.new
|
18
|
+
total_pages = self.get_purchase_orders(options: options)[:total_pages]
|
19
|
+
1.upto(total_pages) do |page|
|
20
|
+
options[:page] = page
|
21
|
+
params = self.set_params(options: options)
|
22
|
+
puts "Collecting purchase_orders page: #{page}/#{total_pages}" if options[:verbose]
|
23
|
+
path = "purchase_orders.json?" + params
|
24
|
+
request = self.execute(http_method: 'get', path: path)
|
25
|
+
request[:data].each do |purchase_order|
|
26
|
+
if block_given?
|
27
|
+
yield purchase_order
|
28
|
+
end
|
29
|
+
purchase_orders << purchase_order
|
30
|
+
end
|
31
|
+
end
|
32
|
+
purchase_orders
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Create an purchase_order given json
|
37
|
+
def create_purchase_order(payload: nil, options: {})
|
38
|
+
self.execute(path: PATHS[:purchase_order], http_method: 'post', payload: payload)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find purchase_order by ID
|
42
|
+
def find_purchase_order(id: , options: {})
|
43
|
+
path = "purchase_orders/#{id}.json"
|
44
|
+
if options[:layout] == 'long'
|
45
|
+
path += '?layout=long'
|
46
|
+
end
|
47
|
+
self.execute(path: path)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update an purchase_order given id and json
|
51
|
+
def update_purchase_order(payload: , id: , options: {})
|
52
|
+
path = "purchase_orders/#{id}.json"
|
53
|
+
self.execute(path: path, http_method: 'put', payload: payload)
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_purchase_order(id: )
|
57
|
+
self.execute(path: "purchase_orders/#{id}.json", http_method: 'delete')
|
58
|
+
end
|
59
|
+
|
60
|
+
alias_method :purchase_orders, :collect_purchase_orders
|
61
|
+
end
|
62
|
+
end
|
data/lib/samanage/api/utils.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'open-uri'
|
2
2
|
require 'fileutils'
|
3
|
+
|
4
|
+
# API Utils
|
3
5
|
module Samanage
|
4
6
|
class Api
|
5
7
|
def send_activation_email(email: )
|
@@ -7,5 +9,14 @@ module Samanage
|
|
7
9
|
raise Samanage::Error.new(error: 'Invalid Email', response: {}) unless user_id
|
8
10
|
self.execute(http_method: 'put', path: "users/#{user_id}.json?send_activation_email=1&add_callbacks=1")
|
9
11
|
end
|
12
|
+
|
13
|
+
def find_name_from_group_id(group_id: )
|
14
|
+
return if [-1,'-1',nil,''].include?(group_id)
|
15
|
+
begin
|
16
|
+
self.find_group(id: group_id).to_h.dig(:data,'name')
|
17
|
+
rescue => e
|
18
|
+
return "Unable to find user for group id #{group_id}"
|
19
|
+
end
|
20
|
+
end
|
10
21
|
end
|
11
22
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Consts and common lookup functions
|
2
|
+
module Samanage
|
3
|
+
INCIDENT_ORIGIN = {
|
4
|
+
1 => 'Web',
|
5
|
+
2 => 'Portal',
|
6
|
+
3 => 'API',
|
7
|
+
4 => 'Email'
|
8
|
+
}
|
9
|
+
|
10
|
+
def find_custom_field(custom_fields_values: , field_name:, user_type: false, user_resolve: 'email')
|
11
|
+
result = custom_fields_values.select{|field| field['name'] == field_name}.first.to_h
|
12
|
+
if user_type
|
13
|
+
if user_resolve == 'email'
|
14
|
+
result_value = result.dig('user','email')
|
15
|
+
else
|
16
|
+
result_value = result.dig('user','name')
|
17
|
+
end
|
18
|
+
else
|
19
|
+
result_value = result.dig('value')
|
20
|
+
end
|
21
|
+
return if [-1,'-1',nil,'',{}].include?(result_value)
|
22
|
+
result_value
|
23
|
+
end
|
24
|
+
end
|
data/lib/samanage/version.rb
CHANGED
@@ -51,7 +51,6 @@ describe Samanage::Api do
|
|
51
51
|
it 'find_change: returns a change card by known id' do
|
52
52
|
sample_id = @changes.sample['id']
|
53
53
|
change = @samanage.find_change(id: sample_id)
|
54
|
-
|
55
54
|
expect(change[:data]['id']).to eq(sample_id) # id should match found change
|
56
55
|
expect(change[:data]).to have_key('name')
|
57
56
|
expect(change[:data]).to have_key('requester')
|
@@ -32,7 +32,7 @@ describe Samanage::Api do
|
|
32
32
|
|
33
33
|
expect(department_create[:data]['id']).to be_an(Integer)
|
34
34
|
expect(department_create[:data]['name']).to eq(department_name)
|
35
|
-
expect(department_create[:code]).to eq(
|
35
|
+
expect(department_create[:code]).to eq(200).or(201)
|
36
36
|
end
|
37
37
|
it 'deletes a valid department' do
|
38
38
|
sample_department_id = @departments.sample.dig('id')
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'faker'
|
1
2
|
require 'samanage'
|
2
3
|
describe Samanage::Api do
|
3
4
|
context 'group' do
|
@@ -46,7 +47,6 @@ describe Samanage::Api do
|
|
46
47
|
group_name = group['name']
|
47
48
|
group_id = group['id']
|
48
49
|
found_group_id = @samanage.find_group_id_by_name(group: group_name)
|
49
|
-
|
50
50
|
expect(group_id).to eq(found_group_id)
|
51
51
|
end
|
52
52
|
it 'returns nil for finding invalid group by name' do
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'samanage'
|
2
|
+
require 'faker'
|
3
|
+
describe Samanage::Api do
|
4
|
+
context 'purchase_orders' do
|
5
|
+
describe 'API Functions' do
|
6
|
+
before(:all) do
|
7
|
+
TOKEN ||= ENV['SAMANAGE_TEST_API_TOKEN']
|
8
|
+
@samanage = Samanage::Api.new(token: TOKEN)
|
9
|
+
@purchase_orders = @samanage.purchase_orders
|
10
|
+
@users = @samanage.get_users[:data]
|
11
|
+
end
|
12
|
+
it 'get_purchase_orders: it returns API call of purchase_orders' do
|
13
|
+
api_call = @samanage.get_purchase_orders
|
14
|
+
expect(api_call).to be_a(Hash)
|
15
|
+
expect(api_call[:total_count]).to be_an(Integer)
|
16
|
+
expect(api_call).to have_key(:response)
|
17
|
+
expect(api_call).to have_key(:code)
|
18
|
+
end
|
19
|
+
it 'collect_purchase_orders: collects array of purchase_orders' do
|
20
|
+
purchase_order_count = @samanage.get_purchase_orders[:total_count]
|
21
|
+
@purchase_orders = @samanage.purchase_orders
|
22
|
+
expect(@purchase_orders).to be_an(Array)
|
23
|
+
expect(@purchase_orders.size).to eq(purchase_order_count)
|
24
|
+
end
|
25
|
+
it 'create_purchase_order(payload: json): creates a purchase_order' do
|
26
|
+
users_email = @samanage.collect_users.sample['email']
|
27
|
+
purchase_order_name = Faker::Book.title
|
28
|
+
json = {
|
29
|
+
purchase_order: {
|
30
|
+
buyer: {email: users_email},
|
31
|
+
name: purchase_order_name,
|
32
|
+
vendor: {name: 'Samanage'}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
purchase_order_create = @samanage.create_purchase_order(payload: json)
|
36
|
+
expect(purchase_order_create[:data]['id']).to be_an(Integer)
|
37
|
+
expect(purchase_order_create[:data]['name']).to eq(purchase_order_name)
|
38
|
+
expect(purchase_order_create[:code]).to eq(200).or(201)
|
39
|
+
end
|
40
|
+
it 'create_purchase_order: fails if no name/title' do
|
41
|
+
users_email = @users.sample['email']
|
42
|
+
json = {
|
43
|
+
:purchase_order => {
|
44
|
+
:buyer => {:email => users_email},
|
45
|
+
:description => "Description"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
expect{@samanage.create_purchase_order(payload: json)}.to raise_error(Samanage::InvalidRequest)
|
49
|
+
end
|
50
|
+
it 'find_purchase_order: returns a purchase_order card by known id' do
|
51
|
+
sample_id = @purchase_orders.sample['id']
|
52
|
+
purchase_order = @samanage.find_purchase_order(id: sample_id)
|
53
|
+
expect(purchase_order[:data]['id']).to eq(sample_id) # id should match found purchase_order
|
54
|
+
expect(purchase_order[:data]).to have_key('name')
|
55
|
+
expect(purchase_order[:data]).to have_key('id')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'find_purchase_order: returns nothing for an invalid id' do
|
59
|
+
sample_id = (0..10).entries.sample
|
60
|
+
expect{@samanage.find_purchase_order(id: sample_id)}.to raise_error(Samanage::NotFound) # id should match found purchase_order
|
61
|
+
end
|
62
|
+
it 'update_purchase_order: update_purchase_order by id' do
|
63
|
+
sample_purchase_order = @purchase_orders.reject{|i| ['Closed','Resolved'].include? i['state']}.sample
|
64
|
+
sample_id = sample_purchase_order['id']
|
65
|
+
description = (0...500).map { ('a'..'z').to_a[rand(26)] }.join
|
66
|
+
purchase_order_json = {
|
67
|
+
:purchase_order => {
|
68
|
+
:description => description
|
69
|
+
}
|
70
|
+
}
|
71
|
+
purchase_order_update = @samanage.update_purchase_order(payload: purchase_order_json, id: sample_id)
|
72
|
+
# expect(purchase_order_update[:data]['description']).to eq(description) # purchase_order bug #00044569
|
73
|
+
expect(purchase_order_update[:code]).to eq(200).or(201)
|
74
|
+
end
|
75
|
+
it 'deletes a valid purchase_order' do
|
76
|
+
sample_purchase_order_id = @purchase_orders.sample['id']
|
77
|
+
purchase_order_delete = @samanage.delete_purchase_order(id: sample_purchase_order_id)
|
78
|
+
expect(purchase_order_delete[:code]).to eq(200).or(201)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'samanage'
|
2
|
+
require 'faker'
|
2
3
|
describe Samanage::Api do
|
3
4
|
context 'Site' do
|
4
5
|
before(:all) do
|
@@ -34,7 +35,7 @@ describe Samanage::Api do
|
|
34
35
|
|
35
36
|
expect(site_create[:data]['id']).to be_an(Integer)
|
36
37
|
expect(site_create[:data]['name']).to eq(site_name)
|
37
|
-
expect(site_create[:code]).to eq(
|
38
|
+
expect(site_create[:code]).to eq(200).or(201)
|
38
39
|
end
|
39
40
|
it 'deletes a valid site' do
|
40
41
|
sample_site_id = @sites.sample['id']
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: samanage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.09
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Walls
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- lib/samanage/api/mobiles.rb
|
68
68
|
- lib/samanage/api/other_assets.rb
|
69
69
|
- lib/samanage/api/problems.rb
|
70
|
+
- lib/samanage/api/purchase_orders.rb
|
70
71
|
- lib/samanage/api/requester.rb
|
71
72
|
- lib/samanage/api/sites.rb
|
72
73
|
- lib/samanage/api/solutions.rb
|
@@ -76,6 +77,7 @@ files:
|
|
76
77
|
- lib/samanage/error.rb
|
77
78
|
- lib/samanage/language.rb
|
78
79
|
- lib/samanage/url_builder.rb
|
80
|
+
- lib/samanage/utils.rb
|
79
81
|
- lib/samanage/version.rb
|
80
82
|
- samanage.gemspec
|
81
83
|
- sample_file.txt
|
@@ -93,6 +95,7 @@ files:
|
|
93
95
|
- spec/api/samanage_mobile_spec.rb
|
94
96
|
- spec/api/samanage_other_asset_spec.rb
|
95
97
|
- spec/api/samanage_problem_spec.rb
|
98
|
+
- spec/api/samanage_purchase_order_spec.rb
|
96
99
|
- spec/api/samanage_site_spec.rb
|
97
100
|
- spec/api/samanage_solution_spec.rb
|
98
101
|
- spec/api/samanage_time_tracks_spec.rb
|