absorb_api 0.2.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +866 -0
  3. data/.ruby-version +1 -0
  4. data/CODE_OF_CONDUCT.md +46 -0
  5. data/Gemfile +3 -10
  6. data/README.md +0 -2
  7. data/Rakefile +3 -1
  8. data/absorb_api.gemspec +30 -16
  9. data/bin/console +11 -5
  10. data/doc/AbsorbApi.html +33 -64
  11. data/doc/AbsorbApi/Api.html +358 -0
  12. data/doc/AbsorbApi/Authorize.html +263 -0
  13. data/doc/AbsorbApi/Category.html +3 -2
  14. data/doc/AbsorbApi/Certificate.html +3 -2
  15. data/doc/AbsorbApi/Chapter.html +3 -2
  16. data/doc/AbsorbApi/Collection.html +3 -2
  17. data/doc/AbsorbApi/Configuration.html +3 -28
  18. data/doc/AbsorbApi/Course.html +3 -71
  19. data/doc/AbsorbApi/CourseEnrollment.html +6 -58
  20. data/doc/AbsorbApi/Create.html +164 -0
  21. data/doc/AbsorbApi/Curriculum.html +3 -2
  22. data/doc/AbsorbApi/Department.html +4 -14
  23. data/doc/AbsorbApi/Lesson.html +3 -2
  24. data/doc/AbsorbApi/LessonEnrollment.html +3 -2
  25. data/doc/AbsorbApi/Prerequisite.html +3 -2
  26. data/doc/AbsorbApi/Record.html +360 -0
  27. data/doc/AbsorbApi/Relations.html +2 -196
  28. data/doc/AbsorbApi/Relations/ClassMethods.html +197 -0
  29. data/doc/AbsorbApi/Resource.html +3 -2
  30. data/doc/AbsorbApi/ResourceNotFound.html +2 -1
  31. data/doc/AbsorbApi/Role.html +3 -2
  32. data/doc/AbsorbApi/RouteNotFound.html +2 -1
  33. data/doc/AbsorbApi/SessionSchedule.html +3 -2
  34. data/doc/AbsorbApi/Tag.html +4 -14
  35. data/doc/AbsorbApi/User.html +85 -35
  36. data/doc/AbsorbApi/ValidationError.html +2 -1
  37. data/doc/AbsorbApi/Where.html +162 -0
  38. data/doc/CODE_OF_CONDUCT_md.html +196 -0
  39. data/doc/Gemfile.html +8 -8
  40. data/doc/Gemfile_lock.html +57 -29
  41. data/doc/Icon/r.html +96 -0
  42. data/doc/LICENSE_txt.html +6 -1
  43. data/doc/Object.html +159 -0
  44. data/doc/README_md.html +6 -3
  45. data/doc/Rakefile.html +7 -2
  46. data/doc/absorb_api_gemspec.html +24 -7
  47. data/doc/bin/setup.html +6 -1
  48. data/doc/created.rid +34 -30
  49. data/doc/css/fonts.css +6 -6
  50. data/doc/index.html +19 -4
  51. data/doc/js/darkfish.js +7 -7
  52. data/doc/js/search_index.js +1 -1
  53. data/doc/js/search_index.js.gz +0 -0
  54. data/doc/js/searcher.js +1 -0
  55. data/doc/js/searcher.js.gz +0 -0
  56. data/doc/table_of_contents.html +118 -31
  57. data/lib/absorb_api.rb +43 -29
  58. data/lib/absorb_api/api.rb +69 -5
  59. data/lib/absorb_api/authorize.rb +37 -7
  60. data/lib/absorb_api/category.rb +3 -1
  61. data/lib/absorb_api/certificate.rb +5 -2
  62. data/lib/absorb_api/chapter.rb +3 -1
  63. data/lib/absorb_api/collection.rb +2 -19
  64. data/lib/absorb_api/configuration.rb +14 -12
  65. data/lib/absorb_api/course.rb +11 -25
  66. data/lib/absorb_api/course_enrollment.rb +13 -26
  67. data/lib/absorb_api/create.rb +19 -0
  68. data/lib/absorb_api/curriculum.rb +11 -2
  69. data/lib/absorb_api/department.rb +10 -4
  70. data/lib/absorb_api/lesson.rb +5 -2
  71. data/lib/absorb_api/lesson_enrollment.rb +8 -2
  72. data/lib/absorb_api/prerequisite.rb +5 -2
  73. data/lib/absorb_api/record.rb +58 -0
  74. data/lib/absorb_api/relations.rb +42 -40
  75. data/lib/absorb_api/resource.rb +3 -1
  76. data/lib/absorb_api/role.rb +3 -1
  77. data/lib/absorb_api/session_schedule.rb +6 -2
  78. data/lib/absorb_api/tag.rb +6 -3
  79. data/lib/absorb_api/user.rb +13 -29
  80. data/lib/absorb_api/version.rb +3 -1
  81. data/lib/absorb_api/where.rb +17 -0
  82. metadata +133 -8
  83. data/lib/absorb_api/base.rb +0 -12
  84. data/lib/absorb_api/orm.rb +0 -36
@@ -1,15 +1,79 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
4
  class Api
3
- attr_reader :connection, :token
5
+ attr_writer :connection
4
6
 
5
- def initialize
6
- @token = AbsorbApi::Authorize.new.token
7
+ delegate :get, :put, :patch, :post, :delete, to: :connection
7
8
 
8
- @connection ||= Faraday.new(url: AbsorbApi.configuration.url, parallel_manager: Typhoeus::Hydra.new(max_concurrency: 200)) do |faraday|
9
+ def connection
10
+ Faraday.new(
11
+ url: AbsorbApi.configuration.url,
12
+ parallel_manager: Typhoeus::Hydra.new(max_concurrency: 200)
13
+ ) do |faraday|
9
14
  faraday.request :json
10
15
  faraday.response :json, content_type: /\bjson$/
11
16
  faraday.adapter :typhoeus
12
- faraday.headers = { 'Authorization' => @token }
17
+ faraday.headers = { "Authorization" => AbsorbApi.token }
18
+ end
19
+ end
20
+
21
+ def get(path, params = {})
22
+ request(:get, path, params)
23
+ end
24
+
25
+ def put(path, params = {})
26
+ request(:put, path, params)
27
+ end
28
+
29
+ def patch(path, params = {})
30
+ request(:patch, path, params)
31
+ end
32
+
33
+ def post(path, params = {})
34
+ request(:post, path, params)
35
+ end
36
+
37
+ def delete(path, params = {})
38
+ request(:delete, path, params)
39
+ end
40
+
41
+ private
42
+
43
+ def request(http_method, path, params)
44
+ ignore_resource_not_found = params.delete(:ignore_resource_not_found)
45
+ response = connection.send(http_method, path, params)
46
+ handle_errored_response(response, ignore_resource_not_found)
47
+
48
+ if response.status == 401
49
+ response = fetch_new_token_and_try_again(http_method, path, params)
50
+ handle_errored_response(response, ignore_resource_not_found)
51
+ end
52
+
53
+ fetch_response_body(response, ignore_resource_not_found)
54
+ end
55
+
56
+ def fetch_new_token_and_try_again(http_method, path, params)
57
+ AbsorbApi.authorization = nil
58
+ connection.send(http_method, path, params)
59
+ end
60
+
61
+ def handle_errored_response(response, ignore_resource_not_found = false)
62
+ unless ignore_resource_not_found
63
+ raise RouteNotFound if [404, 405].include?(response.status)
64
+ raise ResourceNotFound if [400].include?(response.status)
65
+ end
66
+
67
+ raise ValidationError if [500].include?(response.status)
68
+ end
69
+
70
+ def fetch_response_body(response, ignore_resource_not_found = false)
71
+ return response.body unless ignore_resource_not_found
72
+
73
+ if [404, 405, 400].include?(response.status)
74
+ []
75
+ else
76
+ response.body
13
77
  end
14
78
  end
15
79
  end
@@ -1,17 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
4
  class Authorize
3
- attr_reader :token
5
+ attr_accessor :created_at
4
6
 
5
7
  def initialize
6
- @token ||= Faraday.new(url: AbsorbApi.configuration.url) do |faraday|
8
+ token
9
+ end
10
+
11
+ def expired?
12
+ return true if created_at.blank?
13
+
14
+ DateTime.now >= created_at + 4.hours
15
+ end
16
+
17
+ def token
18
+ return @token unless expired?
19
+
20
+ @token = ActiveSupport::JSON.decode(connection.post do |req|
21
+ req.url "Authenticate"
22
+ req.headers["Content-Type"] = "application/json"
23
+ req.headers["accept"] = "json"
24
+ req.body = payload.to_json
25
+ end.body)
26
+ @created_at = DateTime.now
27
+ end
28
+
29
+ private
30
+
31
+ def connection
32
+ Faraday.new(url: AbsorbApi.configuration.url) do |faraday|
7
33
  faraday.request :url_encoded
8
34
  faraday.response :logger
9
35
  faraday.adapter :typhoeus
10
- end.post do |req|
11
- req.url 'Authenticate', username: AbsorbApi.configuration.absorbuser, password: AbsorbApi.configuration.absorbpass, privateKey: AbsorbApi.configuration.absorbkey
12
- req.headers['Content-Type'] = 'application/json'
13
- req.headers['accept'] = 'json'
14
- end.body.delete('\\"')
36
+ end
37
+ end
38
+
39
+ def payload
40
+ {
41
+ "Username" => AbsorbApi.configuration.absorbuser,
42
+ "Password" => AbsorbApi.configuration.absorbpass,
43
+ "PrivateKey" => AbsorbApi.configuration.absorbkey
44
+ }
15
45
  end
16
46
  end
17
47
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Category < Base
4
+ class Category < Record
3
5
  attr_accessor :id, :parent_id, :name, :description
4
6
  end
5
7
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Certificate < Base
3
- attr_accessor :id, :enrollment_id, :full_name, :course_name, :acquired_date, :expiry_date, :notes
4
+ class Certificate < Record
5
+ attr_accessor :id, :enrollment_id, :full_name, :course_name, :acquired_date,
6
+ :expiry_date, :notes
4
7
  end
5
8
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Chapter < Base
4
+ class Chapter < Record
3
5
  attr_accessor :id, :name, :lesson_ids
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
4
  class Collection < ::Array
3
5
  attr_reader :metadata
@@ -6,24 +8,5 @@ module AbsorbApi
6
8
  super(elements)
7
9
  @metadata = metadata
8
10
  end
9
-
10
- # private
11
- # def method_missing(method_name, *arguments, &block)
12
- # if "AbsorbApi::#{metadata[:klass]}".constantize.new.respond_to? method_name
13
- # collection = []
14
- # Base.api.in_parallel do
15
- # self.each do |object|
16
- # collection << Base.api.get("#{metadata[:klass].to_s.pluralize}/#{object.id}/#{method_name}")
17
- # end
18
- # end
19
- # collection.map { |response| response.body.map { |attrs| CourseEnrollment.new(attrs) } }.flatten
20
- # else
21
- # super
22
- # end
23
- # end
24
- #
25
- # def respond_to_missing?(method_name, include_private = false)
26
- # self.first.respond_to? method_name || super
27
- # end
28
11
  end
29
12
  end
@@ -1,21 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
4
  class Configuration
3
- attr_accessor :url, :absorbuser, :absorbpass, :absorbkey, :ignored_lesson_types, :ignored_course_ids
5
+ attr_accessor :url, :absorbuser, :absorbpass, :absorbkey
4
6
  end
5
7
 
6
- class << self
7
- attr_accessor :configuration
8
- end
8
+ cattr_accessor :configuration
9
9
 
10
- def self.configuration
11
- @configuration ||= Configuration.new
12
- end
10
+ class << self
11
+ def configuration
12
+ @configuration ||= Configuration.new
13
+ end
13
14
 
14
- def self.reset
15
- @configuration = Configuration.new
16
- end
15
+ def reset
16
+ @configuration = Configuration.new
17
+ end
17
18
 
18
- def self.configure
19
- yield(configuration)
19
+ def configure
20
+ yield(configuration)
21
+ end
20
22
  end
21
23
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Course < Base
3
- include Relations
4
+ class Course < Record
5
+ with_relationships
6
+ can_search
4
7
 
5
8
  attr_accessor :id, :name, :description, :notes, :external_id, :access_date,
6
9
  :expire_type, :expire_duration, :expiry_date, :active_status,
@@ -10,28 +13,11 @@ module AbsorbApi
10
13
  :category_id, :certificate_url, :audience, :goals, :vendor,
11
14
  :company_cost, :learner_cost, :company_time, :learner_time
12
15
 
13
- has_many :certificates
14
- has_many :chapters
15
- has_many :enrollments, :course_enrollment
16
- has_many :resources
17
- has_many :prerequisites
18
- has_many :lessons
19
-
20
- # gets all associated enrollments given a collection of courses
21
- # all calls are called in parallel
22
- def self.enrollments_from_collection(courses, **conditions)
23
- enrollments = []
24
- connection = AbsorbApi::Api.new.connection
25
- courses.reject! do |course|
26
- AbsorbApi.configuration.ignored_course_ids.include? course.id
27
- end
28
-
29
- connection.in_parallel do
30
- courses.each do |course|
31
- enrollments << connection.get("courses/#{course.id}/enrollments", conditions)
32
- end
33
- end
34
- enrollments.map { |response| response.body.map { |attrs| CourseEnrollment.new(attrs) } }.flatten
35
- end
16
+ with_many :certificates
17
+ with_many :chapters
18
+ with_many :enrollments, :course_enrollment
19
+ with_many :resources
20
+ with_many :prerequisites
21
+ with_many :lessons
36
22
  end
37
23
  end
@@ -1,35 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class CourseEnrollment < Base
3
- attr_accessor :id, :course_id, :course_name, :course_version_id, :user_id, :full_name, :status, :progress, :score, :accepted_terms_and_conditions, :time_spent, :date_started, :date_completed, :enrollment_key_id, :certificate_id, :credits
4
+ class CourseEnrollment < Record
5
+ attr_accessor :id, :course_id, :course_name, :course_version_id, :user_id,
6
+ :full_name, :status, :progress, :score,
7
+ :accepted_terms_and_conditions, :time_spent, :date_started,
8
+ :date_completed, :enrollment_key_id, :certificate_id, :credits
4
9
 
5
10
  def lessons(**conditions)
6
- AbsorbApi::Api.new.connection.get("users/#{self.user_id}/enrollments/#{self.course_id}/lessons", conditions).body.map! do |lesson_attributes|
7
- LessonEnrollment.new(lesson_attributes)
11
+ get(url, conditions.merge(ignore_resource_not_found: true)).map do |attrs|
12
+ LessonEnrollment.new(attrs)
8
13
  end
9
14
  end
10
15
 
11
- # gets all associated lessons given a collection of enrollments
12
- # all calls are called in parallel
13
- # enrollments are chunked in groups of 105 to keep typhoeus from getting bogged down
14
- # modifiedSince must be a DateTime object
15
- def self.lessons_from_collection(course_enrollments, **filters)
16
- lessons = []
17
- connection = AbsorbApi::Api.new.connection
18
- course_enrollments.each_slice(105) do |enrollment_slice|
19
- connection.in_parallel do
20
- enrollment_slice.each do |enrollment|
21
- lessons << connection.get("users/#{enrollment.user_id}/enrollments/#{enrollment.course_id}/lessons", filters)
22
- end
23
- end
24
- end
25
- lessons.map! do |response|
26
- response.body.map do |body|
27
- LessonEnrollment.new(body)
28
- end
29
- end
30
- lessons.flatten.reject do |lesson|
31
- AbsorbApi.configuration.ignored_lesson_types.include? lesson.type
32
- end
16
+ private
17
+
18
+ def url
19
+ "users/#{user_id}/enrollments/#{course_id}/lessons"
33
20
  end
34
21
  end
35
22
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbsorbApi
4
+ module Create
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def create(attributes = {})
9
+ object = new(attributes)
10
+ yield(object) if block_given?
11
+
12
+ attrs = object.as_json.transform_keys(&:camelize)
13
+ response = api.post(to_s.demodulize.pluralize.to_s, attrs)
14
+ object.id = response.body["Id"]
15
+ object
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Curriculum < Base
3
- attr_accessor :is_pacing_enabled, :curriculum_group_ids, :id, :name, :description, :notes, :external_id, :access_date, :expire_type, :expire_duration, :expiry_date, :active_status, :tag_ids, :resource_ids, :editor_ids, :prices, :competency_definition_ids, :prerequisite_course_ids, :post_enrollment_course_ids, :allow_course_evaluation, :category_id, :certificate_url, :audience, :goals, :vendor, :company_cost, :learner_cost, :company_time, :learner_time
4
+ class Curriculum < Record
5
+ attr_accessor :is_pacing_enabled, :curriculum_group_ids, :id, :name,
6
+ :description, :notes, :external_id, :access_date,
7
+ :expire_type, :expire_duration, :expiry_date, :active_status,
8
+ :tag_ids, :resource_ids, :editor_ids, :prices,
9
+ :competency_definition_ids, :prerequisite_course_ids,
10
+ :post_enrollment_course_ids, :allow_course_evaluation,
11
+ :category_id, :certificate_url, :audience, :goals, :vendor,
12
+ :company_cost, :learner_cost, :company_time, :learner_time
4
13
  end
5
14
  end
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Department < Base
3
- include Relations
4
+ class Department < Record
5
+ with_relationships
6
+ can_create
7
+ can_search
4
8
 
5
- attr_accessor :id, :name, :use_department_contact_details, :company_name, :phone_number, :email_address, :external_id, :parent_id, :currency_id
9
+ attr_accessor :id, :name, :use_department_contact_details, :company_name,
10
+ :phone_number, :email_address, :external_id, :parent_id,
11
+ :currency_id
6
12
 
7
- has_one :parent, :department
13
+ with_one :parent, :department
8
14
  end
9
15
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Lesson < Base
3
- attr_accessor :id, :chapter_id, :name, :description, :notes, :type, :width, :height, :url, :use_popup, :passing_score, :weight, :ref_id
4
+ class Lesson < Record
5
+ attr_accessor :id, :chapter_id, :name, :description, :notes, :type, :width,
6
+ :height, :url, :use_popup, :passing_score, :weight, :ref_id
4
7
  end
5
8
  end
@@ -1,5 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class LessonEnrollment < Base
3
- attr_accessor :lesson_id, :chapter_enrollment_id, :type, :attempts, :failures, :last_attempt, :id, :course_enrollment_id, :chapter_id, :session_id, :name, :user_id, :full_name, :status, :progress, :score, :time_spent, :date_enrolled, :date_started, :date_completed
4
+ class LessonEnrollment < Record
5
+ attr_accessor :lesson_id, :chapter_enrollment_id, :type, :attempts,
6
+ :failures, :last_attempt, :id, :course_enrollment_id,
7
+ :chapter_id, :session_id, :name, :user_id, :full_name,
8
+ :status, :progress, :score, :time_spent, :date_enrolled,
9
+ :date_started, :date_completed
4
10
  end
5
11
  end
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbsorbApi
2
- class Prerequisite < Base
3
- attr_accessor :id, :name, :required_number, :prerequisite_type, :course_ids, :competency_definition_ids
4
+ class Prerequisite < Record
5
+ attr_accessor :id, :name, :required_number, :prerequisite_type, :course_ids,
6
+ :competency_definition_ids
4
7
  end
5
8
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbsorbApi
4
+ class Record
5
+ delegate :connection, :get, :put, :patch, :post, :delete, to: :api
6
+ delegate :in_parallel, to: :connection
7
+
8
+ def initialize(params = {})
9
+ params.each do |attr, value|
10
+ next unless respond_to?("#{attr.to_s.underscore}=")
11
+
12
+ public_send("#{attr.to_s.underscore}=", value)
13
+ end
14
+
15
+ yield self if block_given?
16
+ end
17
+
18
+ class << self
19
+ def find(id)
20
+ raise ResourceNotFound if id.blank?
21
+
22
+ new(api.get("#{to_s.demodulize.pluralize}/#{id}"))
23
+ end
24
+
25
+ def all
26
+ objs = api.get(to_s.demodulize.pluralize).map do |attrs|
27
+ new(attrs)
28
+ end
29
+
30
+ Collection.new(objs, klass: to_s.demodulize)
31
+ end
32
+
33
+ def with_relationships
34
+ include Relations
35
+ end
36
+
37
+ def can_create
38
+ include Create
39
+ end
40
+
41
+ def can_search
42
+ include Where
43
+ end
44
+
45
+ private
46
+
47
+ def api
48
+ AbsorbApi::Api.new
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def api
55
+ @api ||= AbsorbApi::Api.new
56
+ end
57
+ end
58
+ end