namely 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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!