bamboozled 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. data/.github/ISSUE_TEMPLATE/question.md +19 -0
  5. data/.github/main.workflow +38 -0
  6. data/.github/pull_request_template.md +27 -0
  7. data/.gitignore +2 -0
  8. data/.rubocop.yml +4 -2
  9. data/.rubocop_todo.yml +305 -0
  10. data/.travis.yml +7 -8
  11. data/CHANGELOG.md +22 -1
  12. data/CONTRIBUTING.md +2 -2
  13. data/Dockerfile +8 -0
  14. data/Gemfile +3 -2
  15. data/README.md +11 -1
  16. data/bamboozled.gemspec +2 -3
  17. data/lib/bamboozled.rb +4 -2
  18. data/lib/bamboozled/api/base.rb +63 -59
  19. data/lib/bamboozled/api/employee.rb +1 -6
  20. data/lib/bamboozled/api/field_collection.rb +107 -0
  21. data/lib/bamboozled/api/meta.rb +15 -5
  22. data/lib/bamboozled/api/report.rb +10 -6
  23. data/lib/bamboozled/api/time_off.rb +9 -6
  24. data/lib/bamboozled/api/time_tracking.rb +24 -0
  25. data/lib/bamboozled/base.rb +12 -6
  26. data/lib/bamboozled/version.rb +1 -1
  27. data/spec/fixtures/add_employee_xml.yml +1 -1
  28. data/spec/fixtures/custom_report.json +38 -0
  29. data/spec/fixtures/meta_fields.json +5 -0
  30. data/spec/fixtures/meta_lists.json +5 -0
  31. data/spec/fixtures/meta_tables.json +5 -0
  32. data/spec/fixtures/meta_users.json +4 -0
  33. data/spec/fixtures/time_tracking_add_200_response.json +7 -0
  34. data/spec/fixtures/time_tracking_add_empty_response.json +9 -0
  35. data/spec/fixtures/time_tracking_adjust_200_response.json +7 -0
  36. data/spec/fixtures/time_tracking_adjust_400_response.json +11 -0
  37. data/spec/fixtures/time_tracking_record_200_response.json +9 -0
  38. data/spec/fixtures/time_tracking_record_400_response.json +11 -0
  39. data/spec/fixtures/time_tracking_record_401_response.json +10 -0
  40. data/spec/fixtures/time_tracking_record_404_response.json +8 -0
  41. data/spec/fixtures/update_employee_xml.yml +1 -1
  42. data/spec/lib/bamboozled/api/base_spec.rb +18 -0
  43. data/spec/lib/bamboozled/api/employee_spec.rb +0 -14
  44. data/spec/lib/bamboozled/api/field_collection_spec.rb +17 -0
  45. data/spec/lib/bamboozled/api/meta_spec.rb +47 -0
  46. data/spec/lib/bamboozled/api/report_spec.rb +17 -0
  47. data/spec/lib/bamboozled/api/time_tracking_spec.rb +123 -0
  48. data/spec/lib/bamboozled/base_spec.rb +26 -0
  49. data/spec/lib/bamboozled_spec.rb +33 -0
  50. metadata +54 -21
  51. data/.hound.yml +0 -2
data/.travis.yml CHANGED
@@ -1,19 +1,18 @@
1
1
  language: ruby
2
2
  sudo: false
3
- cache: bundler
4
3
  rvm:
5
- - 2.0.0
6
- - 2.1
7
4
  - 2.2
5
+ - 2.3.0
6
+ - 2.4.0
8
7
  - ruby-head
9
8
  - jruby-head
9
+
10
+ script:
11
+ - bundle exec rspec
12
+ - bundle exec rubocop
13
+
10
14
  matrix:
11
15
  fast_finish: true
12
16
  allow_failures:
13
17
  - rvm: ruby-head
14
18
  - rvm: jruby-head
15
- before_install: gem update --remote bundler && bundle --version
16
- install:
17
- - bundle install --retry=3
18
- script:
19
- - bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## master (unreleased)
3
+ ## develop (unreleased)
4
4
 
5
5
  ### New Features
6
6
 
@@ -8,6 +8,22 @@
8
8
 
9
9
  ### Bug Fixes
10
10
 
11
+ ## 0.2.0 (2019-03-01)
12
+
13
+ ### New Features
14
+
15
+ * New method `Report#custom` allows fetching a custom report of employees. ([@artfuldodger][])
16
+ * Make httpparty options configurable ([@ivanovv][])
17
+ * Add time tracking api interface ([@nlively][])
18
+
19
+ ### Changes
20
+
21
+ * Using github actions for linting and testing
22
+
23
+ ### Bug Fixes
24
+
25
+ * Fix Metadata API calls. Fixes [Issue #38](https://github.com/Skookum/bamboozled/issues/36) and [Issue #5](https://github.com/Skookum/bamboozled/issues/5)
26
+
11
27
  ## 0.1.0 (2016-06-14)
12
28
 
13
29
  ### New Features
@@ -25,3 +41,8 @@
25
41
  [@enriikke]: https://github.com/Enriikke
26
42
  [@kylefdoherty]: https://github.com/kylefdoherty
27
43
  [@mjording]: https://github.com/mjording
44
+ [@artfuldodger]: https://github.com/artfuldodger
45
+ [@splybon]: https://github.com/splybon
46
+ [@chrisman]: https://github.com/chrisman
47
+ [@ivanovv]: https://github.com/ivanovv
48
+ [@nlively]: https://github.com/nlively
data/CONTRIBUTING.md CHANGED
@@ -7,7 +7,7 @@ below when doing so.
7
7
  ## Issue Reporting
8
8
 
9
9
  * Make sure the issue has not already been reported.
10
- * Check that the issue has not already been fixed in `master`.
10
+ * Check that the issue has not already been fixed in `develop`.
11
11
  * Open a [new issue](https://github.com/Skookum/bamboozled/issues/new) with a
12
12
  clear and concise description of the problem.
13
13
  * Include any relevant code or error output in the issue summary.
@@ -15,7 +15,7 @@ below when doing so.
15
15
  ## Pull Request
16
16
 
17
17
  * Fork the project.
18
- * Create a new branch for your feature/bug.
18
+ * Create a new branch for your feature/bug **off of develop**.
19
19
  * Get coding!
20
20
  * Write good tests. See [Testing](#testing).
21
21
  * Follow the same coding conventions as the rest of the project. See [Coding Style](#coding-style).
data/Dockerfile ADDED
@@ -0,0 +1,8 @@
1
+ FROM ruby:2.5
2
+
3
+ ENV APP_HOME /app
4
+ RUN apt-get update -qq && apt-get install -y build-essential
5
+ RUN mkdir $APP_HOME
6
+ WORKDIR $APP_HOME
7
+ ADD . $APP_HOME/
8
+ RUN bundle install
data/Gemfile CHANGED
@@ -9,7 +9,8 @@ group :development, :test do
9
9
  gem "listen", "3.0.7"
10
10
  gem "guard"
11
11
  gem "guard-rspec", require: false
12
- gem "rubocop", require: false
13
- gem "rubocop-rspec", require: false
14
12
  gem "guard-rubocop", require: false
13
+ gem "rubocop", "~> 0.63.1", require: false
14
+ gem "rubocop-rspec", require: false
15
+
15
16
  end
data/README.md CHANGED
@@ -65,7 +65,8 @@ is allowed to access. Because BambooHR's API doesn't allow for specifying fields
65
65
  on the `/employees/directory` API endpoint, passing a list of fields to retrieve
66
66
  will be signifigantly slower than getting just the default fields since the gem
67
67
  will get the directory of employees, then request the data for each individual
68
- employee resulting in `employees.count + 1` API calls.
68
+ employee resulting in `employees.count + 1` API calls. To get around this,
69
+ consider using a custom report.
69
70
 
70
71
  ```ruby
71
72
  # Returns an array of all employees
@@ -121,6 +122,15 @@ client.time_off.whos_out(Time.now, "2014-12-31")
121
122
 
122
123
  ### Reports
123
124
 
125
+ ```ruby
126
+ # Get a list of employees with specified fields
127
+ # Send `:all` for `fields` to get all fields.
128
+ # Note that this can get a list of employees with additional fields via a single
129
+ # API request instead of one per employee when using the `employees` endpoint.
130
+ client.report.custom(fields, format = "JSON")
131
+ ```
132
+
133
+
124
134
  ```ruby
125
135
  # Find a report by its number
126
136
  client.report.find(report_number, format = "JSON", fd = true)
data/bamboozled.gemspec CHANGED
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path("lib", __dir__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require "bamboozled/version"
5
5
 
@@ -20,12 +20,11 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = ">= 2.0"
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "bundler", ">= 1.10"
24
24
  spec.add_development_dependency "rake", "~> 10.4"
25
25
  spec.add_development_dependency "rspec", "~> 3.1"
26
26
  spec.add_development_dependency "webmock", "~> 1.20"
27
27
 
28
28
  spec.add_dependency "httparty", "~> 0.13"
29
29
  spec.add_dependency "json", "~> 1.8"
30
- spec.add_dependency "activesupport"
31
30
  end
data/lib/bamboozled.rb CHANGED
@@ -7,16 +7,18 @@ require "bamboozled/base"
7
7
  require "bamboozled/errors"
8
8
  require "bamboozled/ext/yesno"
9
9
  require "bamboozled/api/base"
10
+ require "bamboozled/api/field_collection"
10
11
  require "bamboozled/api/employee"
11
12
  require "bamboozled/api/report"
12
13
  require "bamboozled/api/time_off"
14
+ require "bamboozled/api/time_tracking"
13
15
  require "bamboozled/api/meta"
14
16
 
15
17
  module Bamboozled
16
18
  class << self
17
19
  # Creates a standard client that will raise all errors it encounters
18
- def client(subdomain: nil, api_key: nil)
19
- Bamboozled::Base.new(subdomain: subdomain, api_key: api_key)
20
+ def client(subdomain: nil, api_key: nil, httparty_options: {})
21
+ Bamboozled::Base.new(subdomain: subdomain, api_key: api_key, httparty_options: httparty_options)
20
22
  end
21
23
  end
22
24
  end
@@ -1,81 +1,85 @@
1
1
  require 'json'
2
2
  require "time"
3
- require 'active_support/core_ext/hash/indifferent_access'
4
3
 
5
4
  module Bamboozled
6
5
  module API
7
6
  class Base
8
7
  attr_reader :subdomain, :api_key
9
8
 
10
- def initialize(subdomain, api_key)
11
- @subdomain, @api_key = subdomain, api_key
9
+ def initialize(subdomain, api_key, httparty_options = {})
10
+ @subdomain = subdomain
11
+ @api_key = api_key
12
+ @httparty_options = httparty_options || {}
12
13
  end
13
14
 
14
15
  protected
15
- def request(method, path, options = {})
16
- params = {
17
- path: path,
18
- options: options,
19
- method: method
20
- }
16
+ def request(method, path, options = {})
17
+ params = {
18
+ path: path,
19
+ options: options,
20
+ method: method
21
+ }
21
22
 
22
- httparty_options = {
23
- query: options[:query],
24
- body: options[:body],
25
- format: :plain,
26
- basic_auth: auth,
27
- headers: {
28
- "Accept" => "application/json",
29
- "User-Agent" => "Bamboozled/#{Bamboozled::VERSION}"
30
- }.update(options[:headers] || {})
31
- }
23
+ httparty_options = @httparty_options.merge({
24
+ query: options[:query],
25
+ body: options[:body],
26
+ format: :plain,
27
+ basic_auth: auth,
28
+ headers: {
29
+ "Accept" => "application/json",
30
+ "User-Agent" => "Bamboozled/#{Bamboozled::VERSION}"
31
+ }.update(options[:headers] || {})
32
+ })
32
33
 
33
- response = HTTParty.send(method, "#{path_prefix}#{path}", httparty_options)
34
- params[:response] = response.inspect.to_s
34
+ response = HTTParty.send(method, "#{path_prefix}#{path}", httparty_options)
35
+ params[:response] = response.inspect.to_s
35
36
 
36
- case response.code
37
- when 200..201
38
- begin
39
- if response.body.to_s.empty?
40
- {"headers" => response.headers}.with_indifferent_access
41
- else
42
- JSON.parse(response.body).with_indifferent_access
43
- end
44
- rescue
45
- MultiXml.parse(response, symbolize_keys: true)
37
+ case response.code
38
+ when 200..201
39
+ begin
40
+ if response.body.to_s.empty?
41
+ { "headers" => response.headers }
42
+ else
43
+ JSON.parse(response)
46
44
  end
47
- when 400
48
- raise Bamboozled::BadRequest.new(response, params, 'The request was invalid or could not be understood by the server. Resubmitting the request will likely result in the same error.')
49
- when 401
50
- raise Bamboozled::AuthenticationFailed.new(response, params, 'Your API key is missing.')
51
- when 403
52
- raise Bamboozled::Forbidden.new(response, params, 'The application is attempting to perform an action it does not have privileges to access. Verify your API key belongs to an enabled user with the required permissions.')
53
- when 404
54
- raise Bamboozled::NotFound.new(response, params, 'The resource was not found with the given identifier. Either the URL given is not a valid API, or the ID of the object specified in the request is invalid.')
55
- when 406
56
- raise Bamboozled::NotAcceptable.new(response, params, 'The request contains references to non-existent fields.')
57
- when 409
58
- raise Bamboozled::Conflict.new(response, params, 'The request attempts to create a duplicate. For employees, duplicate emails are not allowed. For lists, duplicate values are not allowed.')
59
- when 429
60
- raise Bamboozled::LimitExceeded.new(response, params, 'The account has reached its employee limit. No additional employees could be added.')
61
- when 500
62
- raise Bamboozled::InternalServerError.new(response, params, 'The server encountered an error while processing your request and failed.')
63
- when 502
64
- raise Bamboozled::GatewayError.new(response, params, 'The load balancer or web server had trouble connecting to the Bamboo app. Please try the request again.')
65
- when 503
66
- raise Bamboozled::ServiceUnavailable.new(response, params, 'The service is temporarily unavailable. Please try the request again.')
67
- else
68
- raise Bamboozled::InformBamboo.new(response, params, 'An error occurred that we do not now how to handle. Please contact BambooHR.')
45
+ rescue
46
+ typecast = options.fetch(:typecast_values, true)
47
+ MultiXml.parse(response,
48
+ symbolize_keys: true,
49
+ typecast_xml_value: typecast)
69
50
  end
51
+ when 400
52
+ raise Bamboozled::BadRequest.new(response, params, 'The request was invalid or could not be understood by the server. Resubmitting the request will likely result in the same error.')
53
+ when 401
54
+ raise Bamboozled::AuthenticationFailed.new(response, params, 'Your API key is missing.')
55
+ when 403
56
+ raise Bamboozled::Forbidden.new(response, params, 'The application is attempting to perform an action it does not have privileges to access. Verify your API key belongs to an enabled user with the required permissions.')
57
+ when 404
58
+ raise Bamboozled::NotFound.new(response, params, 'The resource was not found with the given identifier. Either the URL given is not a valid API, or the ID of the object specified in the request is invalid.')
59
+ when 406
60
+ raise Bamboozled::NotAcceptable.new(response, params, 'The request contains references to non-existent fields.')
61
+ when 409
62
+ raise Bamboozled::Conflict.new(response, params, 'The request attempts to create a duplicate. For employees, duplicate emails are not allowed. For lists, duplicate values are not allowed.')
63
+ when 429
64
+ raise Bamboozled::LimitExceeded.new(response, params, 'The account has reached its employee limit. No additional employees could be added.')
65
+ when 500
66
+ raise Bamboozled::InternalServerError.new(response, params, 'The server encountered an error while processing your request and failed.')
67
+ when 502
68
+ raise Bamboozled::GatewayError.new(response, params, 'The load balancer or web server had trouble connecting to the Bamboo app. Please try the request again.')
69
+ when 503
70
+ raise Bamboozled::ServiceUnavailable.new(response, params, 'The service is temporarily unavailable. Please try the request again.')
71
+ else
72
+ raise Bamboozled::InformBamboo.new(response, params, 'An error occurred that we do not now how to handle. Please contact BambooHR.')
70
73
  end
74
+ end
71
75
 
72
- def auth
73
- { username: api_key, password: "x" }
74
- end
76
+ def auth
77
+ { username: api_key, password: "x" }
78
+ end
75
79
 
76
- def path_prefix
77
- "https://api.bamboohr.com/api/gateway.php/#{subdomain}/v1/"
78
- end
80
+ def path_prefix
81
+ "https://api.bamboohr.com/api/gateway.php/#{subdomain}/v1/"
82
+ end
79
83
  end
80
84
  end
81
85
  end
@@ -17,8 +17,7 @@ module Bamboozled
17
17
  end
18
18
 
19
19
  def find(employee_id, fields = nil)
20
- fields = all_fields if fields == :all
21
- fields = fields.join(',') if fields.is_a?(Array)
20
+ fields = FieldCollection.wrap(fields).to_csv
22
21
 
23
22
  request(:get, "employees/#{employee_id}?fields=#{fields}")
24
23
  end
@@ -44,10 +43,6 @@ module Bamboozled
44
43
  request(:get, "employees/#{employee_id}/time_off/calculator?end=#{end_date}")
45
44
  end
46
45
 
47
- def all_fields
48
- %w(address1 address2 age bestEmail birthday city country dateOfBirth department division eeo employeeNumber employmentHistoryStatus ethnicity exempt firstName flsaCode fullName1 fullName2 fullName3 fullName4 fullName5 displayName gender hireDate homeEmail homePhone id jobTitle lastChanged lastName location maritalStatus middleName mobilePhone nickname payChangeReason payGroup payGroupId payRate payRateEffectiveDate payType ssn sin state stateCode status supervisor supervisorId supervisorEId terminationDate workEmail workPhone workPhonePlusExtension workPhoneExtension zipcode photoUploaded rehireDate standardHoursPerWeek bonusDate bonusAmount bonusReason bonusComment commissionDate commisionDate commissionAmount commissionComment).join(',')
49
- end
50
-
51
46
  def photo_binary(employee_id)
52
47
  request(:get, "employees/#{employee_id}/photo/small")
53
48
  end
@@ -0,0 +1,107 @@
1
+ module Bamboozled
2
+ module API
3
+ class FieldCollection
4
+ def self.wrap(fields)
5
+ fields = all_names if fields == :all
6
+ fields = fields.split(",") if fields.is_a?(String)
7
+ new(fields)
8
+ end
9
+
10
+ def self.all_names # rubocop:disable Metrics/MethodLength
11
+ %w[
12
+ address1
13
+ address2
14
+ age
15
+ bestEmail
16
+ birthday
17
+ bonusAmount
18
+ bonusComment
19
+ bonusDate
20
+ bonusReason
21
+ city
22
+ commisionDate
23
+ commissionAmount
24
+ commissionComment
25
+ commissionDate
26
+ country
27
+ dateOfBirth
28
+ department
29
+ displayName
30
+ division
31
+ eeo
32
+ employeeNumber
33
+ employmentHistoryStatus
34
+ ethnicity
35
+ exempt
36
+ firstName
37
+ flsaCode
38
+ fullName1
39
+ fullName2
40
+ fullName3
41
+ fullName4
42
+ fullName5
43
+ gender
44
+ hireDate
45
+ homeEmail
46
+ homePhone
47
+ id
48
+ includeInPayroll
49
+ isPhotoUploaded
50
+ jobTitle
51
+ lastChanged
52
+ lastName
53
+ location
54
+ maritalStatus
55
+ middleName
56
+ mobilePhone
57
+ originalHireDate
58
+ paidPer
59
+ payChangeReason
60
+ payFrequency
61
+ payGroup
62
+ payGroupId
63
+ payPer
64
+ payRate
65
+ payRateEffectiveDate
66
+ paySchedule
67
+ payScheduleId
68
+ payType
69
+ preferredName
70
+ sin
71
+ ssn
72
+ standardHoursPerWeek
73
+ state
74
+ stateCode
75
+ status
76
+ supervisor
77
+ supervisorEId
78
+ supervisorId
79
+ terminationDate
80
+ workEmail
81
+ workPhone
82
+ workPhoneExtension
83
+ workPhonePlusExtension
84
+ zipcode
85
+ ]
86
+ end
87
+
88
+ def initialize(fields)
89
+ self.fields = fields || []
90
+ end
91
+
92
+ def to_csv
93
+ fields.join(",")
94
+ end
95
+
96
+ def to_xml
97
+ "<fields>" +
98
+ fields.map { |field| "<field id=\"#{field}\" />" }.join +
99
+ "</fields>"
100
+ end
101
+
102
+ private
103
+
104
+ attr_accessor :fields
105
+ end
106
+ end
107
+ end