bamboozled 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- data/.github/ISSUE_TEMPLATE/question.md +19 -0
- data/.github/main.workflow +38 -0
- data/.github/pull_request_template.md +27 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -2
- data/.rubocop_todo.yml +305 -0
- data/.travis.yml +7 -8
- data/CHANGELOG.md +22 -1
- data/CONTRIBUTING.md +2 -2
- data/Dockerfile +8 -0
- data/Gemfile +3 -2
- data/README.md +11 -1
- data/bamboozled.gemspec +2 -3
- data/lib/bamboozled.rb +4 -2
- data/lib/bamboozled/api/base.rb +63 -59
- data/lib/bamboozled/api/employee.rb +1 -6
- data/lib/bamboozled/api/field_collection.rb +107 -0
- data/lib/bamboozled/api/meta.rb +15 -5
- data/lib/bamboozled/api/report.rb +10 -6
- data/lib/bamboozled/api/time_off.rb +9 -6
- data/lib/bamboozled/api/time_tracking.rb +24 -0
- data/lib/bamboozled/base.rb +12 -6
- data/lib/bamboozled/version.rb +1 -1
- data/spec/fixtures/add_employee_xml.yml +1 -1
- data/spec/fixtures/custom_report.json +38 -0
- data/spec/fixtures/meta_fields.json +5 -0
- data/spec/fixtures/meta_lists.json +5 -0
- data/spec/fixtures/meta_tables.json +5 -0
- data/spec/fixtures/meta_users.json +4 -0
- data/spec/fixtures/time_tracking_add_200_response.json +7 -0
- data/spec/fixtures/time_tracking_add_empty_response.json +9 -0
- data/spec/fixtures/time_tracking_adjust_200_response.json +7 -0
- data/spec/fixtures/time_tracking_adjust_400_response.json +11 -0
- data/spec/fixtures/time_tracking_record_200_response.json +9 -0
- data/spec/fixtures/time_tracking_record_400_response.json +11 -0
- data/spec/fixtures/time_tracking_record_401_response.json +10 -0
- data/spec/fixtures/time_tracking_record_404_response.json +8 -0
- data/spec/fixtures/update_employee_xml.yml +1 -1
- data/spec/lib/bamboozled/api/base_spec.rb +18 -0
- data/spec/lib/bamboozled/api/employee_spec.rb +0 -14
- data/spec/lib/bamboozled/api/field_collection_spec.rb +17 -0
- data/spec/lib/bamboozled/api/meta_spec.rb +47 -0
- data/spec/lib/bamboozled/api/report_spec.rb +17 -0
- data/spec/lib/bamboozled/api/time_tracking_spec.rb +123 -0
- data/spec/lib/bamboozled/base_spec.rb +26 -0
- data/spec/lib/bamboozled_spec.rb +33 -0
- metadata +54 -21
- 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
|
-
##
|
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 `
|
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
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("
|
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", "
|
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
|
data/lib/bamboozled/api/base.rb
CHANGED
@@ -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
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def request(method, path, options = {})
|
17
|
+
params = {
|
18
|
+
path: path,
|
19
|
+
options: options,
|
20
|
+
method: method
|
21
|
+
}
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
34
|
-
|
34
|
+
response = HTTParty.send(method, "#{path_prefix}#{path}", httparty_options)
|
35
|
+
params[:response] = response.inspect.to_s
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
76
|
+
def auth
|
77
|
+
{ username: api_key, password: "x" }
|
78
|
+
end
|
75
79
|
|
76
|
-
|
77
|
-
|
78
|
-
|
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 =
|
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
|