lessonly-api 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 685801473282dac177c5b10f4c1a8ed916f7def482c892cb29191f6cf5678941
4
+ data.tar.gz: 6f0804275927087399b2ad01d93415204ec3c79fd4afc77d7d4cf846f7d047ff
5
+ SHA512:
6
+ metadata.gz: aed4cacf823900924aa286c555e531df1c7f18e2666f118fce379964a19bcf2b7f78eb7de520cdb4f42544e3857aba8b85990c03c64ca4cd6768ff4e96690a00
7
+ data.tar.gz: 6f90abf488d237699dbcf2c687ca1956388f85416e2cc8b8c8c89bbe9a93652cb485a60dd1dc11db23af3592c73c77804b6d0698de72e83ceb6e9a67f11d7d72
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .env
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aptible-auth.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Aptible, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # Lessonly Api Ruby Client
2
+
3
+
4
+ Ruby client for [Lesson.ly](http://lesson.ly) API.
5
+
6
+
7
+ ## Installation
8
+
9
+ Add the following line to your application's Gemfile
10
+
11
+ ```ruby
12
+ gem 'lessonly-api'
13
+ ```
14
+
15
+ And then run `bundle install`.
16
+
17
+ ## Usage
18
+
19
+ First, configure your client:
20
+
21
+ ```ruby
22
+ require 'lessonly'
23
+
24
+ LessonlyApi.configure do |config|
25
+ config.root_url = 'https://api.lesson.ly/api/v1'
26
+ config.api_key = `LESSONLY API KEY`
27
+ config.domain = `LESSONLY SUBDOMAIN`
28
+ end
29
+ ```
30
+
31
+ This is best done in an application initializer inside `config/initializers`.
32
+
33
+ From here, you can interact with the Lessonly API resources however you wish:
34
+
35
+ ### Users
36
+
37
+ ```ruby
38
+ all_users = LessonlyApi::Users.list
39
+
40
+ skylar = LessonlyApi::Users.show(user_id)
41
+ skylar.name # "John Doe"
42
+ skylar.email # "johndoe@aptible.com"
43
+
44
+ frank = LessonlyApi::User.create({ name: 'Frank Macreery', email: 'frank@aptible.com', role: 'learner' })
45
+ frank.name # "Frank Macreery"
46
+ ```
47
+
48
+ ## Copyright and License
49
+
50
+ MIT License, see [LICENSE](LICENSE.md) for details.
51
+
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'dotenv/load'
6
+
7
+ require 'lessonly_api'
8
+
9
+ LessonlyApi.configure do |config|
10
+ config.root_url = ENV['LESSONLY_ROOT_URL']
11
+ config.api_key = ENV['LESSONLY_API_KEY']
12
+ config.domain = ENV['LESSONLY_DOMAIN']
13
+ end
14
+
15
+ require 'pry-byebug'
16
+ Pry.start
@@ -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,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ require 'lessonly_api/version'
8
+
9
+ Gem::Specification.new do |spec|
10
+ spec.name = 'lessonly-api'
11
+ spec.version = LessonlyApi::VERSION
12
+ spec.authors = ['Ilya Kamenko']
13
+ spec.email = ['ilya.kamenko@gmail.com']
14
+ spec.description = 'Ruby client for Lesson.ly API'
15
+ spec.summary = 'Ruby client for Lesson.ly API'
16
+ spec.homepage = 'https://github.com/toptal/lessonly-api'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
20
+ f.match(%r{^(spec)/})
21
+ end
22
+ spec.test_files = spec.files.grep(%r{^spec\/})
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_runtime_dependency 'gem_config'
26
+ spec.add_runtime_dependency 'activemodel', '>= 3.2'
27
+ spec.add_runtime_dependency 'faraday', '>= 0.8'
28
+ spec.add_runtime_dependency 'httpclient', '~> 2.8'
29
+
30
+ spec.add_development_dependency 'bundler', '>= 2.1.2'
31
+ spec.add_development_dependency 'dotenv', '~> 2.2'
32
+ spec.add_development_dependency 'pry-byebug', '~> 3.7'
33
+ spec.add_development_dependency 'rake', '~> 12.3'
34
+ spec.add_development_dependency 'rspec', '~> 3.0'
35
+ end
@@ -0,0 +1,46 @@
1
+ require 'gem_config'
2
+ require 'logger'
3
+
4
+ module LessonlyApi
5
+ include GemConfig::Base
6
+
7
+ with_configuration do
8
+ has :logger,
9
+ classes: ::Logger,
10
+ default: ::Logger.new($stdout)
11
+
12
+ has :root_url,
13
+ classes: String,
14
+ default: ENV['LESSONLY_ROOT_URL'] || 'https://api.lesson.ly/api/v1.1'
15
+
16
+ has :domain,
17
+ classes: String,
18
+ default: ENV['LESSONLY_DOMAIN'] || ''
19
+
20
+ has :api_key,
21
+ classes: String,
22
+ default: ENV['LESSONLY_API_KEY'] || ''
23
+
24
+ has :webhook_handler
25
+ has :webhook_authentication, classes: String
26
+ end
27
+ end
28
+
29
+ require 'lessonly_api/version'
30
+ require 'lessonly_api/errors'
31
+
32
+ require 'lessonly_api/utils/client'
33
+ require 'lessonly_api/utils/custom_field_converter'
34
+ require 'lessonly_api/resource_type/base'
35
+ require 'lessonly_api/request'
36
+
37
+ require 'lessonly_api/users'
38
+
39
+ require 'lessonly_api/resource_type/users'
40
+ require 'lessonly_api/resource_type/user'
41
+ require 'lessonly_api/resource_type/webhook'
42
+ require 'lessonly_api/resource_type/lesson'
43
+ require 'lessonly_api/resource_type/user_group'
44
+ require 'lessonly_api/resource_type/user_assignments'
45
+ require 'lessonly_api/resource_type/assignment'
46
+
@@ -0,0 +1,26 @@
1
+ module LessonlyApi::Errors
2
+ class Base < StandardError; end
3
+ class ApiTimeoutError < Base; end
4
+ class InvalidJson < Base; end
5
+
6
+ class ApiError < Base
7
+ attr_reader :body
8
+
9
+ def initialize(body)
10
+ @body = body
11
+ end
12
+
13
+ def message
14
+ "#{self.class.name}: #{body['error_info']}"
15
+ end
16
+ end
17
+
18
+ class BadRequest < ApiError; end
19
+ class Unauthorized < ApiError; end
20
+ class Forbidden < ApiError; end
21
+ class NotFound < ApiError; end
22
+ class MethodNotAllowed < ApiError; end
23
+ class NotAcceptable < ApiError; end
24
+ class InternalServerError < ApiError; end
25
+ class ServiceUnavailable < ApiError; end
26
+ end
@@ -0,0 +1,60 @@
1
+ module LessonlyApi
2
+ class Groups < Request
3
+ class << self
4
+ def list
5
+ raw_result = get('/groups')
6
+ raw_result['groups'].map { |raw_group| ResourceType::Group.new(raw_group) }
7
+ end
8
+
9
+ def show(id)
10
+ raw_result = get("/groups/#{id}")
11
+ LessonlyApi::ResourceType::Group.new(raw_result)
12
+ end
13
+
14
+ def create(attributes = {})
15
+ raw_result = post("/users", attributes)
16
+ ResourceType::User.new(raw_result)
17
+ end
18
+
19
+ def update(id, attributes = {})
20
+ raw_result = put("/users/#{id}", attributes)
21
+ ResourceType::User.new(raw_result)
22
+ end
23
+
24
+ def archive(id)
25
+ raw_result = put("/users/#{id}/archive")
26
+ ResourceType::User.new(raw_result)
27
+ end
28
+
29
+ def restore(id)
30
+ raw_result = put("/users/#{id}/restore")
31
+ ResourceType::User.new(raw_result)
32
+ end
33
+
34
+ def delete(id)
35
+ raw_result = delete("/users/#{id}")
36
+ ResourceType::User.new(raw_result)
37
+ end
38
+
39
+ def user_groups(id)
40
+ raw_result = get("/users/#{id}/groups")
41
+ # ResourceType::User.new(raw_result)
42
+ end
43
+
44
+ def update_user_groups(id)
45
+ raw_result = get("/users/#{id}/groups")
46
+ # ResourceType::User.new(raw_result)
47
+ end
48
+
49
+ def user_assignments(id)
50
+ raw_result = get("/users/#{id}/assignments")
51
+ #
52
+ end
53
+
54
+ def create_user_assignments(id)
55
+ raw_result = get("/users/#{id}/assignments")
56
+ #
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,11 @@
1
+ class LessonlyApi::Request < LessonlyApi::ResourceType::Base
2
+ delegate :get, :post, :client, to: :class
3
+
4
+ class << self
5
+ delegate :get, :post, :put, :delete, to: :client
6
+
7
+ def client
8
+ LessonlyApi::Utils::Client.instance
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class Assignment < LessonlyApi::ResourceType::Base
4
+ fields(
5
+ :id,
6
+ :assignee_id,
7
+ :ext_id,
8
+ :assignable_id,
9
+ :assignable_type,
10
+ :due_by,
11
+ :reassigned_at,
12
+ :completed_at,
13
+ :updated_at,
14
+ :status,
15
+ :score
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+ require 'json'
5
+
6
+ module LessonlyApi
7
+ module ResourceType
8
+ class Base
9
+ include ActiveModel::Model
10
+ include ActiveModel::Dirty
11
+ include ActiveModel::Serializers::JSON
12
+
13
+ class_attribute :fields_names
14
+ self.fields_names = []
15
+
16
+ class << self
17
+ def from_json(json)
18
+ parsed_json = json && JSON.parse(json).to_h
19
+ return new({}) unless parsed_json
20
+
21
+ new(parsed_json)
22
+ end
23
+
24
+ def fields(*keys_with_types)
25
+ keys_with_types.each do |name_or_hash|
26
+ field_name = define_methods_for(name_or_hash)
27
+ self.fields_names += [field_name]
28
+ end
29
+ end
30
+
31
+ def load(string)
32
+ from_json(string)
33
+ end
34
+
35
+ def dump(data_type)
36
+ data_type.to_json
37
+ end
38
+
39
+ private
40
+
41
+ def define_methods_for(name_or_hash)
42
+ if name_or_hash.is_a?(Hash)
43
+ define_custom_field(*name_or_hash.to_a.first)
44
+ else
45
+ define_simple_field(name_or_hash)
46
+ end
47
+ end
48
+
49
+ def define_custom_field(name, data_type)
50
+ attr_reader name
51
+
52
+ define_method("#{name}=") do |value|
53
+ value = LessonlyApi::Utils::CustomFieldConverter.new(data_type).convert(value)
54
+ instance_variable_set("@#{name}", value)
55
+ end
56
+
57
+ name
58
+ end
59
+
60
+ def define_simple_field(name)
61
+ attr_accessor name
62
+ name
63
+ end
64
+ end
65
+
66
+ def initialize(attributes = {})
67
+ defined_attributes = attributes.with_indifferent_access.slice(*fields_names)
68
+
69
+ super(defined_attributes)
70
+ end
71
+
72
+ def attributes
73
+ fields_names.each_with_object({}) do |field, hash|
74
+ value = public_send(field)
75
+ hash[field] = value if value
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,7 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class Group < LessonlyApi::Resource
4
+ fields(:id, :name, :archived_at, :archived_by_user_id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class Groups < LessonlyApi::Resource
4
+ fields(:id, :name, :archived_at, :archived_by_user_id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class Lesson < LessonlyApi::ResourceType::Base
4
+ fields(:id, :title, :archived_at, :archived_by_user_id)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class User < LessonlyApi::ResourceType::Base
4
+ fields(
5
+ :id,
6
+ :resource_type,
7
+ :type,
8
+ :ext_uid,
9
+ :email,
10
+ :name,
11
+ :role,
12
+ :role_id,
13
+ :archived_at,
14
+ :archived_by_user_id,
15
+ :job_title,
16
+ :business_unit,
17
+ :department,
18
+ :location,
19
+ :locale,
20
+ :hire_date,
21
+ :manager_name,
22
+ :mobile_phone_number,
23
+ :custom_user_field_data,
24
+ {groups: :user_group}
25
+ )
26
+
27
+ def user_assignments
28
+ LessonlyApi::Users.user_assignments(self.id)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class UserAssignments < LessonlyApi::ResourceType::Base
4
+ fields(:type, assignments: [:assignment])
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class UserGroup < LessonlyApi::ResourceType::Base
4
+ fields(
5
+ :member,
6
+ :manager,
7
+ :memberships,
8
+ :managing,
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class Users < LessonlyApi::ResourceType::Base
4
+ fields(:type, :total_users, :total_pages, :page, :per_page, users: [:user])
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module LessonlyApi
2
+ module ResourceType
3
+ class Webhook < LessonlyApi::ResourceType::Base
4
+ fields(
5
+ {user: :user},
6
+ {lesson: :lesson},
7
+ :score,
8
+ :score_percent,
9
+ :started_at,
10
+ :completed_at,
11
+ :assigned_at,
12
+ :report_card_url
13
+ )
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,61 @@
1
+ module LessonlyApi
2
+ class Users < Request
3
+ class << self
4
+ def list(page: 1, per_page: 50, **filters)
5
+ raw_result = get('/users', page: page, per_page: per_page, filter: filters)
6
+ ResourceType::Users.new(raw_result)
7
+ end
8
+
9
+ def show(id)
10
+ raw_result = get("/users/#{id}")
11
+ LessonlyApi::ResourceType::User.new(raw_result)
12
+ end
13
+
14
+ def create(attributes = {})
15
+ raw_result = post("/users", attributes)
16
+ ResourceType::User.new(raw_result)
17
+ end
18
+
19
+ def update(id, attributes = {})
20
+ raw_result = put("/users/#{id}", attributes)
21
+ ResourceType::User.new(raw_result)
22
+ end
23
+
24
+ def archive(id)
25
+ raw_result = put("/users/#{id}/archive")
26
+ ResourceType::User.new(raw_result)
27
+ end
28
+
29
+ def restore(id)
30
+ raw_result = put("/users/#{id}/restore")
31
+ ResourceType::User.new(raw_result)
32
+ end
33
+
34
+ # TODO: i.kamenko method name should be `delete` probably
35
+ def destroy(id)
36
+ raw_result = delete("/users/#{id}")
37
+ ResourceType::User.new(raw_result)
38
+ end
39
+
40
+ def user_groups(id)
41
+ raw_result = get("/users/#{id}/groups")
42
+ ResourceType::UserGroup.new(raw_result)
43
+ end
44
+
45
+ def update_user_groups(id, attributes = {})
46
+ raw_result = put("/users/#{id}/groups", attributes)
47
+ ResourceType::UserGroup.new(raw_result)
48
+ end
49
+
50
+ def user_assignments(id)
51
+ raw_result = get("/users/#{id}/assignments")
52
+ ResourceType::UserAssignments.new(raw_result)
53
+ end
54
+
55
+ def create_user_assignments(id, assignments = [])
56
+ raw_result = put("/users/#{id}/assignments", assignments: assignments)
57
+ ResourceType::UserAssignments.new(raw_result)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,130 @@
1
+ require 'delegate'
2
+ require 'faraday'
3
+ require 'base64'
4
+ require 'json'
5
+
6
+ module LessonlyApi
7
+ module Utils
8
+ class Client
9
+ extend Forwardable
10
+
11
+ ERRORS_MAPPING = {
12
+ 400 => LessonlyApi::Errors::BadRequest,
13
+ 401 => LessonlyApi::Errors::Unauthorized,
14
+ 403 => LessonlyApi::Errors::Forbidden,
15
+ 404 => LessonlyApi::Errors::NotFound,
16
+ 405 => LessonlyApi::Errors::MethodNotAllowed,
17
+ 406 => LessonlyApi::Errors::NotAcceptable,
18
+ 500 => LessonlyApi::Errors::InternalServerError,
19
+ 503 => LessonlyApi::Errors::ServiceUnavailable
20
+ }.freeze
21
+
22
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal
23
+
24
+ attr_reader :root_url, :domain, :api_key, :logger
25
+
26
+ def initialize(configuration)
27
+ @root_url = configuration.root_url
28
+ @domain = configuration.domain
29
+ @api_key = configuration.api_key
30
+ @logger = configuration.logger
31
+ end
32
+
33
+ def self.instance
34
+ @instance ||= new(LessonlyApi.configuration)
35
+ end
36
+
37
+ def get(endpoint, params = {})
38
+ send_request(:get, endpoint, params)
39
+ end
40
+
41
+ def post(endpoint, params = {})
42
+ send_request(:post, endpoint, params)
43
+ end
44
+
45
+ def put(endpoint, params = {})
46
+ send_request(:put, endpoint, params)
47
+ end
48
+
49
+ def delete(endpoint, params = {})
50
+ send_request(:delete, endpoint, params)
51
+ end
52
+
53
+ private
54
+
55
+ def connection
56
+ Faraday.new(root_url, http_options) do |faraday|
57
+ faraday.request :url_encoded
58
+ faraday.response :logger, logger, headers: true, bodies: true
59
+
60
+ faraday.adapter :httpclient
61
+ end
62
+ end
63
+
64
+ def http_options
65
+ {
66
+ headers: {
67
+ 'Accept' => 'application/json',
68
+ 'Content-Type' => 'application/json',
69
+ 'Authorization' => auth_token
70
+ }
71
+ }
72
+ end
73
+
74
+ def auth_token
75
+ Base64.strict_encode64("#{domain}:#{api_key}")
76
+ end
77
+
78
+ def send_request(method, endpoint, params)
79
+ response = with_tag do
80
+ connection.public_send(method) do |request|
81
+ # TODO: i.kamenko refactor it
82
+ request.url "/api/v1.1/#{endpoint}"
83
+ assign_params(request, method: method, params: params)
84
+ end
85
+ end
86
+
87
+ process_response(response)
88
+ rescue Faraday::TimeoutError => e
89
+ raise LessonlyApi::Errors::ApiTimeoutError, e.message
90
+ rescue Faraday::Error => e
91
+ raise LessonlyApi::Errors::Base, e.message
92
+ end
93
+
94
+ def assign_params(request, method:, params:)
95
+ if method == :get
96
+ request.params = params
97
+ else
98
+ request.body = params.to_json
99
+ end
100
+ end
101
+
102
+ def process_response(response)
103
+ return process_success(response) if response.status >= 200 && response.status < 300
104
+
105
+ error = ERRORS_MAPPING[response.status] || LessonlyApi::Errors::ApiError
106
+ raise(error, parse_json(response.body))
107
+ end
108
+
109
+ def process_success(response)
110
+ return {} if response.body.empty?
111
+
112
+ parse_json(response.body)
113
+ end
114
+
115
+ def with_tag
116
+ if logger&.respond_to?(:tagged)
117
+ logger.tagged('LESSONLY API') { yield }
118
+ else
119
+ yield
120
+ end
121
+ end
122
+
123
+ def parse_json(data)
124
+ JSON.parse(data)
125
+ rescue JSON::ParserError => e
126
+ raise LessonlyApi::Errors::InvalidJson, e.message
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module LessonlyApi
6
+ module Utils
7
+ class CustomFieldConverter
8
+ CONVERSIONS = {
9
+ big_decimal: ->(val) { val.is_a?(BigDecimal) ? val : val && BigDecimal(val.to_s) },
10
+ date: ->(val) { val.is_a?(Date) ? val : val && Date.parse(val) }
11
+ }.freeze
12
+
13
+ def initialize(data_type)
14
+ @data_type = data_type
15
+ end
16
+
17
+ def convert(value)
18
+ if @data_type.is_a?(Array)
19
+ convert_array(value)
20
+ elsif CONVERSIONS.key?(@data_type)
21
+ CONVERSIONS[@data_type].call(value)
22
+ else
23
+ wrap(value, @data_type)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def wrap(value, data_type)
30
+ data_type_class = "LessonlyApi::ResourceType::#{data_type.to_s.camelize}".constantize
31
+ value.is_a?(LessonlyApi::ResourceType::Base) ? value : data_type_class.new(value)
32
+ end
33
+
34
+ def convert_array(array)
35
+ return [] if array.blank?
36
+
37
+ array.map { |item| wrap(item, @data_type.first) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module LessonlyApi
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,50 @@
1
+ module LessonlyApi
2
+ class Webhook
3
+ class << self
4
+ def call(env)
5
+ new(env).call
6
+ end
7
+ end
8
+
9
+ attr_reader :env
10
+
11
+ def initialize(env)
12
+ @env = env
13
+ end
14
+
15
+ def call
16
+ return [405, {"Content-Type" => "text/plain"}, ['Method Not Allowed']] unless webhook_handler
17
+ return [401, {"Content-Type" => "text/plain"}, ['Unauthorized']] unless authorized?
18
+
19
+ parse_webhook_data
20
+
21
+ return [406, {"Content-Type" => "text/plain"}, ['Not Acceptable']] if raw_webhook_data.nil?
22
+
23
+ webhook_handler.call(webhook_data)
24
+
25
+ [200, {"Content-Type" => "text/plain"}, ['OK']]
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :raw_webhook_data
31
+
32
+ def authorized?
33
+ env['HTTP_AUTHORIZATION'] == LessonlyApi.configuration.webhook_authentication
34
+ end
35
+
36
+ def parse_webhook_data
37
+ @raw_webhook_data = JSON.parse(env['rack.input'].read)
38
+ rescue JSON::ParserError
39
+ nil
40
+ end
41
+
42
+ def webhook_data
43
+ LessonlyApi::ResourceType::Webhook.new(raw_webhook_data)
44
+ end
45
+
46
+ def webhook_handler
47
+ LessonlyApi.configuration.webhook_handler
48
+ end
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lessonly-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ilya Kamenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gem_config
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activemodel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: httpclient
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.8'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.1.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.1.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: dotenv
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '12.3'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '12.3'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ description: Ruby client for Lesson.ly API
140
+ email:
141
+ - ilya.kamenko@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - Gemfile
149
+ - LICENSE.md
150
+ - README.md
151
+ - bin/console
152
+ - bin/setup
153
+ - lessonly-api.gemspec
154
+ - lib/lessonly_api.rb
155
+ - lib/lessonly_api/errors.rb
156
+ - lib/lessonly_api/groups.rb
157
+ - lib/lessonly_api/request.rb
158
+ - lib/lessonly_api/resource_type/assignment.rb
159
+ - lib/lessonly_api/resource_type/base.rb
160
+ - lib/lessonly_api/resource_type/group.rb
161
+ - lib/lessonly_api/resource_type/groups.rb
162
+ - lib/lessonly_api/resource_type/lesson.rb
163
+ - lib/lessonly_api/resource_type/user.rb
164
+ - lib/lessonly_api/resource_type/user_assignments.rb
165
+ - lib/lessonly_api/resource_type/user_group.rb
166
+ - lib/lessonly_api/resource_type/users.rb
167
+ - lib/lessonly_api/resource_type/webhook.rb
168
+ - lib/lessonly_api/users.rb
169
+ - lib/lessonly_api/utils/client.rb
170
+ - lib/lessonly_api/utils/custom_field_converter.rb
171
+ - lib/lessonly_api/version.rb
172
+ - lib/lessonly_api/webhook.rb
173
+ homepage: https://github.com/toptal/lessonly-api
174
+ licenses:
175
+ - MIT
176
+ metadata: {}
177
+ post_install_message:
178
+ rdoc_options: []
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ requirements: []
192
+ rubygems_version: 3.1.4
193
+ signing_key:
194
+ specification_version: 4
195
+ summary: Ruby client for Lesson.ly API
196
+ test_files: []