canvas_sync 0.16.4 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (260) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +235 -151
  3. data/app/controllers/{api → canvas_sync/api}/v1/health_check_controller.rb +1 -1
  4. data/app/controllers/canvas_sync/api/v1/live_events_controller.rb +122 -0
  5. data/app/models/canvas_sync/sync_batch.rb +5 -0
  6. data/config/initializers/apartment.rb +10 -1
  7. data/config/routes.rb +7 -0
  8. data/db/migrate/20170915210836_create_canvas_sync_job_log.rb +12 -31
  9. data/db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb +4 -13
  10. data/db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb +3 -11
  11. data/db/migrate/20201018210836_create_canvas_sync_sync_batches.rb +11 -0
  12. data/db/migrate/20201030210836_add_full_sync_to_canvas_sync_sync_batch.rb +7 -0
  13. data/lib/canvas_sync/batch_processor.rb +41 -0
  14. data/lib/canvas_sync/concerns/ability_helper.rb +72 -0
  15. data/lib/canvas_sync/concerns/account/ancestry.rb +2 -0
  16. data/lib/canvas_sync/concerns/account/base.rb +15 -0
  17. data/lib/canvas_sync/concerns/api_syncable.rb +17 -10
  18. data/lib/canvas_sync/concerns/live_event_sync.rb +46 -0
  19. data/lib/canvas_sync/concerns/role/base.rb +57 -0
  20. data/lib/canvas_sync/concerns/sync_mapping.rb +120 -0
  21. data/lib/canvas_sync/engine.rb +80 -0
  22. data/lib/canvas_sync/generators/install_generator.rb +1 -0
  23. data/lib/canvas_sync/generators/install_live_events_generator.rb +0 -1
  24. data/lib/canvas_sync/generators/templates/migrations/create_content_migrations.rb +24 -0
  25. data/lib/canvas_sync/generators/templates/migrations/create_course_nicknames.rb +17 -0
  26. data/lib/canvas_sync/generators/templates/migrations/create_grading_period_groups.rb +18 -0
  27. data/lib/canvas_sync/generators/templates/migrations/create_grading_periods.rb +22 -0
  28. data/lib/canvas_sync/generators/templates/migrations/create_learning_outcome_results.rb +46 -0
  29. data/lib/canvas_sync/generators/templates/migrations/create_learning_outcomes.rb +30 -0
  30. data/lib/canvas_sync/generators/templates/migrations/create_rubric_assessments.rb +31 -0
  31. data/lib/canvas_sync/generators/templates/migrations/create_rubric_associations.rb +36 -0
  32. data/lib/canvas_sync/generators/templates/migrations/create_rubrics.rb +38 -0
  33. data/lib/canvas_sync/generators/templates/migrations/create_user_observers.rb +17 -0
  34. data/lib/canvas_sync/generators/templates/migrations/create_users.rb +0 -1
  35. data/lib/canvas_sync/generators/templates/models/account.rb +3 -0
  36. data/lib/canvas_sync/generators/templates/models/admin.rb +2 -0
  37. data/lib/canvas_sync/generators/templates/models/assignment.rb +3 -0
  38. data/lib/canvas_sync/generators/templates/models/assignment_group.rb +3 -0
  39. data/lib/canvas_sync/generators/templates/models/content_migration.rb +12 -0
  40. data/lib/canvas_sync/generators/templates/models/context_module.rb +3 -0
  41. data/lib/canvas_sync/generators/templates/models/context_module_item.rb +3 -0
  42. data/lib/canvas_sync/generators/templates/models/course.rb +11 -0
  43. data/lib/canvas_sync/generators/templates/models/course_nickname.rb +13 -0
  44. data/lib/canvas_sync/generators/templates/models/enrollment.rb +14 -0
  45. data/lib/canvas_sync/generators/templates/models/grading_period.rb +10 -0
  46. data/lib/canvas_sync/generators/templates/models/grading_period_group.rb +9 -0
  47. data/lib/canvas_sync/generators/templates/models/group.rb +2 -0
  48. data/lib/canvas_sync/generators/templates/models/group_membership.rb +2 -0
  49. data/lib/canvas_sync/generators/templates/models/learning_outcome.rb +24 -0
  50. data/lib/canvas_sync/generators/templates/models/learning_outcome_result.rb +48 -0
  51. data/lib/canvas_sync/generators/templates/models/pseudonym.rb +2 -0
  52. data/lib/canvas_sync/generators/templates/models/role.rb +2 -0
  53. data/lib/canvas_sync/generators/templates/models/rubric.rb +29 -0
  54. data/lib/canvas_sync/generators/templates/models/rubric_assessment.rb +17 -0
  55. data/lib/canvas_sync/generators/templates/models/rubric_association.rb +14 -0
  56. data/lib/canvas_sync/generators/templates/models/section.rb +9 -0
  57. data/lib/canvas_sync/generators/templates/models/submission.rb +3 -0
  58. data/lib/canvas_sync/generators/templates/models/term.rb +3 -0
  59. data/lib/canvas_sync/generators/templates/models/user.rb +11 -0
  60. data/lib/canvas_sync/generators/templates/models/user_observer.rb +13 -0
  61. data/lib/canvas_sync/generators/templates/services/live_events/assignment_event.rb +1 -1
  62. data/lib/canvas_sync/generators/templates/services/live_events/assignment_group_event.rb +1 -1
  63. data/lib/canvas_sync/generators/templates/services/live_events/course_event.rb +1 -3
  64. data/lib/canvas_sync/generators/templates/services/live_events/course_section_event.rb +1 -1
  65. data/lib/canvas_sync/generators/templates/services/live_events/enrollment_event.rb +1 -1
  66. data/lib/canvas_sync/generators/templates/services/live_events/grade_event.rb +1 -1
  67. data/lib/canvas_sync/generators/templates/services/live_events/module_event.rb +1 -1
  68. data/lib/canvas_sync/generators/templates/services/live_events/module_item_event.rb +1 -1
  69. data/lib/canvas_sync/generators/templates/services/live_events/submission_event.rb +1 -1
  70. data/lib/canvas_sync/generators/templates/services/live_events/syllabus_event.rb +1 -1
  71. data/lib/canvas_sync/generators/templates/services/live_events/user_event.rb +1 -3
  72. data/lib/canvas_sync/importers/bulk_importer.rb +138 -31
  73. data/lib/canvas_sync/job.rb +7 -5
  74. data/lib/canvas_sync/job_batches/active_job.rb +108 -0
  75. data/lib/canvas_sync/job_batches/batch.rb +543 -0
  76. data/lib/canvas_sync/job_batches/callback.rb +149 -0
  77. data/lib/canvas_sync/job_batches/chain_builder.rb +249 -0
  78. data/lib/canvas_sync/job_batches/context_hash.rb +159 -0
  79. data/lib/canvas_sync/job_batches/hier_batch_ids.lua +25 -0
  80. data/lib/canvas_sync/job_batches/jobs/base_job.rb +7 -0
  81. data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +22 -0
  82. data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +170 -0
  83. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +22 -0
  84. data/lib/canvas_sync/job_batches/pool.rb +245 -0
  85. data/lib/canvas_sync/job_batches/pool_refill.lua +47 -0
  86. data/lib/canvas_sync/job_batches/redis_model.rb +69 -0
  87. data/lib/canvas_sync/job_batches/redis_script.rb +163 -0
  88. data/lib/canvas_sync/job_batches/schedule_callback.lua +14 -0
  89. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/css/styles.less +182 -0
  90. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/js/batch_tree.js +108 -0
  91. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/js/util.js +2 -0
  92. data/lib/canvas_sync/job_batches/sidekiq/web/helpers.rb +41 -0
  93. data/lib/canvas_sync/job_batches/sidekiq/web/views/_batch_tree.erb +6 -0
  94. data/lib/canvas_sync/job_batches/sidekiq/web/views/_batches_table.erb +44 -0
  95. data/lib/canvas_sync/job_batches/sidekiq/web/views/_common.erb +13 -0
  96. data/lib/canvas_sync/job_batches/sidekiq/web/views/_jobs_table.erb +21 -0
  97. data/lib/canvas_sync/job_batches/sidekiq/web/views/_pagination.erb +26 -0
  98. data/lib/canvas_sync/job_batches/sidekiq/web/views/batch.erb +81 -0
  99. data/lib/canvas_sync/job_batches/sidekiq/web/views/batches.erb +23 -0
  100. data/lib/canvas_sync/job_batches/sidekiq/web/views/pool.erb +137 -0
  101. data/lib/canvas_sync/job_batches/sidekiq/web/views/pools.erb +47 -0
  102. data/lib/canvas_sync/job_batches/sidekiq/web.rb +218 -0
  103. data/lib/canvas_sync/job_batches/sidekiq.rb +136 -0
  104. data/lib/canvas_sync/job_batches/status.rb +91 -0
  105. data/lib/canvas_sync/jobs/begin_sync_chain_job.rb +99 -0
  106. data/lib/canvas_sync/jobs/canvas_process_waiter.rb +41 -0
  107. data/lib/canvas_sync/jobs/report_checker.rb +70 -8
  108. data/lib/canvas_sync/jobs/report_processor_job.rb +4 -7
  109. data/lib/canvas_sync/jobs/report_starter.rb +34 -20
  110. data/lib/canvas_sync/jobs/sync_accounts_job.rb +3 -5
  111. data/lib/canvas_sync/jobs/sync_admins_job.rb +2 -4
  112. data/lib/canvas_sync/jobs/sync_assignment_groups_job.rb +2 -4
  113. data/lib/canvas_sync/jobs/sync_assignments_job.rb +2 -4
  114. data/lib/canvas_sync/jobs/sync_content_migrations_job.rb +20 -0
  115. data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +2 -4
  116. data/lib/canvas_sync/jobs/sync_context_modules_job.rb +2 -4
  117. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +16 -50
  118. data/lib/canvas_sync/jobs/sync_roles_job.rb +2 -5
  119. data/lib/canvas_sync/jobs/sync_rubric_assessments_job.rb +15 -0
  120. data/lib/canvas_sync/jobs/sync_rubric_associations_job.rb +15 -0
  121. data/lib/canvas_sync/jobs/sync_rubrics_job.rb +15 -0
  122. data/lib/canvas_sync/jobs/sync_simple_table_job.rb +11 -32
  123. data/lib/canvas_sync/jobs/sync_submissions_job.rb +6 -4
  124. data/lib/canvas_sync/jobs/sync_terms_job.rb +9 -8
  125. data/lib/canvas_sync/jobs/term_batches_job.rb +50 -0
  126. data/lib/canvas_sync/{generators/templates/services/live_events/base_event.rb → live_events/base_handler.rb} +6 -10
  127. data/lib/canvas_sync/live_events/process_event_job.rb +26 -0
  128. data/lib/canvas_sync/live_events.rb +38 -0
  129. data/lib/canvas_sync/misc_helper.rb +63 -0
  130. data/lib/canvas_sync/processors/assignment_groups_processor.rb +3 -8
  131. data/lib/canvas_sync/processors/assignments_processor.rb +3 -8
  132. data/lib/canvas_sync/processors/content_migrations_processor.rb +19 -0
  133. data/lib/canvas_sync/processors/context_module_items_processor.rb +3 -8
  134. data/lib/canvas_sync/processors/context_modules_processor.rb +3 -8
  135. data/lib/canvas_sync/processors/model_mappings.yml +420 -0
  136. data/lib/canvas_sync/processors/normal_processor.rb +3 -3
  137. data/lib/canvas_sync/processors/provisioning_report_processor.rb +42 -55
  138. data/lib/canvas_sync/processors/report_processor.rb +15 -9
  139. data/lib/canvas_sync/processors/rubric_assessments_processor.rb +19 -0
  140. data/lib/canvas_sync/processors/rubric_associations_processor.rb +19 -0
  141. data/lib/canvas_sync/processors/rubrics_processor.rb +19 -0
  142. data/lib/canvas_sync/processors/submissions_processor.rb +3 -8
  143. data/lib/canvas_sync/record.rb +103 -0
  144. data/lib/canvas_sync/version.rb +1 -1
  145. data/lib/canvas_sync.rb +124 -125
  146. data/spec/canvas_sync/canvas_sync_spec.rb +224 -155
  147. data/spec/canvas_sync/jobs/canvas_process_waiter_spec.rb +34 -0
  148. data/spec/canvas_sync/jobs/job_spec.rb +9 -17
  149. data/spec/canvas_sync/jobs/report_checker_spec.rb +1 -3
  150. data/spec/canvas_sync/jobs/report_processor_job_spec.rb +0 -3
  151. data/spec/canvas_sync/jobs/report_starter_spec.rb +19 -28
  152. data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +1 -4
  153. data/spec/canvas_sync/jobs/sync_assignment_groups_job_spec.rb +2 -1
  154. data/spec/canvas_sync/jobs/sync_assignments_job_spec.rb +3 -2
  155. data/spec/canvas_sync/jobs/sync_content_migrations_job_spec.rb +30 -0
  156. data/spec/canvas_sync/jobs/sync_context_module_items_job_spec.rb +3 -2
  157. data/spec/canvas_sync/jobs/sync_context_modules_job_spec.rb +3 -2
  158. data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +7 -41
  159. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +1 -4
  160. data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +5 -12
  161. data/spec/canvas_sync/jobs/sync_submissions_job_spec.rb +8 -2
  162. data/spec/canvas_sync/jobs/sync_terms_job_spec.rb +1 -4
  163. data/spec/canvas_sync/live_events/live_event_sync_spec.rb +27 -0
  164. data/spec/canvas_sync/live_events/live_events_controller_spec.rb +54 -0
  165. data/spec/canvas_sync/live_events/process_event_job_spec.rb +38 -0
  166. data/spec/canvas_sync/misc_helper_spec.rb +58 -0
  167. data/spec/canvas_sync/models/assignment_spec.rb +1 -1
  168. data/spec/canvas_sync/processors/content_migrations_processor_spec.rb +13 -0
  169. data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +101 -1
  170. data/spec/canvas_sync/processors/rubric_assessments_spec.rb +16 -0
  171. data/spec/canvas_sync/processors/rubric_associations_spec.rb +16 -0
  172. data/spec/canvas_sync/processors/rubrics_processor_spec.rb +17 -0
  173. data/spec/dummy/app/models/account.rb +6 -0
  174. data/spec/dummy/app/models/admin.rb +2 -0
  175. data/spec/dummy/app/models/assignment.rb +3 -0
  176. data/spec/dummy/app/models/assignment_group.rb +3 -0
  177. data/spec/dummy/app/models/content_migration.rb +18 -0
  178. data/spec/dummy/app/models/context_module.rb +3 -0
  179. data/spec/dummy/app/models/context_module_item.rb +3 -0
  180. data/spec/dummy/app/models/course.rb +11 -0
  181. data/spec/dummy/app/models/course_nickname.rb +19 -0
  182. data/spec/dummy/app/models/enrollment.rb +14 -0
  183. data/spec/dummy/app/models/grading_period.rb +16 -0
  184. data/spec/dummy/app/models/grading_period_group.rb +15 -0
  185. data/spec/dummy/app/models/group.rb +2 -0
  186. data/spec/dummy/app/models/group_membership.rb +2 -0
  187. data/spec/dummy/app/models/learning_outcome.rb +30 -0
  188. data/spec/dummy/app/models/learning_outcome_result.rb +54 -0
  189. data/spec/dummy/app/models/pseudonym.rb +16 -0
  190. data/spec/dummy/app/models/role.rb +2 -0
  191. data/spec/dummy/app/models/rubric.rb +35 -0
  192. data/spec/dummy/app/models/rubric_assessment.rb +22 -0
  193. data/spec/dummy/app/models/rubric_association.rb +20 -0
  194. data/spec/dummy/app/models/section.rb +9 -0
  195. data/spec/dummy/app/models/submission.rb +4 -0
  196. data/spec/dummy/app/models/term.rb +3 -0
  197. data/spec/dummy/app/models/user.rb +11 -0
  198. data/spec/dummy/app/models/user_observer.rb +19 -0
  199. data/spec/dummy/app/services/live_events/assignment_event.rb +1 -1
  200. data/spec/dummy/app/services/live_events/course_event.rb +1 -3
  201. data/spec/dummy/app/services/live_events/course_section_event.rb +1 -1
  202. data/spec/dummy/app/services/live_events/enrollment_event.rb +1 -1
  203. data/spec/dummy/app/services/live_events/grade_event.rb +1 -1
  204. data/spec/dummy/app/services/live_events/module_event.rb +1 -1
  205. data/spec/dummy/app/services/live_events/module_item_event.rb +1 -1
  206. data/spec/dummy/app/services/live_events/submission_event.rb +1 -1
  207. data/spec/dummy/app/services/live_events/syllabus_event.rb +1 -1
  208. data/spec/dummy/app/services/live_events/user_event.rb +1 -3
  209. data/spec/dummy/config/environments/test.rb +2 -0
  210. data/spec/dummy/config/routes.rb +1 -0
  211. data/spec/dummy/db/migrate/20201016181346_create_pseudonyms.rb +24 -0
  212. data/spec/dummy/db/migrate/20210907233329_create_user_observers.rb +23 -0
  213. data/spec/dummy/db/migrate/20210907233330_create_grading_periods.rb +28 -0
  214. data/spec/dummy/db/migrate/20211001184920_create_grading_period_groups.rb +24 -0
  215. data/spec/dummy/db/migrate/20220308072643_create_content_migrations.rb +30 -0
  216. data/spec/dummy/db/migrate/20220712210559_create_learning_outcomes.rb +36 -0
  217. data/spec/dummy/db/migrate/{20190702203620_create_users.rb → 20220926221926_create_users.rb} +0 -1
  218. data/spec/dummy/db/migrate/20240408223326_create_course_nicknames.rb +23 -0
  219. data/spec/dummy/db/migrate/20240509105100_create_rubrics.rb +44 -0
  220. data/spec/dummy/db/migrate/20240510094100_create_rubric_associations.rb +42 -0
  221. data/spec/dummy/db/migrate/20240510101100_create_rubric_assessments.rb +37 -0
  222. data/spec/dummy/db/migrate/20240523101010_create_learning_outcome_results.rb +52 -0
  223. data/spec/dummy/db/schema.rb +244 -5
  224. data/spec/factories/user_factory.rb +0 -1
  225. data/spec/job_batching/active_job_spec.rb +107 -0
  226. data/spec/job_batching/batch_spec.rb +489 -0
  227. data/spec/job_batching/callback_spec.rb +38 -0
  228. data/spec/job_batching/context_hash_spec.rb +54 -0
  229. data/spec/job_batching/flow_spec.rb +82 -0
  230. data/spec/job_batching/integration/fail_then_succeed.rb +42 -0
  231. data/spec/job_batching/integration/integration.rb +57 -0
  232. data/spec/job_batching/integration/nested.rb +88 -0
  233. data/spec/job_batching/integration/simple.rb +47 -0
  234. data/spec/job_batching/integration/workflow.rb +134 -0
  235. data/spec/job_batching/integration_helper.rb +50 -0
  236. data/spec/job_batching/pool_spec.rb +161 -0
  237. data/spec/job_batching/sidekiq_spec.rb +125 -0
  238. data/spec/job_batching/status_spec.rb +76 -0
  239. data/spec/job_batching/support/base_job.rb +14 -0
  240. data/spec/job_batching/support/sample_callback.rb +2 -0
  241. data/spec/spec_helper.rb +17 -0
  242. data/spec/support/fixtures/reports/content_migrations.csv +3 -0
  243. data/spec/support/fixtures/reports/course_nicknames.csv +3 -0
  244. data/spec/support/fixtures/reports/grading_period_groups.csv +2 -0
  245. data/spec/support/fixtures/reports/grading_periods.csv +3 -0
  246. data/spec/support/fixtures/reports/learning_outcome_results.csv +3 -0
  247. data/spec/support/fixtures/reports/learning_outcomes.csv +3 -0
  248. data/spec/support/fixtures/reports/provisioning_csv_unzipped/courses.csv +3 -0
  249. data/spec/support/fixtures/reports/provisioning_csv_unzipped/users.csv +4 -0
  250. data/spec/support/fixtures/reports/rubric_assessments.csv +3 -0
  251. data/spec/support/fixtures/reports/rubric_associations.csv +3 -0
  252. data/spec/support/fixtures/reports/rubrics.csv +3 -0
  253. data/spec/support/fixtures/reports/user_observers.csv +3 -0
  254. data/spec/support/fixtures/reports/users.csv +3 -2
  255. data/spec/support/fixtures/reports/xlist.csv +1 -1
  256. metadata +329 -27
  257. data/app/controllers/api/v1/live_events_controller.rb +0 -18
  258. data/lib/canvas_sync/job_chain.rb +0 -57
  259. data/lib/canvas_sync/jobs/fork_gather.rb +0 -59
  260. data/spec/canvas_sync/jobs/fork_gather_spec.rb +0 -73
@@ -3,13 +3,12 @@ require 'spec_helper'
3
3
  RSpec.describe CanvasSync do
4
4
  describe '.provisioning_sync' do
5
5
  it 'invokes the first job in the queue and passes on the rest of the job chain' do
6
- expected_job_chain = CanvasSync.default_provisioning_report_chain(['courses'], nil).chain_data
7
- first_job = expected_job_chain[:jobs].shift
6
+ expected_job_chain = CanvasSync.default_provisioning_report_chain(['courses'], term_scope: nil).normalize![:args][0]
8
7
 
9
- expect(CanvasSync::Jobs::SyncTermsJob).to receive(:perform_later)
8
+ expect(CanvasSync::Jobs::BeginSyncChainJob).to receive(:perform_later)
10
9
  .with(
11
10
  expected_job_chain,
12
- first_job[:options]
11
+ anything
13
12
  )
14
13
 
15
14
  CanvasSync.provisioning_sync(['courses'])
@@ -25,32 +24,68 @@ RSpec.describe CanvasSync do
25
24
 
26
25
  describe '.default_provisioning_report_chain' do
27
26
  it 'splits an options: Hash into options for separate models' do
28
- chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'], :active, options: {
27
+ chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'], term_scope: :active, options: {
29
28
  terms: { a: 1 },
30
29
  users: { b: 2 },
31
30
  provisioning: { c: 3 },
32
31
  global: { d: 4 },
33
32
  })
34
- expect(chain.chain_data).to eq({
35
- jobs: [
36
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: { a: 1 } },
37
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { models: ['users'], b: 2 } },
38
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: 'active', models: ['courses'], c: 3 } }
33
+ expect(chain.normalize!).to eq({
34
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
35
+ :args => [
36
+ [
37
+ {
38
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
39
+ :args=>[
40
+ [
41
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["users"], :b=>2}},
42
+ {
43
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
44
+ :args=>[],
45
+ :kwargs=>{
46
+ :term_scope=>"active",
47
+ :sub_jobs=>[
48
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["courses"], :c=>3}},
49
+ ],
50
+ },
51
+ },
52
+ ],
53
+ ],
54
+ :kwargs=>{},
55
+ },
56
+ ],
39
57
  ],
40
- global_options: { legacy_support: false, d: 4 }
58
+ :kwargs => {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil, :d=>4},
41
59
  })
42
60
  end
43
61
 
44
62
  context 'we are syncing users with a term scope' do
45
63
  it 'syncs the users in a separate job that runs first' do
46
- chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'], :active)
47
- expect(chain.chain_data).to eq({
48
- jobs: [
49
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
50
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { models: ['users'] } },
51
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: 'active', models: ['courses'] } }
52
- ],
53
- global_options: { legacy_support: false }
64
+ chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'], term_scope: :active)
65
+ expect(chain.normalize!).to eq({
66
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
67
+ :args => [[
68
+ {
69
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
70
+ :args=>[
71
+ [
72
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["users"]}},
73
+ {
74
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
75
+ :args=>[],
76
+ :kwargs=>{
77
+ :sub_jobs=>[
78
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["courses"]}}
79
+ ],
80
+ :term_scope=>"active",
81
+ }
82
+ }
83
+ ]
84
+ ],
85
+ :kwargs=>{},
86
+ }
87
+ ]],
88
+ :kwargs => {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil},
54
89
  })
55
90
  end
56
91
  end
@@ -58,26 +93,52 @@ RSpec.describe CanvasSync do
58
93
  context 'we are syncing users without a term scope' do
59
94
  it 'syncs users along with the rest of the provisioning report' do
60
95
  chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'])
61
- expect(chain.chain_data).to eq({
62
- jobs: [
63
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
64
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: ['users', 'courses'] } }
65
- ],
66
- global_options: { legacy_support: false }
96
+ expect(chain.normalize!).to eq({
97
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
98
+ :args => [[
99
+ {
100
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
101
+ :args=>[[
102
+ {
103
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
104
+ :args=>[],
105
+ :kwargs=>{
106
+ :sub_jobs=>[{:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["users", "courses"]}}],
107
+ :term_scope=>nil,
108
+ }
109
+ }
110
+ ]],
111
+ :kwargs=>{}
112
+ }
113
+ ]],
114
+ :kwargs => {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil},
67
115
  })
68
116
  end
69
117
  end
70
118
 
71
119
  context 'we are syncing roles with a term scope' do
72
120
  it 'syncs the roles in a separate job that runs first' do
73
- chain = CanvasSync.default_provisioning_report_chain(['roles', 'courses'], :active)
74
- expect(chain.chain_data).to eq({
75
- jobs: [
76
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
77
- { job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {} },
78
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: 'active', models: ['courses'] } }
79
- ],
80
- global_options: { legacy_support: false }
121
+ chain = CanvasSync.default_provisioning_report_chain(['roles', 'courses'], term_scope: :active)
122
+ expect(chain.normalize!).to eq({
123
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
124
+ :args => [[
125
+ {
126
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
127
+ :args=>[[
128
+ {:job=>"CanvasSync::Jobs::SyncRolesJob", :options=>{}},
129
+ {
130
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
131
+ :args=>[],
132
+ :kwargs=>{
133
+ :sub_jobs=>[{:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["courses"]}}],
134
+ :term_scope=>"active",
135
+ }
136
+ },
137
+ ]],
138
+ :kwargs=>{}
139
+ }
140
+ ]],
141
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
81
142
  })
82
143
  end
83
144
  end
@@ -85,27 +146,53 @@ RSpec.describe CanvasSync do
85
146
  context 'we are syncing roles without a term scope' do
86
147
  it 'syncs roles separately even with no term scope' do
87
148
  chain = CanvasSync.default_provisioning_report_chain(['roles', 'courses'])
88
- expect(chain.chain_data).to eq({
89
- jobs: [
90
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
91
- { job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {} },
92
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: ['courses'] } }
93
- ],
94
- global_options: { legacy_support: false }
149
+ expect(chain.normalize!).to eq({
150
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
151
+ :args => [[
152
+ {
153
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
154
+ :args=>[[
155
+ {:job=>"CanvasSync::Jobs::SyncRolesJob", :options=>{}},
156
+ {
157
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
158
+ :args=>[],
159
+ :kwargs=>{
160
+ :sub_jobs=>[{:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["courses"]}}],
161
+ :term_scope=>nil,
162
+ }
163
+ }
164
+ ]],
165
+ :kwargs=>{}
166
+ }
167
+ ]],
168
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
95
169
  })
96
170
  end
97
171
  end
98
172
 
99
173
  context 'we are syncing admins with a term scope' do
100
174
  it 'syncs the admins in a separate job that runs first' do
101
- chain = CanvasSync.default_provisioning_report_chain(['admins', 'courses'], :active)
102
- expect(chain.chain_data).to eq({
103
- jobs: [
104
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
105
- { job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {} },
106
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: 'active', models: ['courses'] } }
107
- ],
108
- global_options: { legacy_support: false }
175
+ chain = CanvasSync.default_provisioning_report_chain(['admins', 'courses'], term_scope: :active)
176
+ expect(chain.normalize!).to eq({
177
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
178
+ :args => [[
179
+ {
180
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
181
+ :args=>[[
182
+ {:job=>"CanvasSync::Jobs::SyncAdminsJob", :options=>{}},
183
+ {
184
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
185
+ :args=>[],
186
+ :kwargs=>{
187
+ :sub_jobs=>[{:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["courses"]}}],
188
+ :term_scope=>"active"
189
+ }
190
+ }
191
+ ]],
192
+ :kwargs=>{}
193
+ }
194
+ ]],
195
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
109
196
  })
110
197
  end
111
198
  end
@@ -113,13 +200,26 @@ RSpec.describe CanvasSync do
113
200
  context 'we are syncing admins without a term scope' do
114
201
  it 'syncs admins separately even with no term scope' do
115
202
  chain = CanvasSync.default_provisioning_report_chain(['admins', 'courses'])
116
- expect(chain.chain_data).to eq({
117
- jobs: [
118
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
119
- { job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {} },
120
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: ['courses'] } }
121
- ],
122
- global_options: { legacy_support: false }
203
+ expect(chain.normalize!).to eq({
204
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
205
+ :args => [[
206
+ {
207
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
208
+ :args=>[[
209
+ {:job=>"CanvasSync::Jobs::SyncAdminsJob", :options=>{}},
210
+ {
211
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
212
+ :args=>[],
213
+ :kwargs=>{
214
+ :sub_jobs=>[{:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["courses"]}}],
215
+ :term_scope=>nil
216
+ }
217
+ }
218
+ ]],
219
+ :kwargs=>{}
220
+ }
221
+ ]],
222
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
123
223
  })
124
224
  end
125
225
  end
@@ -128,14 +228,29 @@ RSpec.describe CanvasSync do
128
228
  it "appends the SyncAssignmentsJob" do
129
229
  chain = CanvasSync.default_provisioning_report_chain(%w[users enrollments assignments])
130
230
 
131
- expect(chain.chain_data).to eq(
132
- jobs: [
133
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
134
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
135
- { job: CanvasSync::Jobs::SyncAssignmentsJob.to_s, options: {} },
136
- ],
137
- global_options: { legacy_support: false },
138
- )
231
+ expect(chain.normalize!).to eq({
232
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
233
+ :args => [[
234
+ {
235
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
236
+ :args=>[[
237
+ {
238
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
239
+ :args=>[],
240
+ :kwargs=>{
241
+ :sub_jobs=>[
242
+ {:job=>"CanvasSync::Jobs::SyncAssignmentsJob", :options=>{}},
243
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["users", "enrollments"]}}
244
+ ],
245
+ :term_scope=>nil
246
+ }
247
+ }
248
+ ]],
249
+ :kwargs=>{}
250
+ }
251
+ ]],
252
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
253
+ })
139
254
  end
140
255
  end
141
256
 
@@ -143,14 +258,29 @@ RSpec.describe CanvasSync do
143
258
  it "appends the SyncSubmissionsJob" do
144
259
  chain = CanvasSync.default_provisioning_report_chain(%w[users enrollments submissions])
145
260
 
146
- expect(chain.chain_data).to eq(
147
- jobs: [
148
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
149
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
150
- { job: CanvasSync::Jobs::SyncSubmissionsJob.to_s, options: {} },
151
- ],
152
- global_options: { legacy_support: false },
153
- )
261
+ expect(chain.normalize!).to eq({
262
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
263
+ :args => [[
264
+ {
265
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
266
+ :args=>[[
267
+ {
268
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
269
+ :args=>[],
270
+ :kwargs=>{
271
+ :sub_jobs=>[
272
+ {:job=>"CanvasSync::Jobs::SyncSubmissionsJob", :options=>{}},
273
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["users", "enrollments"]}}
274
+ ],
275
+ :term_scope=>nil
276
+ }
277
+ }
278
+ ]],
279
+ :kwargs=>{}
280
+ }
281
+ ]],
282
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
283
+ })
154
284
  end
155
285
  end
156
286
 
@@ -158,95 +288,34 @@ RSpec.describe CanvasSync do
158
288
  it "appends the SyncAssignmentGroupsJob" do
159
289
  chain = CanvasSync.default_provisioning_report_chain(%w[users enrollments assignment_groups])
160
290
 
161
- expect(chain.chain_data).to eq(
162
- jobs: [
163
- { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
164
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
165
- { job: CanvasSync::Jobs::SyncAssignmentGroupsJob.to_s, options: {} },
166
- ],
167
- global_options: { legacy_support: false },
168
- )
291
+ expect(chain.normalize!).to eq({
292
+ :job => "CanvasSync::Jobs::BeginSyncChainJob",
293
+ :args => [[
294
+ {
295
+ :job=>"CanvasSync::JobBatches::ConcurrentBatchJob",
296
+ :args=>[[
297
+ {
298
+ :job=>"CanvasSync::Jobs::SyncTermsJob",
299
+ :args=>[],
300
+ :kwargs=>{
301
+ :sub_jobs=>[
302
+ {:job=>"CanvasSync::Jobs::SyncAssignmentGroupsJob", :options=>{}},
303
+ {:job=>"CanvasSync::Jobs::SyncProvisioningReportJob", :options=>{:models=>["users", "enrollments"]}}
304
+ ],
305
+ :term_scope=>nil
306
+ }
307
+ }
308
+ ]],
309
+ :kwargs=>{}
310
+ }
311
+ ]],
312
+ :kwargs => {:batch_genre=>nil, :full_sync_every=>nil, :legacy_support=>false, :updated_after=>nil},
313
+ })
169
314
  end
170
315
  end
171
316
  end
172
317
  end
173
318
 
174
- describe ".simple_report_sync" do
175
-
176
- it 'invokes the first job in the queue and passes on the rest of the job chain' do
177
- expected_job_chain = CanvasSync.simple_report_chain(
178
- [
179
- {
180
- report_name: 'proservices_provisioning_csv',
181
- model: 'users',
182
- params: {
183
- "parameters[include_deleted]" => true,
184
- "parameters[users]" => true
185
- }
186
- }
187
- ]
188
- ).chain_data
189
- first_job = expected_job_chain[:jobs].shift
190
-
191
- expect(CanvasSync::Jobs::SyncSimpleTableJob).to receive(:perform_later)
192
- .with(
193
- expected_job_chain,
194
- first_job[:options]
195
- )
196
-
197
- CanvasSync.simple_report_sync(
198
- [
199
- {
200
- report_name: 'proservices_provisioning_csv',
201
- model: 'users',
202
- params: {
203
- "parameters[include_deleted]" => true,
204
- "parameters[users]" => true
205
- }
206
- }
207
- ]
208
- )
209
- end
210
-
211
- it 'receives the job chain for the specified table' do
212
- chain = CanvasSync.simple_report_chain(
213
- [
214
- {
215
- report_name: 'proservices_provisioning_csv',
216
- model: 'users',
217
- params: {
218
- "parameters[include_deleted]" => true,
219
- "parameters[users]" => true
220
- }
221
- }
222
- ]
223
- )
224
-
225
- expected_job_chain = {
226
- jobs: [
227
- {
228
- job: CanvasSync::Jobs::SyncSimpleTableJob.to_s,
229
- options: {
230
- report_name: 'proservices_provisioning_csv',
231
- model: 'users',
232
- mapping: 'users',
233
- klass: 'User',
234
- term_scope: nil,
235
- params: {
236
- "parameters[include_deleted]" => true,
237
- "parameters[users]" => true
238
- }
239
- }
240
- }
241
- ],
242
- global_options: {}
243
- }
244
-
245
- expect(chain.chain_data).to eq(expected_job_chain)
246
-
247
- end
248
- end
249
-
250
319
  describe ".sync_scope" do
251
320
 
252
321
  end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::Jobs::CanvasProcessWaiter do
4
+ let!(:worker) { CanvasSync::Jobs::CanvasProcessWaiter.new }
5
+
6
+ describe 'perform' do
7
+ it 'queues a InvokeCallbackWorker when complete' do
8
+ job = {
9
+ args: [1, 2],
10
+ kwargs: { progress: anything },
11
+ }
12
+ allow_any_instance_of(Bearcat::Client).to receive(:get).and_return('workflow_state' => 'completed')
13
+ expect(CanvasSync::Jobs::CanvasProcessWaiter::InvokeCallbackWorker).to receive(:perform_later).with(job)
14
+ worker.perform('/blah/', job)
15
+ end
16
+
17
+ it 'invokes the on_failure callback if failed' do
18
+ job = {
19
+ args: [1, 2],
20
+ kwargs: { progress: anything },
21
+ }
22
+ allow_any_instance_of(Bearcat::Client).to receive(:get).and_return('workflow_state' => 'failed')
23
+ expect(CanvasSync::Jobs::CanvasProcessWaiter::InvokeCallbackWorker).to receive(:perform_later).with(job)
24
+ worker.perform('/blah/', nil, on_failure: job)
25
+ end
26
+
27
+ it 're-enqueues if incomplete' do
28
+ ActiveJob::Base.queue_adapter = :test
29
+ allow_any_instance_of(Bearcat::Client).to receive(:get).and_return('workflow_state' => 'pending')
30
+ worker.perform('/blah/', nil, interval: 13)
31
+ expect(CanvasSync::Jobs::CanvasProcessWaiter).to have_been_enqueued.once.with('/blah/', nil, interval: 13)
32
+ end
33
+ end
34
+ end
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  class GoodJob < CanvasSync::Job
4
- def perform(job_chain, argument)
4
+ def perform(argument)
5
5
  end
6
6
  end
7
7
 
8
8
  class EvilError < StandardError; end;
9
9
 
10
10
  class BadJob < CanvasSync::Job
11
- def perform(job_chain, argument)
11
+ def perform(argument)
12
12
  raise EvilError
13
13
  end
14
14
 
@@ -25,19 +25,19 @@ RSpec.describe CanvasSync::Job do
25
25
  describe '#perform' do
26
26
  it 'creates a CanvasSync::JobLog and logs relevant data on it' do
27
27
  expect {
28
- GoodJob.perform_now({}, "argument")
28
+ GoodJob.perform_now("argument")
29
29
  }.to change { CanvasSync::JobLog.count }.by(1)
30
30
 
31
31
  job_log = CanvasSync::JobLog.last
32
32
  expect(job_log.started_at).to_not be_nil
33
33
  expect(job_log.job_class).to eq(GoodJob.to_s)
34
- expect(job_log.job_arguments).to eq([{}, "argument"])
34
+ expect(job_log.job_arguments).to eq(["argument"])
35
35
  expect(job_log.completed_at).to_not be_nil
36
36
  end
37
37
 
38
38
  it 'logs exceptions on the CanvasSync::JobLog and then re-raises' do
39
39
  expect {
40
- BadJob.perform_now({}, "argument")
40
+ BadJob.perform_now("argument")
41
41
  }.to raise_exception(StandardError)
42
42
 
43
43
  job_log = CanvasSync::JobLog.last
@@ -47,25 +47,17 @@ RSpec.describe CanvasSync::Job do
47
47
 
48
48
  it 'invokes an error handler' do
49
49
  expect(BadJob).to receive(:good_catcher).once
50
+ set_batch_context(on_failure: 'BadJob.good_catcher')
50
51
  expect {
51
- BadJob.perform_now({
52
- jobs: [],
53
- global_options: {
54
- on_failure: 'BadJob.good_catcher'
55
- }
56
- })
52
+ BadJob.perform_now()
57
53
  }.to raise_exception(StandardError)
58
54
  end
59
55
 
60
56
  it 'logs a failing error handler' do
61
57
  expect(BadJob).to receive(:bad_catcher).once.and_call_original
58
+ set_batch_context(on_failure: 'BadJob.bad_catcher')
62
59
  expect {
63
- BadJob.perform_now({
64
- jobs: [],
65
- global_options: {
66
- on_failure: 'BadJob.bad_catcher'
67
- }
68
- })
60
+ BadJob.perform_now()
69
61
  }.to raise_exception(StandardError)
70
62
  job_log = CanvasSync::JobLog.last
71
63
  expect(job_log.backtrace).to include "Error Occurred while handling an Error"
@@ -5,12 +5,10 @@ RSpec.describe CanvasSync::Jobs::ReportChecker do
5
5
  let(:report_id) { 1 }
6
6
  let(:report_name) { 'provisioning_csv' }
7
7
  let(:processor) { 'FakeProcessor' }
8
- let(:job_chain) { { jobs: [], global_options: {} } }
9
8
 
10
9
  describe '#perform' do
11
10
  def start_job
12
11
  CanvasSync::Jobs::ReportChecker.perform_now(
13
- job_chain,
14
12
  'provisioning_csv',
15
13
  report_id,
16
14
  processor,
@@ -25,7 +23,7 @@ RSpec.describe CanvasSync::Jobs::ReportChecker do
25
23
  .and_return({ 'status' => 'complete', 'attachment' => { 'url' => 'blah' } })
26
24
 
27
25
  expect(CanvasSync::Jobs::ReportProcessorJob).to receive(:perform_later)
28
- .with(job_chain, report_name, 'blah', processor, {}, report_id)
26
+ .with(report_name, 'blah', processor, {}, report_id)
29
27
 
30
28
  start_job
31
29
  end
@@ -6,17 +6,14 @@ RSpec.describe CanvasSync::Jobs::ReportProcessorJob do
6
6
  let(:report_name) { 'provisioning_csv' }
7
7
  let(:report_url) { 'https://test.instructure.com/sample_report_download' }
8
8
  let(:processor) { FakeProcessor.to_s }
9
- let(:job_chain) { { jobs: [], global_options: {} } }
10
9
  let(:report_id) { 1 }
11
10
 
12
11
  describe '#perform' do
13
12
  it 'downloads the report to a file and then calls the process method on the processor, and then invokes the next job' do
14
13
  expect(IO).to receive(:copy_stream)
15
14
  expect(FakeProcessor).to receive(:process)
16
- expect(CanvasSync).to receive(:invoke_next).with(job_chain)
17
15
 
18
16
  CanvasSync::Jobs::ReportProcessorJob.perform_now(
19
- job_chain,
20
17
  report_name,
21
18
  report_url,
22
19
  processor,