recruitee 0.0.0
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 +7 -0
- data/.gitignore +25 -0
- data/.rubocop.yml +41 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +38 -0
- data/Rakefile +10 -0
- data/lib/recruitee.rb +25 -0
- data/lib/recruitee/api/admins.rb +22 -0
- data/lib/recruitee/api/candidates.rb +34 -0
- data/lib/recruitee/api/interview_events.rb +32 -0
- data/lib/recruitee/api/interview_results.rb +25 -0
- data/lib/recruitee/api/offers.rb +11 -0
- data/lib/recruitee/api/placements.rb +21 -0
- data/lib/recruitee/api_resource.rb +76 -0
- data/lib/recruitee/client.rb +56 -0
- data/lib/recruitee/http/client.rb +58 -0
- data/lib/recruitee/resource.rb +68 -0
- data/lib/recruitee/resources/admin.rb +6 -0
- data/lib/recruitee/resources/candidate.rb +24 -0
- data/lib/recruitee/resources/interview_event.rb +13 -0
- data/lib/recruitee/resources/interview_result.rb +13 -0
- data/lib/recruitee/resources/offer.rb +6 -0
- data/lib/recruitee/resources/placement.rb +13 -0
- data/lib/recruitee/response.rb +19 -0
- data/lib/recruitee/util.rb +14 -0
- data/lib/recruitee/version.rb +5 -0
- data/recruitee.gemspec +35 -0
- data/spec/recruitee/client_spec.rb +33 -0
- data/spec/recruitee/http/client_spec.rb +66 -0
- data/spec/recruitee_spec.rb +10 -0
- data/spec/spec_helper.rb +21 -0
- metadata +232 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2a25ca7a277b2e3b08a59fecc4a5b43b3acb05d3d3be001a63dfc1c7e00b57db
|
4
|
+
data.tar.gz: 71bac41e4d08b3e1988be66ebc8cf9458df13b35f9b2ec2d51931001a58a9ac7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dddef10f9eca96ba7c375f86c924867801a8c2ab4a4fc70266638356cf7c407558d2b1598f5da0f30f76e946d57a87c55c04cc18e17cdc1954bb68fb8baf38f3
|
7
|
+
data.tar.gz: 9c502a4e3775c54e69111c90002a8f24849c6963eeffcebb587fcaa1893894d0bd84e5a63d75c7c682b0b7b7dfb6fc813e913586d129a566f07abab867072e9a
|
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
/.config
|
3
|
+
/coverage/
|
4
|
+
/pkg/
|
5
|
+
/spec/reports/
|
6
|
+
/test/tmp/
|
7
|
+
/test/version_tmp/
|
8
|
+
/tmp/
|
9
|
+
/features/shared
|
10
|
+
.DS_Store
|
11
|
+
|
12
|
+
# Documentation
|
13
|
+
/.yardoc/
|
14
|
+
/_yardoc/
|
15
|
+
/doc/
|
16
|
+
/rdoc/
|
17
|
+
|
18
|
+
# Environment
|
19
|
+
/.bundle/
|
20
|
+
/vendor/bundle
|
21
|
+
/lib/bundler/man/
|
22
|
+
|
23
|
+
Gemfile.lock
|
24
|
+
.ruby-version
|
25
|
+
.ruby-gemset
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- bin/**/*
|
4
|
+
- tmp/**/*
|
5
|
+
- vendor/bundle/**/*
|
6
|
+
NewCops: enable
|
7
|
+
TargetRubyVersion: 2.4
|
8
|
+
|
9
|
+
require:
|
10
|
+
- rubocop-performance
|
11
|
+
- rubocop-rspec
|
12
|
+
|
13
|
+
Layout/ArgumentAlignment:
|
14
|
+
EnforcedStyle: "with_fixed_indentation"
|
15
|
+
|
16
|
+
Layout/BlockAlignment:
|
17
|
+
EnforcedStyleAlignWith: 'start_of_block'
|
18
|
+
|
19
|
+
Layout/FirstHashElementIndentation:
|
20
|
+
EnforcedStyle: 'consistent'
|
21
|
+
|
22
|
+
Layout/LineLength:
|
23
|
+
AutoCorrect: true
|
24
|
+
IgnoredPatterns: ['\A\s*#']
|
25
|
+
Max: 80
|
26
|
+
|
27
|
+
Layout/MultilineMethodCallIndentation:
|
28
|
+
EnforcedStyle: 'indented'
|
29
|
+
|
30
|
+
Metrics/BlockLength:
|
31
|
+
Exclude:
|
32
|
+
- 'spec/**/*'
|
33
|
+
|
34
|
+
Naming/MemoizedInstanceVariableName:
|
35
|
+
EnforcedStyleForLeadingUnderscores: 'required'
|
36
|
+
|
37
|
+
Style/Documentation:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/SymbolArray:
|
41
|
+
EnforcedStyle: 'brackets'
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020- Major League Hacking PBC Inc. (https://mlh.io)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# recruitee-ruby
|
2
|
+
|
3
|
+
An unofficial client for the [Recruitee](http://recruitee.com/)
|
4
|
+
[Private API](https://api.recruitee.com/docs/index.html).
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'recruitee'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself:
|
17
|
+
|
18
|
+
$ gem install recruitee
|
19
|
+
|
20
|
+
### Setup
|
21
|
+
|
22
|
+
Create an instance of the client with your company ID and API token. These can
|
23
|
+
be found under [Personal API tokens](https://app.recruitee.com/#/settings/api_tokens)
|
24
|
+
under the "Apps and plugins" section of your settings.
|
25
|
+
|
26
|
+
If you're using Rails, create an initializer `config/initializers/recruitee.rb`:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
$recruitee = Recruitee::Client.new("COMPANY_ID", "API_TOKEN")
|
30
|
+
```
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
1. Fork it
|
35
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
36
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
37
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
38
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/recruitee.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
BASE_DIR = "#{File.dirname(__FILE__)}/recruitee"
|
4
|
+
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
require 'forwardable'
|
8
|
+
|
9
|
+
# Version
|
10
|
+
require 'recruitee/version'
|
11
|
+
|
12
|
+
# API resource support classes
|
13
|
+
require 'recruitee/http/client'
|
14
|
+
require 'recruitee/client'
|
15
|
+
require 'recruitee/api_resource'
|
16
|
+
require 'recruitee/resource'
|
17
|
+
require 'recruitee/response'
|
18
|
+
require 'recruitee/util'
|
19
|
+
|
20
|
+
# Resources
|
21
|
+
Dir["#{BASE_DIR}/api/**/*.rb"].sort.each { |file| require file }
|
22
|
+
Dir["#{BASE_DIR}/resources/**/*.rb"].sort.each { |file| require file }
|
23
|
+
|
24
|
+
module Recruitee
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
module API
|
5
|
+
class Admins < APIResource
|
6
|
+
undef_method :find
|
7
|
+
undef_method :create
|
8
|
+
undef_method :update
|
9
|
+
undef_method :destroy
|
10
|
+
|
11
|
+
def initialize(client)
|
12
|
+
super(client, Admin)
|
13
|
+
end
|
14
|
+
|
15
|
+
def current
|
16
|
+
payload = request(:get, '/admin')
|
17
|
+
|
18
|
+
initialize_from(payload[resource_name])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
module API
|
5
|
+
class Candidates < APIResource
|
6
|
+
def initialize(client)
|
7
|
+
super(client, Candidate)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(offers: [], **candidate)
|
11
|
+
super({ offers: offers, candidate: candidate })
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_by_email(email)
|
15
|
+
hits = search_for_email(email)
|
16
|
+
# Recruitee's search is very fuzzy, so we need to find the real match
|
17
|
+
match = hits.find { |hit| hit['emails'].include?(email) }
|
18
|
+
|
19
|
+
match ? find(match['id']) : nil
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def search_for(email)
|
25
|
+
search = {
|
26
|
+
query: email, limit: 100, only: 'candidates', with_emails: true
|
27
|
+
}
|
28
|
+
res = request(:get, '/search/new/quick', params: search)
|
29
|
+
|
30
|
+
res.dig('candidates', 'hits')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
module API
|
5
|
+
class InterviewEvents < APIResource
|
6
|
+
def initialize(client)
|
7
|
+
super(client, InterviewEvent)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(candidate_id:, **interview_event)
|
11
|
+
payload = request(
|
12
|
+
:post,
|
13
|
+
"/interview/candidates/#{candidate_id}/events",
|
14
|
+
body: { interview_event: interview_event }
|
15
|
+
)
|
16
|
+
|
17
|
+
initialize_from(payload[resource_name])
|
18
|
+
end
|
19
|
+
|
20
|
+
def schedule(id, data = {})
|
21
|
+
body = namespaced_body(data)
|
22
|
+
payload = request(:post, "#{resource_url}/#{id}/schedule", body: body)
|
23
|
+
|
24
|
+
initialize_from(payload[resource_name])
|
25
|
+
end
|
26
|
+
|
27
|
+
def resource_url
|
28
|
+
'/interview/events'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
module API
|
5
|
+
class InterviewResults < APIResource
|
6
|
+
def initialize(client)
|
7
|
+
super(client, InterviewResult)
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(candidate_id:, **interview_result)
|
11
|
+
payload = request(
|
12
|
+
:post,
|
13
|
+
"/interview/candidates/#{candidate_id}/results",
|
14
|
+
body: { interview_result: interview_result }
|
15
|
+
)
|
16
|
+
|
17
|
+
initialize_from(payload[resource_name])
|
18
|
+
end
|
19
|
+
|
20
|
+
def resource_url
|
21
|
+
'/interview/results'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
module API
|
5
|
+
class Placements < APIResource
|
6
|
+
undef_method :find
|
7
|
+
undef_method :list
|
8
|
+
undef_method :update
|
9
|
+
|
10
|
+
def initialize(client)
|
11
|
+
super(client, Placement)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def namespaced_body(data)
|
17
|
+
data
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
class APIResource
|
5
|
+
attr_reader :client, :resource_klass
|
6
|
+
|
7
|
+
def initialize(client, resource_klass)
|
8
|
+
if client.nil? || resource_klass.nil?
|
9
|
+
raise ArgumentError, 'client & resource class must be provided'
|
10
|
+
end
|
11
|
+
|
12
|
+
@client = client
|
13
|
+
@resource_klass = resource_klass
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(data = {})
|
17
|
+
body = namespaced_body(data)
|
18
|
+
payload = request(:post, resource_url, body: body)
|
19
|
+
|
20
|
+
initialize_from(payload[resource_name])
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy(id, params = {})
|
24
|
+
payload = request(:delete, "#{resource_url}/#{id}", params: params)
|
25
|
+
|
26
|
+
initialize_from(payload[resource_name])
|
27
|
+
end
|
28
|
+
|
29
|
+
def find(id, params = {})
|
30
|
+
payload = request(:get, "#{resource_url}/#{id}", params: params)
|
31
|
+
|
32
|
+
initialize_from(payload[resource_name])
|
33
|
+
end
|
34
|
+
|
35
|
+
def list(params = {})
|
36
|
+
payload = request(:get, resource_url, params: params)
|
37
|
+
|
38
|
+
payload["#{resource_name}s"].map { |props| initialize_from(props) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def update(id, data = {})
|
42
|
+
body = namespaced_body(data)
|
43
|
+
payload = request(:patch, "#{resource_url}/#{id}", body: body)
|
44
|
+
|
45
|
+
initialize_from(payload[resource_name])
|
46
|
+
end
|
47
|
+
|
48
|
+
def resource_name
|
49
|
+
Util.underscore(resource_klass.name.split('::').last)
|
50
|
+
end
|
51
|
+
|
52
|
+
def resource_url
|
53
|
+
"/#{resource_name}s"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def initialize_from(properties)
|
59
|
+
resource_klass.new(client, properties)
|
60
|
+
end
|
61
|
+
|
62
|
+
def namespaced_body(data)
|
63
|
+
data[resource_name.to_sym] ? data : { "#{resource_name}": data }
|
64
|
+
end
|
65
|
+
|
66
|
+
def request(*args)
|
67
|
+
response = client.request(*args)
|
68
|
+
|
69
|
+
if response.status_code < 200 || response.status_code >= 400
|
70
|
+
raise response.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
response.body
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
# The Recruitee API client class that acts as a container for basic
|
5
|
+
# client authentication.
|
6
|
+
class Client
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_accessor :company_id, :api_token, :http_client
|
10
|
+
|
11
|
+
def_delegators :http_client, :request
|
12
|
+
|
13
|
+
def initialize(company_id, api_token, options = {})
|
14
|
+
@company_id = company_id
|
15
|
+
@api_token = api_token
|
16
|
+
@http_client = HTTP::Client.new(self, options)
|
17
|
+
|
18
|
+
validate!
|
19
|
+
end
|
20
|
+
|
21
|
+
def admins
|
22
|
+
@_admins ||= API::Admins.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def candidates
|
26
|
+
@_candidates ||= API::Candidates.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def interview_events
|
30
|
+
@_interview_events ||= API::InterviewEvents.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def interview_results
|
34
|
+
@_interview_results ||= API::InterviewResults.new(self)
|
35
|
+
end
|
36
|
+
alias evaluations interview_results
|
37
|
+
|
38
|
+
def offers
|
39
|
+
@_offers ||= API::Offers.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def placements
|
43
|
+
@_placements ||= API::Placements.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def validate!
|
49
|
+
[:company_id, :api_token].each do |name|
|
50
|
+
unless send(name)
|
51
|
+
raise ArgumentError, "Error! Missing required #{name}."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
module HTTP
|
5
|
+
class Client
|
6
|
+
attr_reader :recruitee, :options, :prefix
|
7
|
+
|
8
|
+
DEFAULT_DOMAIN = 'https://api.recruitee.com'
|
9
|
+
|
10
|
+
def initialize(recruitee, options = {})
|
11
|
+
raise ArgumentError if recruitee.nil?
|
12
|
+
|
13
|
+
@recruitee = recruitee
|
14
|
+
@prefix = options[:prefix] || "/c/#{@recruitee.company_id}"
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def request(method, path, params: nil, body: nil, headers: {})
|
19
|
+
res = connection.send(method.downcase.to_sym, prefixed(path)) do |req|
|
20
|
+
req.body = body unless body.nil?
|
21
|
+
req.params = params unless params.nil?
|
22
|
+
req.headers = default_headers.merge(headers)
|
23
|
+
end
|
24
|
+
|
25
|
+
Recruitee::Response.new(res.status, res.body, headers: res.headers)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def default_headers
|
31
|
+
{
|
32
|
+
'Accept-Charset': 'utf-8',
|
33
|
+
'Authorization': "Bearer #{@recruitee.api_token}",
|
34
|
+
'Content-Type': 'application/json',
|
35
|
+
'Accept': 'application/json'
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def domain
|
40
|
+
options[:domain] || DEFAULT_DOMAIN
|
41
|
+
end
|
42
|
+
|
43
|
+
def connection
|
44
|
+
@_connection ||= Faraday.new(url: domain, ssl: { verify: true }) do |f|
|
45
|
+
f.options.params_encoder = Faraday::FlatParamsEncoder
|
46
|
+
f.request :json
|
47
|
+
f.request :url_encoded
|
48
|
+
f.response :logger if options[:debug]
|
49
|
+
f.adapter Faraday.default_adapter
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def prefixed(path)
|
54
|
+
"#{prefix}#{path}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
class Resource
|
5
|
+
attr_reader :properties
|
6
|
+
|
7
|
+
def initialize(client, properties)
|
8
|
+
@client = client
|
9
|
+
@properties = deep_symbolize_keys(properties)
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
@properties[key.to_sym]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(key, value)
|
17
|
+
send(:"#{key}=", value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(name, *args)
|
21
|
+
if name.to_s.end_with?('=')
|
22
|
+
attr = name.to_s[0...-1].to_sym
|
23
|
+
@properties[attr] = args.first
|
24
|
+
elsif @properties.key?(name)
|
25
|
+
@properties[name]
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def respond_to_missing?(name, include_private = false)
|
32
|
+
attr = name.to_s.end_with?('=') ? name.to_s[0...-1].to_sym : name
|
33
|
+
@properties.key?(attr) || super
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
values = @properties.map { |k, v| "#{k}: #{v}" }.join(' ')
|
38
|
+
"<#{self.class.name} #{values}>"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :client
|
44
|
+
|
45
|
+
def deep_symbolize_keys(object)
|
46
|
+
deep_transform_keys(object) do |key|
|
47
|
+
begin
|
48
|
+
key.to_sym
|
49
|
+
rescue StandardError
|
50
|
+
key
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def deep_transform_keys(object, &block)
|
56
|
+
case object
|
57
|
+
when Hash
|
58
|
+
object.each_with_object({}) do |(key, value), result|
|
59
|
+
result[yield(key)] = deep_transform_keys(value, &block)
|
60
|
+
end
|
61
|
+
when Array
|
62
|
+
object.map { |e| deep_transform_keys(e, &block) }
|
63
|
+
else
|
64
|
+
object
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
class Candidate < Resource
|
5
|
+
def interview_events
|
6
|
+
@_interview_events ||= client.interview_events.list(candidate_id: id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def interview_results
|
10
|
+
@_interview_results ||= client.interview_results.list(candidate_id: id)
|
11
|
+
end
|
12
|
+
alias evaluations interview_results
|
13
|
+
|
14
|
+
def offers
|
15
|
+
@_offers ||= placements.map(&:offer)
|
16
|
+
end
|
17
|
+
|
18
|
+
def placements
|
19
|
+
@_placements ||= @properties[:placements].map do |attrs|
|
20
|
+
Placement.new(client, attrs)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Recruitee
|
4
|
+
class Response
|
5
|
+
attr_accessor :status_code, :body, :headers
|
6
|
+
|
7
|
+
def initialize(status_code, body, headers: nil)
|
8
|
+
body = '{}' if !body || body.empty?
|
9
|
+
|
10
|
+
@status_code = status_code
|
11
|
+
@headers = headers ? headers.to_hash : {}
|
12
|
+
@body = JSON.parse(body)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"[#{status_code}] #{body}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/recruitee.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'recruitee/version'
|
6
|
+
require 'english'
|
7
|
+
|
8
|
+
Gem::Specification.new do |gem|
|
9
|
+
gem.name = 'recruitee'
|
10
|
+
gem.version = Recruitee::VERSION
|
11
|
+
gem.required_ruby_version = '>= 2.4.0'
|
12
|
+
gem.authors = ['Mike Swift']
|
13
|
+
gem.email = ['swift@mlh.io']
|
14
|
+
gem.description = 'Unofficial Recruitee API SDK for Ruby.'
|
15
|
+
gem.summary = gem.description
|
16
|
+
gem.homepage = 'https://github.com/mlh/recruitee-rb'
|
17
|
+
gem.license = 'MIT'
|
18
|
+
|
19
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ['lib']
|
22
|
+
|
23
|
+
gem.add_dependency 'faraday', '~> 1.1.0'
|
24
|
+
gem.add_dependency 'faraday_middleware', '~> 1.0.0'
|
25
|
+
|
26
|
+
gem.add_development_dependency 'faker', '~> 2.14.0'
|
27
|
+
gem.add_development_dependency 'pry', '~> 0.13.1'
|
28
|
+
gem.add_development_dependency 'rake', '~> 13.0.1'
|
29
|
+
gem.add_development_dependency 'rspec', '~> 3.10.0'
|
30
|
+
gem.add_development_dependency 'rubocop', '~> 1.3.0'
|
31
|
+
gem.add_development_dependency 'rubocop-performance', '~> 1.8.1'
|
32
|
+
gem.add_development_dependency 'rubocop-rspec', '~> 2.0.0'
|
33
|
+
gem.add_development_dependency 'simplecov', '~> 0.19.1'
|
34
|
+
gem.add_development_dependency 'webmock', '~> 3.10.0'
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Recruitee::Client do
|
6
|
+
let(:company_id) { Faker::Lorem.characters(number: 10) }
|
7
|
+
let(:api_token) { Faker::Lorem.characters(number: 40) }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
it 'instantiates a valid Recruitee Client' do
|
11
|
+
expect { described_class.new(company_id, api_token) }
|
12
|
+
.not_to raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'requires a valid company id' do
|
16
|
+
expect { described_class.new(nil, api_token) }
|
17
|
+
.to raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'requires a valid api key' do
|
21
|
+
expect { described_class.new(company_id, nil) }
|
22
|
+
.to raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#candidates' do
|
27
|
+
subject(:client) { described_class.new(company_id, api_token) }
|
28
|
+
|
29
|
+
it 'returns a candidate object' do
|
30
|
+
expect(client.candidates).to be_kind_of(Recruitee::APIResource)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Recruitee::HTTP::Client do
|
6
|
+
subject(:client) { described_class.new(recruitee) }
|
7
|
+
|
8
|
+
let(:company_id) { Faker::Lorem.characters(number: 10) }
|
9
|
+
let(:api_token) { Faker::Lorem.characters(number: 40) }
|
10
|
+
let(:recruitee) { Recruitee::Client.new(company_id, api_token) }
|
11
|
+
|
12
|
+
describe '#request' do
|
13
|
+
it 'prefixes the path with the company id' do
|
14
|
+
stub_request(:get, api_url('/example'))
|
15
|
+
.to_return(status: 200, body: '', headers: {})
|
16
|
+
|
17
|
+
client.request(:get, '/example')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sets the authorization header from the recruitee client' do
|
21
|
+
stub_request(:get, api_url('/example'))
|
22
|
+
.with(headers: { 'Authorization': /#{Regexp.quote(api_token)}/ })
|
23
|
+
.to_return(status: 200, body: '', headers: {})
|
24
|
+
|
25
|
+
client.request(:get, '/example')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "does not send a query string when one isn't provided" do
|
29
|
+
stub_request(:get, api_url('/example'))
|
30
|
+
.with(query: nil)
|
31
|
+
.to_return(status: 200, body: '', headers: {})
|
32
|
+
|
33
|
+
client.request(:get, '/example')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'sends a query string when params are provided' do
|
37
|
+
stub_request(:get, api_url('/example'))
|
38
|
+
.with(query: { foo: 'bar' })
|
39
|
+
.to_return(status: 200, body: '', headers: {})
|
40
|
+
|
41
|
+
client.request(:get, '/example', params: { foo: 'bar' })
|
42
|
+
end
|
43
|
+
|
44
|
+
it "does not send a request body when one isn't provided" do
|
45
|
+
stub_request(:post, api_url('/example'))
|
46
|
+
.with(body: '')
|
47
|
+
.to_return(status: 200, body: '', headers: {})
|
48
|
+
|
49
|
+
client.request(:post, '/example')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'sends the request body as JSON when one is provided' do
|
53
|
+
stub_request(:post, api_url('/example'))
|
54
|
+
.with(body: { foo: 'bar' }.to_json)
|
55
|
+
.to_return(status: 200, body: '', headers: {})
|
56
|
+
|
57
|
+
client.request(:post, '/example', body: { foo: 'bar' })
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def api_url(path)
|
64
|
+
"https://api.recruitee.com/c/#{company_id}#{path}"
|
65
|
+
end
|
66
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'faker'
|
7
|
+
require 'pry'
|
8
|
+
require 'rspec'
|
9
|
+
require 'rubocop'
|
10
|
+
require 'webmock/rspec'
|
11
|
+
|
12
|
+
require 'recruitee'
|
13
|
+
|
14
|
+
begin
|
15
|
+
require 'simplecov'
|
16
|
+
SimpleCov.start
|
17
|
+
rescue LoadError
|
18
|
+
warn 'warning: simplecov gem not found; skipping coverage'
|
19
|
+
end
|
20
|
+
|
21
|
+
WebMock.disable_net_connect!
|
metadata
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: recruitee
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Swift
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday_middleware
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faker
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.14.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.14.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.13.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.13.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 13.0.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 13.0.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.10.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.10.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.3.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.3.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-performance
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.8.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.8.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.0.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 2.0.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.19.1
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.19.1
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: webmock
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 3.10.0
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 3.10.0
|
167
|
+
description: Unofficial Recruitee API SDK for Ruby.
|
168
|
+
email:
|
169
|
+
- swift@mlh.io
|
170
|
+
executables: []
|
171
|
+
extensions: []
|
172
|
+
extra_rdoc_files: []
|
173
|
+
files:
|
174
|
+
- ".gitignore"
|
175
|
+
- ".rubocop.yml"
|
176
|
+
- Gemfile
|
177
|
+
- LICENSE
|
178
|
+
- README.md
|
179
|
+
- Rakefile
|
180
|
+
- lib/recruitee.rb
|
181
|
+
- lib/recruitee/api/admins.rb
|
182
|
+
- lib/recruitee/api/candidates.rb
|
183
|
+
- lib/recruitee/api/interview_events.rb
|
184
|
+
- lib/recruitee/api/interview_results.rb
|
185
|
+
- lib/recruitee/api/offers.rb
|
186
|
+
- lib/recruitee/api/placements.rb
|
187
|
+
- lib/recruitee/api_resource.rb
|
188
|
+
- lib/recruitee/client.rb
|
189
|
+
- lib/recruitee/http/client.rb
|
190
|
+
- lib/recruitee/resource.rb
|
191
|
+
- lib/recruitee/resources/admin.rb
|
192
|
+
- lib/recruitee/resources/candidate.rb
|
193
|
+
- lib/recruitee/resources/interview_event.rb
|
194
|
+
- lib/recruitee/resources/interview_result.rb
|
195
|
+
- lib/recruitee/resources/offer.rb
|
196
|
+
- lib/recruitee/resources/placement.rb
|
197
|
+
- lib/recruitee/response.rb
|
198
|
+
- lib/recruitee/util.rb
|
199
|
+
- lib/recruitee/version.rb
|
200
|
+
- recruitee.gemspec
|
201
|
+
- spec/recruitee/client_spec.rb
|
202
|
+
- spec/recruitee/http/client_spec.rb
|
203
|
+
- spec/recruitee_spec.rb
|
204
|
+
- spec/spec_helper.rb
|
205
|
+
homepage: https://github.com/mlh/recruitee-rb
|
206
|
+
licenses:
|
207
|
+
- MIT
|
208
|
+
metadata: {}
|
209
|
+
post_install_message:
|
210
|
+
rdoc_options: []
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: 2.4.0
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
requirements: []
|
224
|
+
rubygems_version: 3.0.3
|
225
|
+
signing_key:
|
226
|
+
specification_version: 4
|
227
|
+
summary: Unofficial Recruitee API SDK for Ruby.
|
228
|
+
test_files:
|
229
|
+
- spec/recruitee/client_spec.rb
|
230
|
+
- spec/recruitee/http/client_spec.rb
|
231
|
+
- spec/recruitee_spec.rb
|
232
|
+
- spec/spec_helper.rb
|