canvas_shim 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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +21 -0
  5. data/app/assets/config/canvas_shim_manifest.js +2 -0
  6. data/app/assets/javascripts/canvas_shim/application.js +13 -0
  7. data/app/assets/stylesheets/canvas_shim/application.css +15 -0
  8. data/app/controllers/canvas_shim/application_controller.rb +12 -0
  9. data/app/controllers/canvas_shim/settings_api/swagger_controller.rb +11 -0
  10. data/app/controllers/canvas_shim/settings_api/v1/users_controller.rb +27 -0
  11. data/app/deploy/canvas_shim_asset_uploader.rb +19 -0
  12. data/app/helpers/canvas_shim/application_helper.rb +4 -0
  13. data/app/jobs/canvas_shim/application_job.rb +4 -0
  14. data/app/mailers/canvas_shim/application_mailer.rb +6 -0
  15. data/app/models/canvas_shim/application_record.rb +5 -0
  16. data/app/services/courses_service.rb +5 -0
  17. data/app/services/courses_service/commands/distribute_due_dates.rb +50 -0
  18. data/app/services/courses_service/commands/distribute_due_dates/scheduler.rb +70 -0
  19. data/app/services/grades_service.rb +33 -0
  20. data/app/services/grades_service/account.rb +13 -0
  21. data/app/services/grades_service/commands/zero_out_assignment_grades.rb +73 -0
  22. data/app/services/grades_service/commands/zero_out_assignment_grades_rollback.rb +38 -0
  23. data/app/services/pipeline_service.rb +27 -0
  24. data/app/services/pipeline_service/account.rb +9 -0
  25. data/app/services/pipeline_service/api/publish.rb +53 -0
  26. data/app/services/pipeline_service/commands/publish.rb +44 -0
  27. data/app/services/pipeline_service/commands/publish_events.rb +17 -0
  28. data/app/services/pipeline_service/endpoints/pipeline.rb +62 -0
  29. data/app/services/pipeline_service/endpoints/pipeline/message_builder.rb +75 -0
  30. data/app/services/pipeline_service/events/emitter.rb +56 -0
  31. data/app/services/pipeline_service/events/graded_out_event.rb +31 -0
  32. data/app/services/pipeline_service/events/http_client.rb +14 -0
  33. data/app/services/pipeline_service/events/responders/sis.rb +66 -0
  34. data/app/services/pipeline_service/events/subscription.rb +11 -0
  35. data/app/services/pipeline_service/http_client.rb +21 -0
  36. data/app/services/pipeline_service/logger.rb +39 -0
  37. data/app/services/pipeline_service/pipeline_client.rb +41 -0
  38. data/app/services/pipeline_service/serializers/assignment.rb +55 -0
  39. data/app/services/pipeline_service/serializers/base_methods.rb +33 -0
  40. data/app/services/pipeline_service/serializers/canvas_api_enrollment.rb +55 -0
  41. data/app/services/pipeline_service/serializers/enrollment.rb +31 -0
  42. data/app/services/pipeline_service/serializers/fetcher.rb +17 -0
  43. data/app/services/pipeline_service/serializers/submission.rb +30 -0
  44. data/app/services/pipeline_service/serializers/user.rb +31 -0
  45. data/app/services/settings_service.rb +51 -0
  46. data/app/services/settings_service/api_docs.yml +68 -0
  47. data/app/services/settings_service/assignment.rb +24 -0
  48. data/app/services/settings_service/assignment_repository.rb +89 -0
  49. data/app/services/settings_service/auth_middleware.rb +19 -0
  50. data/app/services/settings_service/auth_token.rb +21 -0
  51. data/app/services/settings_service/authenticator_stub.rb +9 -0
  52. data/app/services/settings_service/commands/get_enrollment_settings.rb +17 -0
  53. data/app/services/settings_service/commands/get_settings.rb +34 -0
  54. data/app/services/settings_service/commands/get_user_settings.rb +18 -0
  55. data/app/services/settings_service/commands/update_enrollment_setting.rb +22 -0
  56. data/app/services/settings_service/commands/update_settings.rb +38 -0
  57. data/app/services/settings_service/commands/update_user_setting.rb +21 -0
  58. data/app/services/settings_service/enrollment.rb +28 -0
  59. data/app/services/settings_service/queries/zero_grader_audit.rb +6 -0
  60. data/app/services/settings_service/repository.rb +93 -0
  61. data/app/services/settings_service/school.rb +26 -0
  62. data/app/services/settings_service/student_assignment.rb +24 -0
  63. data/app/services/settings_service/student_assignment_repository.rb +116 -0
  64. data/app/services/settings_service/submission.rb +24 -0
  65. data/app/services/settings_service/user.rb +28 -0
  66. data/app/views/layouts/canvas_shim/application.html.erb +14 -0
  67. data/config/initializers/hash.rb +17 -0
  68. data/config/initializers/string.rb +9 -0
  69. data/config/routes.rb +9 -0
  70. data/lib/canvas_shim.rb +5 -0
  71. data/lib/canvas_shim/engine.rb +12 -0
  72. data/lib/canvas_shim/version.rb +3 -0
  73. data/lib/tasks/canvas_shim_tasks.rake +7 -0
  74. metadata +214 -0
@@ -0,0 +1,11 @@
1
+ module PipelineService
2
+ module Events
3
+ class Subscription
4
+ attr_reader :responder, :event
5
+ def initialize(event:, responder: nil)
6
+ @responder = responder
7
+ @event = event
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ module PipelineService
2
+ class HTTPClient
3
+ include Singleton
4
+
5
+ def self.post(args)
6
+ instance.post(args)
7
+ end
8
+
9
+ def self.get(endpoint, args={})
10
+ instance.get(endpoint, args)
11
+ end
12
+
13
+ def post(args)
14
+ PipelinePublisher::MessagesApi.new.messages_post(*args)
15
+ end
16
+
17
+ def get(endpoint, args)
18
+ HTTParty.get(endpoint, args)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ module PipelineService
2
+ class Logger
3
+ HEADERS = {}
4
+ def initialize(message, args={})
5
+ @message = message
6
+ @args = args
7
+ configure_dependencies
8
+ end
9
+
10
+ def call
11
+ if PipelineService.perform_synchronously?
12
+ perform
13
+ else
14
+ queue.enqueue(self)
15
+ end
16
+ end
17
+
18
+ # This makes it possible for the instance to be run later by Delayed::Job
19
+ def perform
20
+ post
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :http_client, :message, :queue
26
+
27
+ def configure_dependencies
28
+ @queue = @args[:queue] || Delayed::Job
29
+ @http_client = @args[:http_client] || HTTParty
30
+ end
31
+
32
+ def post
33
+ http_client.post(
34
+ 'https://3wupzgqsoh.execute-api.us-west-2.amazonaws.com/prod',
35
+ body: message.to_json
36
+ )
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module PipelineService
2
+ # PipelineClient builds a pipeline message using the object
3
+ # posts it to the endpoint and logs the message that was sent.
4
+ #
5
+ # Accepts an ActiveRecord object or a hash. If using a hash, you must provide
6
+ # a noun as an optional parameter
7
+ #
8
+ # PipelineCient.new(object: Enrollment.last)
9
+ # PipelineCient.new(object: { data: { foo: 'bar' } }, noun: 'enrollment' )
10
+ class PipelineClient
11
+ def initialize(args)
12
+ @args = args
13
+ @object = args[:object]
14
+ configure_dependencies
15
+ end
16
+
17
+ def call
18
+ fetch_enrollment_from_hash
19
+ post
20
+ self
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :endpoint, :object
26
+
27
+ def configure_dependencies
28
+ @endpoint = @args[:endpoint] || Endpoints::Pipeline
29
+ end
30
+
31
+ # EVIL! I'm rewriting an arg. What could go wrong?
32
+ def fetch_enrollment_from_hash
33
+ return unless object.is_a?(Hash)
34
+ @args[:object] = Enrollment.find(object[:id])
35
+ end
36
+
37
+ def post
38
+ endpoint.new(@args).call
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,55 @@
1
+ module PipelineService
2
+ module Serializers
3
+ class Assignment
4
+ def initialize(object:)
5
+ @object = object
6
+ end
7
+
8
+ def call
9
+ fetch
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :object
15
+
16
+ def domain
17
+ ENV['CANVAS_DOMAIN']
18
+ end
19
+
20
+ def endpoint
21
+ [
22
+ protocol, domain, ':', port,
23
+ '/api/v1/courses/', course_id, '/assignments/', object.id
24
+ ].join('')
25
+ end
26
+
27
+ def use_ssl?
28
+ ENV['CANVAS_SSL'] == 'true'
29
+ end
30
+
31
+ def port
32
+ return '3000' if Rails.env == 'development'
33
+ return '443' if use_ssl?
34
+ '80'
35
+ end
36
+
37
+ def headers
38
+ { Authorization: "Bearer #{ENV['STRONGMIND_INTEGRATION_KEY']}" }
39
+ end
40
+
41
+ def fetch
42
+ PipelineService::HTTPClient.get(endpoint, headers: headers).parsed_response
43
+ end
44
+
45
+ def course_id
46
+ object.course.id
47
+ end
48
+
49
+ def protocol
50
+ return 'https://' if use_ssl?
51
+ 'http://'
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ module PipelineService
2
+ module Serializers
3
+ module BaseMethods
4
+ private
5
+
6
+ def protocol
7
+ ENV['CANVAS_SSL'] == 'true' ? 'https://' : 'http://'
8
+ end
9
+
10
+ def host
11
+ ENV['CANVAS_DOMAIN']
12
+ end
13
+
14
+ def port
15
+ 80
16
+ end
17
+
18
+ def host_with_port
19
+ "#{host}:#{port}"
20
+ end
21
+
22
+ def ssl?
23
+ ENV['CANVAS_SSL'] == 'true'
24
+ end
25
+
26
+ def request
27
+ Struct.new(:host_with_port, :ssl?, :protocol, :host, :port).new(
28
+ host_with_port, ssl?, protocol, host, port
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,55 @@
1
+ module PipelineService
2
+ module Serializers
3
+ class CanvasAPIEnrollment
4
+ def initialize(object:)
5
+ @object = object
6
+ end
7
+
8
+ def call
9
+ fetch
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :object
15
+
16
+ def domain
17
+ ENV['CANVAS_DOMAIN']
18
+ end
19
+
20
+ def endpoint
21
+ [
22
+ protocol, domain, ':', port,
23
+ '/api/v1/accounts/',account_id,'/enrollments/',object.id
24
+ ].join('')
25
+ end
26
+
27
+ def use_ssl?
28
+ ENV['CANVAS_SSL'] == 'true'
29
+ end
30
+
31
+ def port
32
+ return '3000' if Rails.env == 'development'
33
+ return '443' if use_ssl?
34
+ '80'
35
+ end
36
+
37
+ def headers
38
+ { Authorization: "Bearer #{ENV['STRONGMIND_INTEGRATION_KEY']}" }
39
+ end
40
+
41
+ def fetch
42
+ PipelineService::HTTPClient.get(endpoint, headers: headers).parsed_response
43
+ end
44
+
45
+ def account_id
46
+ object.root_account.id
47
+ end
48
+
49
+ def protocol
50
+ return 'https://' if use_ssl?
51
+ 'http://'
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ module PipelineService
2
+ # This ugly thing lets us call the canvas user api
3
+ module Serializers
4
+ class Enrollment
5
+ include ::Api::V1::User
6
+ include BaseMethods
7
+
8
+ attr_accessor :services_enabled, :context, :current_user, :params, :request
9
+
10
+ def service_enabled?(service); @services_enabled.include? service; end
11
+
12
+ def avatar_image_url(*args); "avatar_image_url(#{args.first})"; end
13
+
14
+ def course_student_grades_url(course_id, user_id); ""; end
15
+
16
+ def course_user_url(course_id, user_id); ""; end
17
+
18
+ def initialize(object:)
19
+ @domain_root_account = ::Account.default
20
+ @params = {}
21
+ @request = OpenStruct.new
22
+ @object = object
23
+ @admin = PipelineService::Account.account_admin
24
+ end
25
+
26
+ def call
27
+ enrollment_json(@object, @admin, {})
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module PipelineService
2
+ module Serializers
3
+ module Fetcher
4
+ # Will fetch a serializer with the same name as the object parameter.
5
+ # Raises a name error if the serializer doesn't exist.
6
+ # Maps any object with a class containing 'enrollment' to the enrollment serializer
7
+ def self.fetch(object:)
8
+ case object.class.to_s
9
+ when /Enrollment/
10
+ PipelineService::Serializers::Enrollment
11
+ else
12
+ "PipelineService::Serializers::#{object.class.to_s}".constantize
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module PipelineService
2
+ module Serializers
3
+ # This ugly thing lets us call the canvas user api
4
+ class Submission
5
+ include ::Api
6
+ include ::Api::V1::Submission
7
+ include ActionView::Helpers
8
+ include ActionDispatch::Routing::UrlFor
9
+ include Rails.application.routes.url_helpers
10
+ include BaseMethods
11
+
12
+ def params;{};end
13
+
14
+ def initialize(object:)
15
+ default_url_options[:host] = host
16
+ @object = object
17
+ @admin = PipelineService::Account.account_admin
18
+ end
19
+
20
+ def request
21
+ super
22
+ end
23
+
24
+ def call
25
+ @current_user = @admin
26
+ submission_json(@object, @object.assignment, @admin, {}, nil, [])
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module PipelineService
2
+ module Serializers
3
+ # This ugly thing lets us call the canvas user api
4
+ class User
5
+ include ::Api::V1::User
6
+ include BaseMethods
7
+
8
+ attr_accessor :services_enabled, :context, :current_user, :params, :request
9
+
10
+ def service_enabled?(service); @services_enabled.include? service; end
11
+
12
+ def avatar_image_url(*args); "avatar_image_url(#{args.first})"; end
13
+
14
+ def course_student_grades_url(course_id, user_id); ""; end
15
+
16
+ def course_user_url(course_id, user_id); ""; end
17
+
18
+ def initialize(object:)
19
+ @domain_root_account = ::Account.default
20
+ @params = {}
21
+ @request = OpenStruct.new
22
+ @object = object
23
+ @admin = PipelineService::Account.account_admin
24
+ end
25
+
26
+ def call
27
+ user_json(@object, @admin, {})
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,51 @@
1
+ # Usage:
2
+ # SettingsService.update_enrollment_setting(id: 1, setting: 'foo', value: 'bar')
3
+ module SettingsService
4
+ cattr_writer :canvas_domain
5
+
6
+ def self.update_settings(id:, setting:, value:, object:)
7
+ Commands::UpdateSettings.new(
8
+ id: id,
9
+ setting: setting,
10
+ value: value,
11
+ object: object
12
+ ).call
13
+ end
14
+
15
+ def self.update_enrollment_setting(id:, setting:, value:)
16
+ Commands::UpdateEnrollmentSetting.new(
17
+ id: id,
18
+ setting: setting,
19
+ value: value
20
+ ).call
21
+ end
22
+
23
+ def self.get_enrollment_settings(id:)
24
+ Commands::GetEnrollmentSettings.new(id: id).call
25
+ end
26
+
27
+ def self.get_settings(id:, object:)
28
+ Commands::GetSettings.new(id: id, object: object).call
29
+ end
30
+
31
+ def self.update_user_setting(id:, setting:, value:)
32
+ Commands::UpdateUserSetting.new(
33
+ id: id,
34
+ setting: setting,
35
+ value: value
36
+ ).call
37
+ end
38
+
39
+ def self.get_user_settings(id:)
40
+ Commands::GetUserSettings.new(id: id).call
41
+ end
42
+
43
+ def self.canvas_domain
44
+ @@canvas_domain || ENV['CANVAS_DOMAIN']
45
+ end
46
+
47
+ def self.query
48
+ Queries::ZeroGraderAudit.run
49
+ end
50
+
51
+ end
@@ -0,0 +1,68 @@
1
+ swagger: '2.0'
2
+ info:
3
+ description: This is User Settings
4
+ version: '1.0'
5
+ title: User Settings
6
+ contact:
7
+ email: devemail@example.com
8
+ host: localhost:3001
9
+ basePath: "/canvas_shim/settings_api/v1"
10
+ tags:
11
+ - name: user_setting
12
+ description: User Settings
13
+ schemes:
14
+ - http
15
+ paths:
16
+ "/users/{id}":
17
+ put:
18
+ tags:
19
+ - user_setting
20
+ summary: Adjust a setting on a user
21
+ description: ''
22
+ operationId: addUserSetting
23
+ consumes:
24
+ - application/json
25
+ produces:
26
+ - application/json
27
+ parameters:
28
+ - in: body
29
+ name: body
30
+ description: Settings to be stored on a user
31
+ required: true
32
+ schema:
33
+ "$ref": "#/definitions/User"
34
+ - name: id
35
+ in: path
36
+ description: ID of the user to apply the setting to
37
+ required: true
38
+ type: integer
39
+ format: int64
40
+ responses:
41
+ '405':
42
+ description: Invalid input
43
+ securityDefinitions:
44
+ api_key:
45
+ type: apiKey
46
+ name: api_key
47
+ in: header
48
+ definitions:
49
+ User:
50
+ type: object
51
+ properties:
52
+ accomodations:
53
+ type: array
54
+ items:
55
+ "$ref": "#/definitions/AccomodationTypes"
56
+ AccomodationTypes:
57
+ type: string
58
+ example: cat
59
+ ApiResponse:
60
+ type: object
61
+ properties:
62
+ code:
63
+ type: integer
64
+ format: int32
65
+ type:
66
+ type: string
67
+ message:
68
+ type: string