noyo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +57 -0
  3. data/.codecov.yml +13 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +73 -0
  7. data/.ruby-version +1 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +4 -0
  10. data/Gemfile.lock +89 -0
  11. data/README.md +176 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +7 -0
  14. data/bin/setup +8 -0
  15. data/lib/noyo.rb +3 -0
  16. data/lib/noyo_accounts.rb +4 -0
  17. data/lib/noyo_accounts/client.rb +6 -0
  18. data/lib/noyo_accounts/client/services.rb +8 -0
  19. data/lib/noyo_accounts/client/services/auth_service.rb +45 -0
  20. data/lib/noyo_api.rb +6 -0
  21. data/lib/noyo_api/api.rb +13 -0
  22. data/lib/noyo_api/client.rb +9 -0
  23. data/lib/noyo_api/client/services.rb +8 -0
  24. data/lib/noyo_api/client/services/base_service.rb +37 -0
  25. data/lib/noyo_api/client/user_agent.rb +15 -0
  26. data/lib/noyo_api/client/version.rb +5 -0
  27. data/lib/noyo_api/configuration.rb +28 -0
  28. data/lib/noyo_fulfillment.rb +5 -0
  29. data/lib/noyo_fulfillment/api_response.rb +32 -0
  30. data/lib/noyo_fulfillment/models.rb +25 -0
  31. data/lib/noyo_fulfillment/models/api_resource.rb +191 -0
  32. data/lib/noyo_fulfillment/models/api_resource_collection.rb +41 -0
  33. data/lib/noyo_fulfillment/models/base_model.rb +115 -0
  34. data/lib/noyo_fulfillment/models/contact.rb +19 -0
  35. data/lib/noyo_fulfillment/models/demographic_change.rb +8 -0
  36. data/lib/noyo_fulfillment/models/dependent.rb +15 -0
  37. data/lib/noyo_fulfillment/models/employee.rb +108 -0
  38. data/lib/noyo_fulfillment/models/group.rb +25 -0
  39. data/lib/noyo_fulfillment/models/group_enrollment.rb +20 -0
  40. data/lib/noyo_fulfillment/models/group_plans.rb +15 -0
  41. data/lib/noyo_fulfillment/models/group_plans/add.rb +11 -0
  42. data/lib/noyo_fulfillment/models/group_plans/base.rb +22 -0
  43. data/lib/noyo_fulfillment/models/group_plans/dental.rb +13 -0
  44. data/lib/noyo_fulfillment/models/group_plans/life.rb +11 -0
  45. data/lib/noyo_fulfillment/models/group_plans/long_term_disability.rb +11 -0
  46. data/lib/noyo_fulfillment/models/group_plans/medical.rb +12 -0
  47. data/lib/noyo_fulfillment/models/group_plans/short_term_disability.rb +11 -0
  48. data/lib/noyo_fulfillment/models/group_plans/vision.rb +11 -0
  49. data/lib/noyo_fulfillment/models/individual_enrollment.rb +32 -0
  50. data/lib/noyo_fulfillment/models/location.rb +16 -0
  51. data/lib/noyo_fulfillment/models/member_request.rb +40 -0
  52. data/lib/noyo_fulfillment/models/member_transaction.rb +27 -0
  53. data/lib/noyo_fulfillment/models/mixins.rb +9 -0
  54. data/lib/noyo_fulfillment/models/mixins/has_person.rb +29 -0
  55. data/lib/noyo_fulfillment/models/mixins/nested_under.rb +64 -0
  56. data/lib/noyo_fulfillment/models/new_hire.rb +7 -0
  57. data/lib/noyo_fulfillment/models/open_enrollment.rb +7 -0
  58. data/lib/noyo_fulfillment/models/person.rb +18 -0
  59. data/lib/noyo_fulfillment/models/qualifying_life_event.rb +8 -0
  60. data/lib/noyo_fulfillment/models/termination.rb +9 -0
  61. data/noyo.gemspec +47 -0
  62. metadata +232 -0
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'noyo'
5
+
6
+ require 'irb'
7
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ require 'noyo_api'
2
+ require 'noyo_accounts'
3
+ require 'noyo_fulfillment'
@@ -0,0 +1,4 @@
1
+ require 'noyo_accounts/client'
2
+
3
+ module NoyoAccounts
4
+ end
@@ -0,0 +1,6 @@
1
+ require 'noyo_accounts/client/services'
2
+
3
+ module NoyoAccounts
4
+ module Client
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ require 'noyo_accounts/client/services/auth_service'
2
+
3
+ module NoyoAccounts
4
+ module Client
5
+ module Services
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,45 @@
1
+ require 'httparty'
2
+
3
+ module NoyoAccounts
4
+ module Client
5
+ module Services
6
+ class AuthService < NoyoApi::Client::Services::BaseService
7
+ include HTTParty
8
+ include NoyoApi::Client::UserAgent
9
+ base_uri NoyoApi::Api.config.accounts_base_uri
10
+
11
+ def initialize
12
+ # Get the latest config value, in case it changed
13
+ self.class.base_uri(NoyoApi::Api.config.accounts_base_uri)
14
+ end
15
+
16
+ def self.authorize_path
17
+ '/auth/public/token'
18
+ end
19
+
20
+ def authorize(client_id, client_secret)
21
+ auth = {
22
+ username: client_id,
23
+ password: client_secret,
24
+ }
25
+ body = {
26
+ grant_type: 'client_credentials',
27
+ }
28
+ options = {
29
+ body: body.to_json,
30
+ basic_auth: auth,
31
+ headers: {
32
+ 'Content-Type' => 'application/json',
33
+ 'Accept' => 'application/json',
34
+ 'User-Agent' => self.class.user_agent,
35
+ },
36
+ }
37
+ result = self.class.post(self.class.authorize_path, options)
38
+ raise 'Bad credentials.' if result.code == 401
39
+
40
+ parse_httparty_response(result)&.[]('access_token')
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ require 'noyo_api/api'
2
+ require 'noyo_api/client'
3
+ require 'noyo_api/configuration'
4
+
5
+ module NoyoApi
6
+ end
@@ -0,0 +1,13 @@
1
+ module NoyoApi
2
+ class Api
3
+ def self.configure
4
+ @@config ||= Configuration.new
5
+ yield(@@config) if block_given?
6
+ @@config
7
+ end
8
+
9
+ def self.config
10
+ @@config ||= Configuration.new
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'noyo_api/client/user_agent'
2
+ require 'noyo_api/client/services'
3
+ require 'noyo_api/client/version'
4
+
5
+ module NoyoApi
6
+ module Client
7
+
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require 'noyo_api/client/services/base_service'
2
+
3
+ module NoyoApi
4
+ module Client
5
+ module Services
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module NoyoApi
2
+ module Client
3
+ module Services
4
+ class BaseService
5
+ def self.root_path
6
+ '/api/v1'
7
+ end
8
+
9
+ def self.resource
10
+ class_name = name.split('::').last
11
+ class_name.gsub(/(.*)(Service)/, '\\1').downcase
12
+ end
13
+
14
+ def self.resource_path
15
+ File.join(root_path, resource)
16
+ end
17
+
18
+ protected
19
+
20
+ def headers(*_args)
21
+ {
22
+ 'Content-Type' => 'application/json',
23
+ 'Accept' => 'application/json',
24
+ }
25
+ end
26
+
27
+ def parse_httparty_response(httparty_response)
28
+ response = httparty_response.parsed_response
29
+ if response.is_a? String
30
+ response = JSON.parse(response).with_indifferent_access
31
+ end
32
+ response
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ module NoyoApi
2
+ module Client
3
+ module UserAgent
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def user_agent
10
+ "Noyo RubyBindings/#{NoyoApi::Client::VERSION}"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module NoyoApi
2
+ module Client
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ module NoyoApi
2
+ class Configuration
3
+ CONFIG_SETTERS = [
4
+ :client_id, :client_secret, :access_token, :accounts_base_uri, :fulfillment_base_uri,
5
+ :verbose,
6
+ ].freeze
7
+ REQUIRED_SETTERS = [ :client_id, :client_secret ].freeze
8
+ attr_accessor(*CONFIG_SETTERS)
9
+
10
+ def accounts_base_uri
11
+ @accounts_base_uri || 'https://accounts.development.noyoconnect.com'
12
+ end
13
+
14
+ def fulfillment_base_uri
15
+ @fulfillment_base_uri || 'https://fulfillment.development.noyoconnect.com'
16
+ end
17
+
18
+ def unconfigured?
19
+ unconfigured_setters = REQUIRED_SETTERS.map do |setter|
20
+ value = send(setter)
21
+ value.nil? ||
22
+ (value != true && value != false && (!value.respond_to?(:empty?) || value.empty?))
23
+ end
24
+
25
+ unconfigured_setters.any?
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ require 'noyo_fulfillment/api_response'
2
+ require 'noyo_fulfillment/models'
3
+
4
+ module NoyoFulfillment
5
+ end
@@ -0,0 +1,32 @@
1
+ module NoyoFulfillment
2
+ class ServerError < StandardError
3
+ def initialize(msg = 'There seems to be a problem server side. Please contact ' \
4
+ "Noyo at eng-admin@gonoyo.com for a fix for the error at #{Time.now}.")
5
+ super(msg)
6
+ end
7
+ end
8
+
9
+ class ApiResponse
10
+ attr_accessor :meta, :response
11
+
12
+ def initialize(response)
13
+ body = response.parsed_response
14
+ if body.is_a? String
15
+ parsed_body = JSON.parse(body)
16
+ if parsed_body.is_a?(Hash)
17
+ body = parsed_body.with_indifferent_access
18
+ elsif parsed_body.is_a?(Array)
19
+ body = parsed_body.map(&:with_indifferent_access)
20
+ end
21
+ end
22
+ raise ServerError unless body.is_a?(Hash) || body.is_a?(Array)
23
+
24
+ if body.is_a?(Hash) && body.key?('meta')
25
+ @meta = body['meta']
26
+ @response = body['response']
27
+ else
28
+ @response = body
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ require 'noyo_fulfillment/models/mixins'
2
+ require 'noyo_fulfillment/models/base_model'
3
+ require 'noyo_fulfillment/models/api_resource'
4
+ require 'noyo_fulfillment/models/api_resource_collection'
5
+ require 'noyo_fulfillment/models/person'
6
+ require 'noyo_fulfillment/models/employee'
7
+ require 'noyo_fulfillment/models/dependent'
8
+ require 'noyo_fulfillment/models/group'
9
+ require 'noyo_fulfillment/models/contact'
10
+ require 'noyo_fulfillment/models/location'
11
+ require 'noyo_fulfillment/models/group_enrollment'
12
+ require 'noyo_fulfillment/models/group_plans'
13
+ require 'noyo_fulfillment/models/new_hire'
14
+ require 'noyo_fulfillment/models/termination'
15
+ require 'noyo_fulfillment/models/demographic_change'
16
+ require 'noyo_fulfillment/models/open_enrollment'
17
+ require 'noyo_fulfillment/models/qualifying_life_event'
18
+ require 'noyo_fulfillment/models/member_request'
19
+ require 'noyo_fulfillment/models/member_transaction'
20
+ require 'noyo_fulfillment/models/individual_enrollment'
21
+
22
+ module NoyoFulfillment
23
+ module Models
24
+ end
25
+ end
@@ -0,0 +1,191 @@
1
+ require 'httparty'
2
+ # Used for underscore, pluralize
3
+ require 'active_support/inflector'
4
+
5
+ module NoyoFulfillment
6
+ class ApiResource < NoyoFulfillment::BaseModel
7
+ include HTTParty
8
+ include NoyoApi::Client::UserAgent
9
+ base_uri NoyoApi::Api.config.fulfillment_base_uri
10
+
11
+ @@PAGE_SIZE = 20
12
+ @@MAX_AUTH_ATTEMPTS = 5
13
+
14
+ def initialize(params={})
15
+ super(params)
16
+ # Get the latest config value, in case it changed
17
+ self.class.base_uri(NoyoApi::Api.config.fulfillment_base_uri)
18
+ end
19
+
20
+ def self.resource_path_name
21
+ class_name.underscore.pluralize
22
+ end
23
+
24
+ def self.resource_url(**_)
25
+ if self == ApiResource
26
+ message = 'ApiResource is an abstract class. You should perform actions on its subclasses.'
27
+ raise NotImplementedError, message
28
+ end
29
+ "/api/v1/#{resource_path_name}"
30
+ end
31
+
32
+ def self.single_resource_url(id, **_)
33
+ "#{resource_url}/#{id}"
34
+ end
35
+
36
+ def single_resource_url
37
+ self.class.single_resource_url(self&.id)
38
+ end
39
+
40
+ def self.perform_request_authenticated(*args)
41
+ if args.length > 2 && !args[2].nil?
42
+ options = args[2]
43
+ else
44
+ options = {}
45
+ end
46
+ recursion_depth = options[:recursion_depth] || 0
47
+
48
+ if NoyoApi::Api.config.access_token.nil?
49
+ authenticate
50
+ end
51
+
52
+ if !NoyoApi::Api.config.access_token.nil?
53
+ options[:headers] ||= {}
54
+ options[:headers] = options[:headers].merge(headers)
55
+
56
+ if NoyoApi::Api.config.verbose
57
+ puts "Calling API with action: #{args[0]}"
58
+ puts "Calling API with URL: #{args[1]}"
59
+ puts "Calling API with options: #{options.to_json}"
60
+ end
61
+
62
+ response = send(args[0], args[1], options)
63
+ body = get_body(response)
64
+
65
+ case response.code
66
+ when 401
67
+ reauthenticated = handle_unauthenticated(body)
68
+ if reauthenticated
69
+ recursion_depth += 1
70
+ if recursion_depth < @@MAX_AUTH_ATTEMPTS
71
+ options[:recursion_depth] = recursion_depth
72
+ perform_request_authenticated(args[0], args[1], options)
73
+ else
74
+ puts "Couldn't successfully reauthenticate." if NoyoApi::Api.config.verbose
75
+ nil
76
+ end
77
+ end
78
+ when 422
79
+ if NoyoApi::Api.config.verbose
80
+ puts "Body of response: #{body}"
81
+ end
82
+ response
83
+ else
84
+ response
85
+ end
86
+ elsif access_token.nil?
87
+ raise 'Could not log in. Please try again later.'
88
+ end
89
+ end
90
+
91
+ def self.get_body(response)
92
+ if response.nil?
93
+ return {}
94
+ end
95
+
96
+ body = response.parsed_response
97
+ if body.is_a? String
98
+ parsed_body = JSON.parse(body)
99
+ if parsed_body.is_a?(Hash)
100
+ body = parsed_body.with_indifferent_access
101
+ elsif parsed_body.is_a?(Array)
102
+ body = parsed_body.map(&:with_indifferent_access)
103
+ end
104
+ else
105
+ body
106
+ end
107
+ end
108
+
109
+ def self.headers
110
+ {
111
+ 'Authorization': "Bearer #{NoyoApi::Api.config.access_token}",
112
+ 'Accept': 'application/json',
113
+ 'Content-Type': 'application/json',
114
+ 'User-Agent' => user_agent,
115
+ }
116
+ end
117
+
118
+ def self.authenticate
119
+ client_id = NoyoApi::Api.config.client_id
120
+ client_secret = NoyoApi::Api.config.client_secret
121
+ service = NoyoAccounts::Client::Services::AuthService.new
122
+ access_token = service.authorize(client_id, client_secret)
123
+ NoyoApi::Api.config.access_token = access_token
124
+ end
125
+
126
+ def self.all(**args)
127
+ page_number = args[:page_number] || 1
128
+ base_uri(NoyoApi::Api.config.fulfillment_base_uri)
129
+
130
+ offset = (page_number - 1) * @@PAGE_SIZE
131
+ query = { offset: offset, page_size: @@PAGE_SIZE }
132
+ response = perform_request_authenticated(:get, resource_url(args), query: query)
133
+ api_response = ApiResponse.new(response)
134
+ response_body = api_response.response
135
+ if response_body.empty?
136
+ nil
137
+ else
138
+ if response_body.is_a?(Array)
139
+ results = response_body.map{ |response_item| new(response_item) }
140
+ NoyoFulfillment::ApiResourceCollection.new(results, page_number)
141
+ else
142
+ new(response_body)
143
+ end
144
+ end
145
+ end
146
+
147
+ def self.find(id, **args)
148
+ self.base_uri(NoyoApi::Api.config.fulfillment_base_uri)
149
+
150
+ response = perform_request_authenticated(:get, single_resource_url(id, args))
151
+ body = get_body(response)
152
+
153
+ if body.nil? || body.empty?
154
+ nil
155
+ else
156
+ new(body)
157
+ end
158
+ end
159
+
160
+ def self.handle_unauthenticated(body)
161
+ if body&.[]('code') == 16
162
+ puts 'Need to reauth' if NoyoApi::Api.config.verbose
163
+ # returns the access token
164
+ return authenticate
165
+ end
166
+
167
+ false
168
+ end
169
+
170
+ def create_hash
171
+ attributes
172
+ end
173
+
174
+ def create
175
+ options = { body: create_hash.to_json }
176
+ response = self.class.perform_request_authenticated(:post, create_url, options)
177
+ body = self.class.get_body(response)
178
+ update_attrs(self.class.new(body))
179
+
180
+ self
181
+ end
182
+
183
+ def resource_url
184
+ self.class.resource_url
185
+ end
186
+
187
+ def create_url
188
+ resource_url
189
+ end
190
+ end
191
+ end