samanage 2.1.08 → 2.1.09

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e376a06968afc85a3f7f74f55999d7e548785b1d
4
- data.tar.gz: 6939690d8c8d2bfcaeb452a7ff072492f2f80fd1
3
+ metadata.gz: 1b27ba531435e04046290dc044b89fc0e2f51125
4
+ data.tar.gz: 203d7b74e6757cc0e8e97344e0e79b7fc97851de
5
5
  SHA512:
6
- metadata.gz: 6e4af5a38e8f3201079f00bec15360a66cad77dc4312dca56aaaa3afb131e7760ca8508a75baf3a0772ed60cd6798dc2d7c5db10542a047d7f79e9c9e5a33d8e
7
- data.tar.gz: 3bf8bc187267a37bdb101a97e0ebd45c9ebe8476851b77ee15ad2ad628a6dbed7273eb709f60c725dd20148ef6824d8683939ddea8c0a60894729be2bb46239b
6
+ metadata.gz: e44d497c9ff424afe653e29344904dae7290cec74af9c6f3695c00ae376db34136b47f179f83ad9025f46c35e82a31e342deee7a9216640618627fef1112da3b
7
+ data.tar.gz: 63f2235a968316f2c4617401c15f5e7fef12cf3e463c00621a7baa8e3d2c9d13b37ba89d4331ed4f3daa8853c4154c18e5164ff696c590d80ca6a4995e331a69
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gem 'rspec'
4
4
 
5
- gem 'httparty', '0.15.7'
5
+ gem 'httparty', '0.16.4'
6
6
  gem 'ffi', '1.9.24'
7
7
  group :development do
8
8
  gem 'guard-rspec', require: false
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.15.7)
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.15.7)
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 == String && self.content_type == 'json'
59
+ if payload.class == Hash && self.content_type == 'json'
59
60
  begin
60
- payload = JSON.parse(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, query: payload, headers: headers)
88
+ api_call = self.class.post(full_path, body: payload, headers: headers)
88
89
  when 'put'
89
- api_call = self.class.put(full_path, query: payload, headers: headers)
90
+ api_call = self.class.put(full_path, body: payload, headers: headers)
90
91
  when 'delete'
91
- api_call = self.class.delete(full_path, query: payload, headers: headers)
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 3
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!(:layout,'layout')) # layout not needed as audit only on individual record
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Samanage
2
- VERSION = '2.1.08'
2
+ VERSION = '2.1.09'
3
3
  end
@@ -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(201).or(200)
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(201).or(200)
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.08
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: 2018-10-24 00:00:00.000000000 Z
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