namely 0.0.1 → 0.1.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -24
  3. data/lib/namely.rb +2 -101
  4. data/lib/namely/authenticator.rb +61 -14
  5. data/lib/namely/collection.rb +78 -0
  6. data/lib/namely/connection.rb +90 -0
  7. data/lib/namely/exceptions.rb +0 -3
  8. data/lib/namely/model.rb +78 -0
  9. data/lib/namely/resource_gateway.rb +7 -2
  10. data/lib/namely/version.rb +1 -1
  11. data/spec/fixtures/vcr_cassettes/{country_head.yml → countries_head.yml} +4 -4
  12. data/spec/fixtures/vcr_cassettes/{country_head_missing.yml → countries_head_missing.yml} +4 -4
  13. data/spec/fixtures/vcr_cassettes/{country_index.yml → countries_index.yml} +6 -6
  14. data/spec/fixtures/vcr_cassettes/{country_show.yml → countries_show.yml} +4 -4
  15. data/spec/fixtures/vcr_cassettes/{country_show_missing.yml → countries_show_missing.yml} +7 -7
  16. data/spec/fixtures/vcr_cassettes/{currencytype_index.yml → currency_types_index.yml} +5 -5
  17. data/spec/fixtures/vcr_cassettes/current_user.yml +57 -0
  18. data/spec/fixtures/vcr_cassettes/{event_head.yml → events_head.yml} +4 -4
  19. data/spec/fixtures/vcr_cassettes/{event_head_missing.yml → events_head_missing.yml} +5 -5
  20. data/spec/fixtures/vcr_cassettes/events_index.yml +89 -0
  21. data/spec/fixtures/vcr_cassettes/{event_show.yml → events_show.yml} +5 -5
  22. data/spec/fixtures/vcr_cassettes/{event_show_missing.yml → events_show_missing.yml} +6 -6
  23. data/spec/fixtures/vcr_cassettes/fields_index.yml +48 -0
  24. data/spec/fixtures/vcr_cassettes/{jobtier_index.yml → job_tiers_index.yml} +5 -5
  25. data/spec/fixtures/vcr_cassettes/{field_index.yml → profiles/fields_index.yml} +5 -5
  26. data/spec/fixtures/vcr_cassettes/profiles_create.yml +85 -0
  27. data/spec/fixtures/vcr_cassettes/{profile_create_failed.yml → profiles_create_failed.yml} +9 -10
  28. data/spec/fixtures/vcr_cassettes/{profile_head.yml → profiles_head.yml} +4 -4
  29. data/spec/fixtures/vcr_cassettes/{profile_head_missing.yml → profiles_head_missing.yml} +4 -4
  30. data/spec/fixtures/vcr_cassettes/profiles_index.yml +981 -0
  31. data/spec/fixtures/vcr_cassettes/{profile_show.yml → profiles_show.yml} +4 -4
  32. data/spec/fixtures/vcr_cassettes/{profile_show_missing.yml → profiles_show_missing.yml} +7 -7
  33. data/spec/fixtures/vcr_cassettes/profiles_show_updated.yml +91 -0
  34. data/spec/fixtures/vcr_cassettes/profiles_update.yml +95 -0
  35. data/spec/fixtures/vcr_cassettes/profiles_update_revert.yml +95 -0
  36. data/spec/fixtures/vcr_cassettes/{report_head.yml → reports_head.yml} +4 -4
  37. data/spec/fixtures/vcr_cassettes/{report_head_missing.yml → reports_head_missing.yml} +5 -5
  38. data/spec/fixtures/vcr_cassettes/reports_show.yml +186 -0
  39. data/spec/fixtures/vcr_cassettes/{report_show_missing.yml → reports_show_missing.yml} +7 -7
  40. data/spec/fixtures/vcr_cassettes/token.yml +6 -6
  41. data/spec/namely/authenticator_spec.rb +36 -0
  42. data/spec/namely/connection_spec.rb +15 -0
  43. data/spec/namely/integration_spec.rb +94 -0
  44. data/spec/namely/resource_gateway_spec.rb +18 -10
  45. data/spec/shared_examples/a_resource_with_a_create_action.rb +24 -0
  46. data/spec/shared_examples/a_resource_with_a_show_action.rb +38 -0
  47. data/spec/shared_examples/a_resource_with_an_index_action.rb +15 -0
  48. data/spec/shared_examples/{a_model_with_an_update_action.rb → a_resource_with_an_update_action.rb} +10 -9
  49. data/spec/spec_helper.rb +0 -17
  50. metadata +75 -88
  51. data/lib/namely/country.rb +0 -9
  52. data/lib/namely/currency_type.rb +0 -9
  53. data/lib/namely/event.rb +0 -9
  54. data/lib/namely/field.rb +0 -9
  55. data/lib/namely/job_tier.rb +0 -9
  56. data/lib/namely/profile.rb +0 -13
  57. data/lib/namely/report.rb +0 -9
  58. data/lib/namely/restful_model.rb +0 -150
  59. data/spec/fixtures/vcr_cassettes/event_index.yml +0 -88
  60. data/spec/fixtures/vcr_cassettes/profile_create.yml +0 -85
  61. data/spec/fixtures/vcr_cassettes/profile_index.yml +0 -979
  62. data/spec/fixtures/vcr_cassettes/profile_show_updated.yml +0 -91
  63. data/spec/fixtures/vcr_cassettes/profile_update.yml +0 -95
  64. data/spec/fixtures/vcr_cassettes/profile_update_revert.yml +0 -95
  65. data/spec/fixtures/vcr_cassettes/report_show.yml +0 -185
  66. data/spec/namely/configuration_spec.rb +0 -33
  67. data/spec/namely/country_spec.rb +0 -11
  68. data/spec/namely/currency_type_spec.rb +0 -5
  69. data/spec/namely/event_spec.rb +0 -11
  70. data/spec/namely/field_spec.rb +0 -5
  71. data/spec/namely/job_tier_spec.rb +0 -5
  72. data/spec/namely/profile_spec.rb +0 -25
  73. data/spec/namely/report_spec.rb +0 -8
  74. data/spec/shared_examples/a_model_with_a_create_action.rb +0 -24
  75. data/spec/shared_examples/a_model_with_a_show_action.rb +0 -38
  76. data/spec/shared_examples/a_model_with_an_index_action.rb +0 -17
@@ -25,9 +25,9 @@ http_interactions:
25
25
  Content-Type:
26
26
  - application/json; charset=utf-8
27
27
  Date:
28
- - Tue, 04 Nov 2014 16:34:49 GMT
28
+ - Tue, 11 Nov 2014 16:10:17 GMT
29
29
  Server:
30
- - nginx/1.6.2
30
+ - nginx
31
31
  Status:
32
32
  - 404 Not Found
33
33
  Strict-Transport-Security:
@@ -37,18 +37,18 @@ http_interactions:
37
37
  X-Rack-Cache:
38
38
  - miss
39
39
  X-Request-Id:
40
- - 52e1e643-65de-487a-8b28-2be8291d2f1b
40
+ - 59152f7c-363e-4b32-af5a-afa1b6f92fa2
41
41
  X-Runtime:
42
- - '0.025837'
42
+ - '0.043690'
43
43
  Content-Length:
44
- - '64'
44
+ - '62'
45
45
  Connection:
46
46
  - keep-alive
47
47
  body:
48
48
  encoding: ASCII-8BIT
49
49
  string: !binary |-
50
50
  H4sIAAAAAAAAA6tWyk0tLk5MT1WyUgpKLcgvKlHIyy9RSMsvzUtR0lFKLSrK
51
- LypWssorzcmpBQBUHrxBLAAAAA==
51
+ LypWsoqOrQUAFxI/qSoAAAA=
52
52
  http_version:
53
- recorded_at: Tue, 04 Nov 2014 16:34:49 GMT
53
+ recorded_at: Tue, 11 Nov 2014 16:10:17 GMT
54
54
  recorded_with: VCR 2.9.3
@@ -27,13 +27,13 @@ http_interactions:
27
27
  Content-Type:
28
28
  - application/json
29
29
  Date:
30
- - Tue, 04 Nov 2014 16:38:10 GMT
30
+ - Wed, 05 Nov 2014 19:34:30 GMT
31
31
  Etag:
32
- - '"fc3490b779796351d40312ba491c69c2"'
32
+ - '"d5351a93b070c18ad88f3eb2c60ec3cc"'
33
33
  Pragma:
34
34
  - no-cache
35
35
  Server:
36
- - nginx
36
+ - nginx/1.6.2
37
37
  Status:
38
38
  - 200 OK
39
39
  Strict-Transport-Security:
@@ -42,9 +42,9 @@ http_interactions:
42
42
  X-Rack-Cache:
43
43
  - invalidate, pass
44
44
  X-Request-Id:
45
- - 386d8523-f838-4023-a096-f74bc6c6291e
45
+ - 22bf7278-55c0-44bc-81b5-daac511edc48
46
46
  X-Runtime:
47
- - '0.059499'
47
+ - '0.065517'
48
48
  Content-Length:
49
49
  - '141'
50
50
  Connection:
@@ -53,5 +53,5 @@ http_interactions:
53
53
  encoding: UTF-8
54
54
  string: '{"access_token":"<%= access_token %>","refresh_token":"<%= refresh_token %>","token_type":"bearer","expires_in":899}'
55
55
  http_version:
56
- recorded_at: Tue, 04 Nov 2014 16:38:11 GMT
56
+ recorded_at: Wed, 05 Nov 2014 19:34:30 GMT
57
57
  recorded_with: VCR 2.9.3
@@ -79,6 +79,24 @@ describe Namely::Authenticator do
79
79
  expect(parsed_uri.scheme).to eq "http"
80
80
  expect(parsed_uri.host).to eq "testing.example.com"
81
81
  end
82
+
83
+ it "accepts a state parameter" do
84
+ authenticator = described_class.new(
85
+ client_id: "MY_CLIENT_ID",
86
+ client_secret: "MY_CLIENT_SECRET",
87
+ )
88
+
89
+ state = "this-is-a-piece-of-state"
90
+
91
+ authorization_code_url = authenticator.authorization_code_url(
92
+ subdomain: "ellingsonmineral",
93
+ state: state,
94
+ )
95
+
96
+ parsed_uri = URI(authorization_code_url)
97
+ parsed_query = CGI.parse(parsed_uri.query)
98
+ expect(parsed_query["state"]).to eq [state]
99
+ end
82
100
  end
83
101
 
84
102
  describe "#retrieve_tokens" do
@@ -140,4 +158,22 @@ describe Namely::Authenticator do
140
158
  end
141
159
  end
142
160
  end
161
+
162
+ describe "#current_user" do
163
+ it "returns the profile of the current user" do
164
+ VCR.use_cassette("current_user") do
165
+ authenticator = described_class.new(
166
+ client_id: "MY_CLIENT_ID",
167
+ client_secret: "MY_CLIENT_SECRET",
168
+ )
169
+
170
+ profile = authenticator.current_user(
171
+ access_token: ENV.fetch("TEST_ACCESS_TOKEN"),
172
+ subdomain: ENV.fetch("TEST_SUBDOMAIN"),
173
+ )
174
+
175
+ expect(profile.id).to eq "459748d5-608c-4dce-bca9-49a066d7f3d0"
176
+ end
177
+ end
178
+ end
143
179
  end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Namely::Connection do
4
+ describe "#initialize" do
5
+ context "when no provided an access token or subdomain" do
6
+ it "raises an ArgumentError" do
7
+ expect { Namely::Connection.new }.to raise_error ArgumentError
8
+ expect { Namely::Connection.new(access_token: "token") }.
9
+ to raise_error ArgumentError
10
+ expect { Namely::Connection.new(subdomain: "subdomain") }.
11
+ to raise_error ArgumentError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,94 @@
1
+ require "spec_helper"
2
+
3
+ describe "integration tests" do
4
+ describe "country collections" do
5
+ subject { conn.countries }
6
+
7
+ it_behaves_like(
8
+ "a resource with an index action",
9
+ [:id, :name, :subdivision_type]
10
+ )
11
+
12
+ it_behaves_like(
13
+ "a resource with a show action",
14
+ id: "US",
15
+ name: "United States",
16
+ subdivision_type: "State"
17
+ )
18
+ end
19
+
20
+ describe "currency type collections" do
21
+ subject { conn.currency_types }
22
+
23
+ it_behaves_like "a resource with an index action"
24
+ end
25
+
26
+ describe "event collections" do
27
+ subject { conn.events }
28
+
29
+ it_behaves_like "a resource with an index action", [:type]
30
+
31
+ it_behaves_like(
32
+ "a resource with a show action",
33
+ id: "e5573698-3934-4abf-99cf-577b526d4789",
34
+ type: "recent_arrival",
35
+ )
36
+ end
37
+
38
+ describe "field collections" do
39
+ subject { conn.fields }
40
+
41
+ it_behaves_like "a resource with an index action"
42
+ end
43
+
44
+ describe "job tier collections" do
45
+ subject { conn.job_tiers }
46
+
47
+ it_behaves_like "a resource with an index action", [:title]
48
+ end
49
+
50
+ describe "profile collections" do
51
+ subject { conn.profiles }
52
+
53
+ it_behaves_like(
54
+ "a resource with an index action",
55
+ [:first_name, :last_name]
56
+ )
57
+
58
+ it_behaves_like(
59
+ "a resource with a show action",
60
+ id: "20332458-c1fe-412f-bcb8-01622f04a35d",
61
+ first_name: "Leighton",
62
+ last_name: "Meester"
63
+ )
64
+
65
+ it_behaves_like(
66
+ "a resource with a create action",
67
+ first_name: "Beardsly",
68
+ last_name: "McDog",
69
+ email: "beardsly-#{Time.now.utc.to_f}@namely.com"
70
+ )
71
+
72
+ it_behaves_like(
73
+ "a resource with an update action",
74
+ "20332458-c1fe-412f-bcb8-01622f04a35d",
75
+ middle_name: "Beardsly"
76
+ )
77
+ end
78
+
79
+ describe "report collections" do
80
+ subject { conn.reports }
81
+
82
+ it_behaves_like(
83
+ "a resource with a show action",
84
+ id: "bbf089f6-90c5-473c-8928-058014a462c9"
85
+ )
86
+ end
87
+
88
+ def conn
89
+ Namely::Connection.new(
90
+ access_token: ENV.fetch("TEST_ACCESS_TOKEN"),
91
+ subdomain: ENV.fetch("TEST_SUBDOMAIN"),
92
+ )
93
+ end
94
+ end
@@ -1,12 +1,20 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Namely::ResourceGateway do
4
+ def access_token
5
+ ENV.fetch("TEST_ACCESS_TOKEN")
6
+ end
7
+
8
+ def subdomain
9
+ ENV.fetch("TEST_SUBDOMAIN")
10
+ end
11
+
4
12
  def gateway
5
13
  @gateway ||= Namely::ResourceGateway.new(
6
- access_token: Namely.configuration.access_token,
14
+ access_token: access_token,
7
15
  endpoint: "widgets",
8
16
  resource_name: "widgets",
9
- subdomain: Namely.configuration.subdomain
17
+ subdomain: subdomain,
10
18
  )
11
19
  end
12
20
 
@@ -22,10 +30,10 @@ describe Namely::ResourceGateway do
22
30
  it "returns the parsed JSON representation of #index" do
23
31
  stub_request(
24
32
  :get,
25
- "https://#{Namely.configuration.subdomain}.namely.com/api/v1/widgets"
33
+ "https://#{subdomain}.namely.com/api/v1/widgets"
26
34
  ).with(
27
35
  query: {
28
- access_token: Namely.configuration.access_token,
36
+ access_token: access_token,
29
37
  limit: :all
30
38
  }
31
39
  ).to_return(
@@ -41,10 +49,10 @@ describe Namely::ResourceGateway do
41
49
  it "returns the parsed JSON representation of #show" do
42
50
  stub_request(
43
51
  :get,
44
- "https://#{Namely.configuration.subdomain}.namely.com/api/v1/widgets/#{valid_id}"
52
+ "https://#{subdomain}.namely.com/api/v1/widgets/#{valid_id}"
45
53
  ).with(
46
54
  query: {
47
- access_token: Namely.configuration.access_token
55
+ access_token: access_token
48
56
  }
49
57
  ).to_return(
50
58
  body: "{\"widgets\": [{\"name\": \"wilbur\", \"favorite_color\": \"chartreuse\"}]}",
@@ -62,10 +70,10 @@ describe Namely::ResourceGateway do
62
70
  it "returns an empty response if it succeeds" do
63
71
  stub_request(
64
72
  :head,
65
- "https://#{Namely.configuration.subdomain}.namely.com/api/v1/widgets/#{valid_id}"
73
+ "https://#{subdomain}.namely.com/api/v1/widgets/#{valid_id}"
66
74
  ).with(
67
75
  query: {
68
- access_token: Namely.configuration.access_token
76
+ access_token: access_token
69
77
  }
70
78
  ).to_return(
71
79
  body: "",
@@ -78,10 +86,10 @@ describe Namely::ResourceGateway do
78
86
  it "raises a RestClient::ResourceNotFound error if it fails" do
79
87
  stub_request(
80
88
  :head,
81
- "https://#{Namely.configuration.subdomain}.namely.com/api/v1/widgets/#{invalid_id}"
89
+ "https://#{subdomain}.namely.com/api/v1/widgets/#{invalid_id}"
82
90
  ).with(
83
91
  query: {
84
- access_token: Namely.configuration.access_token
92
+ access_token: access_token
85
93
  }
86
94
  ).to_return(
87
95
  status: 404
@@ -0,0 +1,24 @@
1
+ shared_examples "a resource with a create action" do |model_attributes|
2
+ def invalid_attributes
3
+ {}
4
+ end
5
+
6
+ describe "#create" do
7
+ it "creates a model based on a hash of attributes" do
8
+ model = nil
9
+
10
+ VCR.use_cassette("#{subject.endpoint}_create") do
11
+ model = subject.create!(model_attributes)
12
+ end
13
+
14
+ expect(model).to respond_to :id
15
+ expect(model.id).not_to be_empty
16
+ end
17
+
18
+ it "raises a FailedRequestError when the create action fails" do
19
+ VCR.use_cassette("#{subject.endpoint}_create_failed") do
20
+ expect { subject.create!(invalid_attributes) }.to raise_error Namely::FailedRequestError
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ shared_examples "a resource with a show action" do |model_attributes|
2
+ def invalid_id
3
+ "this-is-almost-certainly-not-the-id-of-any-model"
4
+ end
5
+
6
+ describe "#find" do
7
+ it "finds a model by its unique id" do
8
+ VCR.use_cassette("#{subject.endpoint}_show") do
9
+ object = subject.find(model_attributes[:id])
10
+
11
+ expect(object).to be_a Namely::Model
12
+ model_attributes.each do |method, value|
13
+ expect(object.public_send(method)).to eq value
14
+ end
15
+ end
16
+ end
17
+
18
+ it "raises an error if that model can't be found" do
19
+ VCR.use_cassette("#{subject.endpoint}_show_missing") do
20
+ expect { subject.find(invalid_id) }.to raise_error Namely::NoSuchModelError
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#exists?" do
26
+ it "returns true if a model exists" do
27
+ VCR.use_cassette("#{subject.endpoint}_head") do
28
+ expect(subject.exists?(model_attributes[:id])).to eq true
29
+ end
30
+ end
31
+
32
+ it "returns false if a model doesn't exist" do
33
+ VCR.use_cassette("#{subject.endpoint}_head_missing") do
34
+ expect(subject.exists?(invalid_id)).to eq false
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ shared_examples "a resource with an index action" do |instance_methods = []|
2
+ it "returns every model" do
3
+ VCR.use_cassette("#{subject.endpoint}_index") do
4
+ objects = subject.all
5
+
6
+ expect(objects).not_to be_empty
7
+ objects.each do |object|
8
+ expect(object).to be_a Namely::Model
9
+ instance_methods.each do |instance_method|
10
+ expect(object).to respond_to instance_method
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,25 +1,26 @@
1
- shared_examples_for "a model with an update action" do |valid_id, changes|
1
+ shared_examples_for "a resource with an update action" do |valid_id, changes|
2
2
  describe "#update" do
3
3
  it "updates an existing object" do
4
4
  model = nil
5
- original_values = nil
6
5
 
7
- VCR.use_cassette("#{classname}_show") do
8
- model = described_class.find(valid_id)
6
+ VCR.use_cassette("#{subject.endpoint}_show") do
7
+ model = subject.find(valid_id)
9
8
  end
10
9
 
11
10
  changes.each do |attribute, value|
12
11
  expect(model[attribute]).not_to eq value
13
12
  end
14
13
 
15
- original_values = model.to_h.select { |attribute, _| changes.keys.include?(attribute) }
14
+ original_values = model.to_h.select do |attribute, _|
15
+ changes.keys.include?(attribute)
16
+ end
16
17
 
17
- VCR.use_cassette("#{classname}_update") do
18
+ VCR.use_cassette("#{subject.endpoint}_update") do
18
19
  model.update(changes)
19
20
  end
20
21
 
21
- VCR.use_cassette("#{classname}_show_updated") do
22
- model = described_class.find(valid_id)
22
+ VCR.use_cassette("#{subject.endpoint}_show_updated") do
23
+ model = subject.find(valid_id)
23
24
  end
24
25
  changes.each do |attribute, value|
25
26
  expect(model[attribute]).to eq value
@@ -30,7 +31,7 @@ shared_examples_for "a model with an update action" do |valid_id, changes|
30
31
  end
31
32
 
32
33
  def revert_changes(model, original_values)
33
- VCR.use_cassette("#{classname}_update_revert") do
34
+ VCR.use_cassette("#{subject.endpoint}_update_revert") do
34
35
  model.update(original_values)
35
36
  end
36
37
  end
@@ -30,20 +30,3 @@ VCR.configure do |config|
30
30
  end
31
31
  end
32
32
  end
33
-
34
- def classname
35
- described_class.name.split("::").last.downcase
36
- end
37
-
38
- def set_configuration!
39
- Namely.configure do |config|
40
- config.access_token = ENV.fetch("TEST_ACCESS_TOKEN")
41
- config.subdomain = ENV.fetch("TEST_SUBDOMAIN")
42
- end
43
- end
44
-
45
- def unset_configuration!
46
- Namely.configuration = nil
47
- end
48
-
49
- set_configuration!