bamboozled 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e8d67307421bf68000afd6ace774f340f25e2dd
4
- data.tar.gz: f077d6540b2b8022d3100d420e81aa5de118da63
3
+ metadata.gz: 635d6d1db5b169bb515d7f4d40a392fcc92e93db
4
+ data.tar.gz: ddaa342918519a1c1e084b6b887c75996ed3ae2a
5
5
  SHA512:
6
- metadata.gz: e906a09b6c8dc5c65b37d6e7fba76065f6c89ee3d73d6dc70138d958595c2f17705ffdf14aff9e9e63f78e11f0b890be4b0ca316f06fe6798ea9e52e8a0dd339
7
- data.tar.gz: a197f8ebb1f8ddba7a58ae48e0b6bf8caef92e57fc1af05f65629e77879a955532bc69660d28864faa8079467ef9605936917e36e2e91014b282a9f6cdd2c294
6
+ metadata.gz: c0f19e374b5c96fe7e0a52b3fbef85a26d53858aaa79aec1f76f924ee04e8459b66baa8e93101a327f6afa68ed6645302f5408b34895fee5fcf76fa2919e0996
7
+ data.tar.gz: 48691b174853d721d92ecf5a7e7d235c0a0f55639e4c42fa3ffbdf8d17003cd9be82b7f3d64884b45e8541788fe8bcc442747d824b7f9fdb07e94918b4348978
data/.gitignore CHANGED
@@ -10,3 +10,5 @@ doc
10
10
 
11
11
  # jeweler generated
12
12
  pkg
13
+
14
+ *.gem
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bamboozled (0.0.1)
4
+ bamboozled (0.0.2)
5
5
  httparty
6
6
  json
7
7
 
data/Readme.md ADDED
@@ -0,0 +1,73 @@
1
+ # Bamboozled
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/bamboozled.svg)](http://badge.fury.io/rb/bamboozled) [![Code Climate](https://codeclimate.com/github/Skookum/bamboozled.png)](https://codeclimate.com/github/Skookum/bamboozled)
4
+
5
+ Bamboozled wraps the BambooHR API without the use of Rails dependencies.
6
+
7
+ # Usage:
8
+
9
+ ```ruby
10
+ # Create the client:
11
+ client = Bamboozled.client('your_subdomain', 'your_api_key')
12
+ ```
13
+
14
+ ### Employee related data:
15
+
16
+ ```ruby
17
+ # Returns an array of all employees
18
+ client.employee.all
19
+
20
+ # Returns a hash of a single employee
21
+ client.employee.find(employee_id, fields = nil)
22
+
23
+ # Tabular employee data
24
+ client.employee.job_info(employee_id)
25
+ client.employee.employment_status(employee_id)
26
+ client.employee.compensation(employee_id)
27
+ client.employee.dependents(employee_id)
28
+ client.employee.contacts(employee_id)
29
+
30
+ # Time off estimate for employee. Requires end date in Date or Time format or YY-MM-DD string.
31
+ client.employee.time_off_estimate(employee_id, end_date)
32
+ ```
33
+
34
+ ### Time off data
35
+
36
+ ```ruby
37
+ # Get time off requests filtered by a number of parameters
38
+ # :id - the ID of the time off request
39
+ # :action -
40
+ # :employeeId - the ID of the employee you're looking for
41
+ # :start - filter start date
42
+ # :end - filter end date
43
+ # :type - type of time off request
44
+ # :status - must be one or more of the following: approved denied superceded requested canceled
45
+ client.time_off.requests(:employeeId: employee_id, start: Time.now)
46
+
47
+ # See who is out when.
48
+ client.time_off.whos_out(Time.now, '2014-12-31')
49
+ ```
50
+
51
+ # Reports
52
+
53
+ ```ruby
54
+ # Find a report by its number
55
+ client.report.find(report_number, format = 'JSON', fd = true)
56
+ ```
57
+
58
+ ## Todo:
59
+
60
+ 1. Write tests!
61
+ 2. Implement photos endpoints
62
+ 3. Implement metadata endpoints
63
+ 4. Implement last change information endpoints
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Make some specs pass
71
+ 5. Push to the branch (`git push origin my-new-feature`)
72
+ 6. Create new Pull Request
73
+
@@ -0,0 +1,75 @@
1
+ require 'json'
2
+
3
+ module Bamboozled
4
+ module API
5
+ class Base
6
+ attr_reader :subdomain, :api_key
7
+
8
+ def initialize(subdomain, api_key)
9
+ @subdomain, @api_key = subdomain, api_key
10
+ end
11
+
12
+ protected
13
+ def request(method, path, options = {})
14
+ params = {
15
+ path: path,
16
+ options: options,
17
+ method: method
18
+ }
19
+
20
+ httparty_options = {
21
+ query: options[:query],
22
+ body: options[:body],
23
+ format: :plain,
24
+ basic_auth: auth,
25
+ headers: {
26
+ "Accept" => "application/json",
27
+ "User-Agent" => "Bamboozled/#{Bamboozled::VERSION}"
28
+ }.update(options[:headers] || {})
29
+ }
30
+
31
+ response = HTTParty.send(method, "#{path_prefix}#{path}", httparty_options)
32
+ params[:response] = response.inspect.to_s
33
+
34
+ case response.code
35
+ when 200..201
36
+ begin
37
+ JSON.load(response, nil)
38
+ rescue
39
+ MultiXml.parse(response, symbolize_keys: true)
40
+ end
41
+ when 400
42
+ 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.')
43
+ when 401
44
+ raise Bamboozled::AuthenticationFailed.new(response, params, 'Your API key is missing.')
45
+ when 403
46
+ 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.')
47
+ when 404
48
+ 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.')
49
+ when 406
50
+ raise Bamboozled::NotAcceptable.new(response, params, 'The request contains references to non-existent fields.')
51
+ when 409
52
+ 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.')
53
+ when 429
54
+ raise Bamboozled::LimitExceeded.new(response, params, 'The account has reached its employee limit. No additional employees could be added.')
55
+ when 500
56
+ raise Bamboozled::InternalServerError.new(response, params, 'The server encountered an error while processing your request and failed.')
57
+ when 502
58
+ raise Bamboozled::GatewayError.new(response, params, 'The load balancer or web server had trouble connecting to the Bamboo app. Please try the request again.')
59
+ when 503
60
+ raise Bamboozled::ServiceUnavailable.new(response, params, 'The service is temporarily unavailable. Please try the request again.')
61
+ else
62
+ raise Bamboozled::InformBamboo.new(response, params, 'An error occurred that we do not now how to handle. Please contact BambooHR.')
63
+ end
64
+ end
65
+
66
+ def auth
67
+ { username: api_key, password: "x" }
68
+ end
69
+
70
+ def path_prefix
71
+ "https://api.bamboohr.com/api/gateway.php/#{subdomain}/v1/"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,41 @@
1
+ module Bamboozled
2
+ module API
3
+ class Employee < Base
4
+
5
+ def all(fields = nil)
6
+ fields = all_fields if fields.nil?
7
+ fields = fields.join(',') if fields.is_a?(Array)
8
+
9
+ response = request(:get, "employees/directory?fields=#{fields}")
10
+ response['employees']
11
+ end
12
+
13
+ def find(id, fields = nil)
14
+ fields = all_fields if fields.nil?
15
+ fields = fields.join(',') if fields.is_a?(Array)
16
+
17
+ request(:get, "employees/#{id}?fields=#{fields}")
18
+ end
19
+
20
+ # Tabular data
21
+ [:job_info, :employment_status, :compensation, :dependents, :contacts].each do |action|
22
+ define_method(action.to_s) do |argument_id|
23
+ request(:get, "employees/#{argument_id}/tables/#{action.to_s.gsub(/_(.)/) {|e| $1.upcase}}")
24
+ end
25
+ end
26
+
27
+ def time_off_estimate(id, end_date)
28
+ end_date = end_date.strftime("%F") unless end_date.is_a?(String)
29
+ request(:get, "employees/#{id}/time_off/calculator?end=#{end_date}")
30
+ end
31
+
32
+ def default_fields
33
+ %w(displayName firstName lastName jobTitle workPhone mobilePhone workEmail department location photoUploaded photoUrl canUploadPhoto).join(',')
34
+ end
35
+
36
+ def all_fields
37
+ %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(',')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ module Bamboozled
2
+ module API
3
+ class Report < Base
4
+
5
+ def find(number, format = 'JSON', fd = true)
6
+ request(:get, "reports/#{number}?format=#{format.upcase}&fd=#{fd.yesno}")
7
+ end
8
+
9
+ # TODO - Implement custom reports
10
+ # def custom(options, format = 'JSON')
11
+ # request(:post, "reports/custom?format=#{format.upcase}", data_should_go_here)
12
+ # end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module Bamboozled
2
+ module API
3
+ class TimeOff < Base
4
+
5
+ def requests(options = {})
6
+ allowed_parameters = [:id, :action, :employeeId, :start, :end, :type, :status]
7
+ options = options.keep_if { |k,_| allowed_parameters.include? k }
8
+
9
+ # Convert non string dates to strings.
10
+ [:start, :end].each do |action|
11
+ options[action] = options[action].strftime("%F") if options[action] && !options[action].is_a?(String)
12
+ end
13
+
14
+ # Make sure all statuses are allowed
15
+ if options[:status]
16
+ allowed_statuses = %w(approved denied superceded requested canceled)
17
+ options[:status] = Array(options[:status]).keep_if { |v| allowed_statuses.include? v }.join(",")
18
+ end
19
+
20
+ request(:get, "time_off/requests?#{URI.encode_www_form(options)}")
21
+ end
22
+
23
+ def whos_out(start_date, end_date = nil)
24
+ start_date = start_date.strftime("%F") unless start_date.is_a?(String)
25
+ end_date = end_date.strftime("%F") unless end_date.nil? || end_date.is_a?(String)
26
+ request(:get, "time_off/whos_out?start=#{start_date}&end=#{end_date}")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ module Bamboozled
2
+ class Base
3
+ attr_reader :request
4
+
5
+ def initialize(subdomain: nil, api_key: nil)
6
+ @subdomain, @api_key = subdomain, api_key
7
+ end
8
+
9
+ def employee
10
+ @employee ||= Bamboozled::API::Employee.new(@subdomain, @api_key)
11
+ end
12
+
13
+ def report
14
+ @report ||= Bamboozled::API::Report.new(@subdomain, @api_key)
15
+ end
16
+
17
+ def time_off
18
+ @time_off ||= Bamboozled::API::TimeOff.new(@subdomain, @api_key)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ module Bamboozled
2
+ class HTTPError < StandardError
3
+ attr_reader :response
4
+ attr_reader :params
5
+ attr_reader :hint
6
+
7
+ def initialize(response, params = {}, hint = nil)
8
+ @response = response
9
+ @params = params
10
+ @hint = hint
11
+ super(response)
12
+ end
13
+
14
+ def to_s
15
+ "#{self.class.to_s} : #{response.code} #{response.body}" + (hint ? "\n#{hint}" : "")
16
+ end
17
+ end
18
+
19
+ # 400
20
+ class BadRequest < HTTPError; end #400
21
+ class Unauthorized < HTTPError ; end #
22
+ class Forbidden < HTTPError ; end # 403
23
+ class NotFound < HTTPError; end # 404
24
+ class NotAcceptable < HTTPError; end # 406
25
+ class Conflict < HTTPError; end # 409
26
+ class LimitExceeded < HTTPError; end # 429
27
+ class InternalServerError < HTTPError; end # 500
28
+ class GatewayError < HTTPError; end # 502
29
+ class ServiceUnavailable < HTTPError; end # 503
30
+ class InformBamboo < HTTPError; end
31
+ end
File without changes
@@ -1,3 +1,3 @@
1
1
  module Bamboozled
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/bamboozled.rb CHANGED
@@ -2,9 +2,19 @@ require 'httparty'
2
2
  require 'json'
3
3
  require 'uri'
4
4
 
5
- Dir[File.dirname(__FILE__) + '/bamboozled/*.rb'].each do |file|
6
- require file
7
- end
5
+ require 'bamboozled/ext/yesno'
6
+
7
+ require 'bamboozled/version'
8
+ require 'bamboozled/errors'
9
+ require 'bamboozled/base'
10
+
11
+ %w(base employee report time_off).each {|a| require "bamboozled/api/#{a}"}
8
12
 
9
13
  module Bamboozled
14
+ class << self
15
+ # Creates a standard client that will raise all errors it encounters
16
+ def client(subdomain: nil, api_key: nil)
17
+ Bamboozled::Base.new(subdomain: subdomain, api_key: api_key)
18
+ end
19
+ end
10
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bamboozled
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rickert
@@ -48,11 +48,17 @@ files:
48
48
  - ".gitignore"
49
49
  - Gemfile
50
50
  - Gemfile.lock
51
+ - Readme.md
51
52
  - bamboozled.gemspec
52
53
  - lib/bamboozled.rb
53
- - lib/bamboozled/client.rb
54
+ - lib/bamboozled/api/base.rb
55
+ - lib/bamboozled/api/employee.rb
56
+ - lib/bamboozled/api/report.rb
57
+ - lib/bamboozled/api/time_off.rb
58
+ - lib/bamboozled/base.rb
59
+ - lib/bamboozled/errors.rb
60
+ - lib/bamboozled/ext/yesno.rb
54
61
  - lib/bamboozled/version.rb
55
- - lib/bamboozled/yesno.rb
56
62
  homepage: http://github.com/Skookum/bamboozled
57
63
  licenses:
58
64
  - MIT
@@ -1,132 +0,0 @@
1
- module Bamboozled
2
- class Client
3
- include HTTParty
4
- base_uri 'api.bamboohr.com'
5
-
6
- def initialize(subdomain, api_key)
7
- @subdomain, @api_key = subdomain, api_key
8
- end
9
-
10
- def all_employees(fields = nil)
11
- fields = all_fields if fields.nil?
12
- r = request("employees/directory?fields=#{fields}")
13
- r['employees']
14
- end
15
-
16
- def employee(id, fields = nil)
17
- fields = all_fields if fields.nil?
18
- request("employees/#{id}?fields=#{fields}")
19
- end
20
-
21
- # Tabular data
22
- [:job_info, :employment_status, :compensation, :dependents, :contacts].each do |action|
23
- define_method("employee_#{action}") do |argument_id|
24
- request("employees/#{argument_id}/tables/#{action.to_s.gsub(/_(.)/) {|e| $1.upcase}}")
25
- end
26
- end
27
-
28
- # Reports
29
- def report(number, format = 'JSON', fd = true)
30
- request("reports/#{number}?format=#{format.upcase}&fd=#{fd.yesno}")
31
- end
32
-
33
- # def custom_report(options, format = 'JSON')
34
- # post("reports/custom?format=#{format.upcase}", nil)
35
- # end
36
-
37
- # Time Off Requests
38
- def time_off_requests(options = {})
39
- allowed_parameters = [:id, :action, :employeeId, :start, :end, :type, :status]
40
- options = options.keep_if { |k,_| allowed_parameters.include? k }
41
-
42
- # Convert non string dates to strings.
43
- [:start, :end].each do |action|
44
- options[action] = options[action].strftime("%F") if options[action] && !options[action].is_a?(String)
45
- end
46
-
47
- # Make sure all statuses are allowed
48
- if options[:status]
49
- allowed_statuses = %w(approved denied superceded requested canceled)
50
- options[:status] = Array(options[:status]).keep_if { |v| allowed_statuses.include? v }.join(",")
51
- end
52
-
53
- request("time_off/requests?#{URI.encode_www_form(options)}")
54
- end
55
-
56
- def estimate_time_off(employee_id, end_date)
57
- end_date = end_date.strftime("%F") unless end_date.is_a?(String)
58
- request("employees/#{employee_id}/time_off/calculator?end=#{end_date}")
59
- end
60
-
61
- def whos_out(start_date, end_date = nil)
62
- start_date = start_date.strftime("%F") unless start_date.is_a?(String)
63
- end_date = end_date.strftime("%F") unless end_date.nil? || end_date.is_a?(String)
64
- request("time_off/whos_out?start=#{start_date}&end=#{end_date}")
65
- end
66
-
67
- def default_fields
68
- %w(displayName firstName lastName jobTitle workPhone mobilePhone workEmail department location photoUploaded photoUrl canUploadPhoto).join(',')
69
- end
70
-
71
- def all_fields
72
- %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(',')
73
- end
74
-
75
- private
76
-
77
- def request(path, fields = nil)
78
- fields = all_fields if fields.nil?
79
- self.class.get("#{path_prefix}#{path}", basic_auth: auth, headers: headers)
80
- end
81
-
82
- def post(path, data)
83
- self.class.post("#{path_prefix}#{path}", body: data, basic_auth: auth, headers: headers)
84
- end
85
-
86
- def headers
87
- { 'Accept' => 'application/json' }
88
- end
89
-
90
- def auth
91
- { username: @api_key, password: "x" }
92
- end
93
-
94
- def path_prefix
95
- "/api/gateway.php/#{@subdomain}/v1/"
96
- end
97
-
98
- # def self.get_all_stores
99
- # get("/v1/stores")
100
- # end
101
-
102
- # def self.get_stores_by_zip(zip)
103
- # get("/v1/stores(postalCode=#{zip})")
104
- # end
105
-
106
- # def self.get_stores_by_zip_and_distance(zip, distance)
107
- # get("/v1/stores(area(#{zip},#{distance}))")
108
- # end
109
-
110
- # def self.get_product_by_sku(sku)
111
- # get("/v1/products/#{sku}.xml")
112
- # end
113
-
114
- # def self.get_products(filter)
115
- # response = get(URI.escape("/v1/products(#{filter})"))
116
- # response["products"]["product"]
117
- # end
118
-
119
- # def self.method_missing(method_id, *args)
120
- # if match = /get_products_by_([_a-zA-Z]\w*)/.match(method_id.to_s)
121
- # attribute_names = match.captures.last.split('_and_')
122
-
123
- # request = ""
124
- # attribute_names.each_with_index { |name, idx| request = request + name + "=" + args[idx] + (attribute_names.length-1 == idx ? "" : "&") }
125
-
126
- # get_products(request)
127
- # else
128
- # super
129
- # end
130
- # end
131
- end
132
- end