noyo 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 (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