canvas_sync 0.23.5 → 0.24.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -4
  3. data/lib/canvas_sync/version.rb +1 -1
  4. data/lib/canvas_sync.rb +6 -4
  5. data/spec/canvas_sync/canvas_sync_spec.rb +11 -11
  6. data/spec/canvas_sync/live_events/process_event_job_spec.rb +1 -0
  7. data/spec/{dummy → internal}/app/models/application_record.rb +1 -0
  8. data/spec/{dummy → internal}/app/models/content_migration.rb +0 -0
  9. data/spec/{dummy → internal}/app/models/course.rb +0 -1
  10. data/spec/{dummy → internal}/app/models/course_nickname.rb +0 -0
  11. data/spec/{dummy → internal}/app/models/learning_outcome.rb +0 -0
  12. data/spec/{dummy → internal}/app/services/live_events/assignment_event.rb +0 -0
  13. data/spec/{dummy → internal}/app/services/live_events/course_event.rb +0 -0
  14. data/spec/{dummy → internal}/app/services/live_events/course_section_event.rb +0 -0
  15. data/spec/{dummy → internal}/app/services/live_events/enrollment_event.rb +0 -0
  16. data/spec/{dummy → internal}/app/services/live_events/grade_event.rb +0 -0
  17. data/spec/{dummy → internal}/app/services/live_events/module_event.rb +0 -0
  18. data/spec/{dummy → internal}/app/services/live_events/module_item_event.rb +0 -0
  19. data/spec/{dummy → internal}/app/services/live_events/submission_event.rb +0 -0
  20. data/spec/{dummy → internal}/app/services/live_events/syllabus_event.rb +0 -0
  21. data/spec/{dummy → internal}/app/services/live_events/user_event.rb +0 -0
  22. data/spec/internal/bin/rails +9 -0
  23. data/spec/internal/config/database.yml +5 -0
  24. data/spec/internal/config/routes.rb +5 -0
  25. data/spec/internal/config/storage.yml +3 -0
  26. data/spec/{dummy/db/migrate/20220926221926_create_users.rb → internal/db/migrate/20250912205136_create_users.rb} +0 -0
  27. data/spec/{dummy/db/migrate/20201016181346_create_pseudonyms.rb → internal/db/migrate/20250912205137_create_pseudonyms.rb} +0 -0
  28. data/spec/{dummy/db/migrate/20200415171620_create_groups.rb → internal/db/migrate/20250912205139_create_groups.rb} +0 -0
  29. data/spec/{dummy/db/migrate/20200416214248_create_group_memberships.rb → internal/db/migrate/20250912205140_create_group_memberships.rb} +0 -0
  30. data/spec/{dummy/db/migrate/20190702203622_create_accounts.rb → internal/db/migrate/20250912205141_create_accounts.rb} +0 -0
  31. data/spec/{dummy/db/migrate/20190702203623_create_terms.rb → internal/db/migrate/20250912205142_create_terms.rb} +0 -0
  32. data/spec/{dummy/db/migrate/20190702203625_create_sections.rb → internal/db/migrate/20250912205144_create_sections.rb} +0 -0
  33. data/spec/{dummy/db/migrate/20190702203626_create_assignments.rb → internal/db/migrate/20250912205145_create_assignments.rb} +0 -0
  34. data/spec/{dummy/db/migrate/20190702203627_create_submissions.rb → internal/db/migrate/20250912205146_create_submissions.rb} +0 -0
  35. data/spec/{dummy/db/migrate/20190927204545_create_roles.rb → internal/db/migrate/20250912205147_create_roles.rb} +2 -2
  36. data/spec/{dummy/db/migrate/20190927204546_create_admins.rb → internal/db/migrate/20250912205148_create_admins.rb} +0 -0
  37. data/spec/{dummy/db/migrate/20190702203630_create_assignment_groups.rb → internal/db/migrate/20250912205149_create_assignment_groups.rb} +0 -0
  38. data/spec/{dummy/db/migrate/20190702203631_create_context_modules.rb → internal/db/migrate/20250912205150_create_context_modules.rb} +0 -0
  39. data/spec/{dummy/db/migrate/20190702203632_create_context_module_items.rb → internal/db/migrate/20250912205151_create_context_module_items.rb} +0 -0
  40. data/spec/{dummy/db/migrate/20210907233329_create_user_observers.rb → internal/db/migrate/20250912205152_create_user_observers.rb} +0 -0
  41. data/spec/{dummy/db/migrate/20210907233330_create_grading_periods.rb → internal/db/migrate/20250912205153_create_grading_periods.rb} +0 -0
  42. data/spec/{dummy/db/migrate/20211001184920_create_grading_period_groups.rb → internal/db/migrate/20250912205154_create_grading_period_groups.rb} +0 -0
  43. data/spec/{dummy/db/migrate/20220308072643_create_content_migrations.rb → internal/db/migrate/20250912205155_create_content_migrations.rb} +0 -0
  44. data/spec/{dummy/db/migrate/20220712210559_create_learning_outcomes.rb → internal/db/migrate/20250912205156_create_learning_outcomes.rb} +0 -0
  45. data/spec/{dummy/db/migrate/20240523101010_create_learning_outcome_results.rb → internal/db/migrate/20250912205157_create_learning_outcome_results.rb} +0 -0
  46. data/spec/{dummy/db/migrate/20240510094100_create_rubric_associations.rb → internal/db/migrate/20250912205160_create_rubric_associations.rb} +0 -0
  47. data/spec/{dummy/db/migrate/20240510101100_create_rubric_assessments.rb → internal/db/migrate/20250912205161_create_rubric_assessments.rb} +0 -0
  48. data/spec/{dummy/db/migrate/20240828161300_create_course_progresses.rb → internal/db/migrate/20250912205162_create_course_progresses.rb} +0 -0
  49. data/spec/internal/db/schema.rb +6 -0
  50. data/spec/spec_helper.rb +8 -4
  51. metadata +182 -372
  52. data/lib/canvas_sync/job_batches/batch.rb +0 -595
  53. data/lib/canvas_sync/job_batches/callback.rb +0 -135
  54. data/lib/canvas_sync/job_batches/chain_builder.rb +0 -247
  55. data/lib/canvas_sync/job_batches/compat/active_job.rb +0 -108
  56. data/lib/canvas_sync/job_batches/compat/sidekiq/web/batches_assets/css/styles.less +0 -182
  57. data/lib/canvas_sync/job_batches/compat/sidekiq/web/batches_assets/js/batch_tree.js +0 -108
  58. data/lib/canvas_sync/job_batches/compat/sidekiq/web/batches_assets/js/util.js +0 -2
  59. data/lib/canvas_sync/job_batches/compat/sidekiq/web/helpers.rb +0 -41
  60. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/_batch_tree.erb +0 -6
  61. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/_batches_table.erb +0 -44
  62. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/_common.erb +0 -13
  63. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/_jobs_table.erb +0 -21
  64. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/_pagination.erb +0 -26
  65. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/batch.erb +0 -81
  66. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/batches.erb +0 -23
  67. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/pool.erb +0 -137
  68. data/lib/canvas_sync/job_batches/compat/sidekiq/web/views/pools.erb +0 -47
  69. data/lib/canvas_sync/job_batches/compat/sidekiq/web.rb +0 -218
  70. data/lib/canvas_sync/job_batches/compat/sidekiq.rb +0 -149
  71. data/lib/canvas_sync/job_batches/compat.rb +0 -20
  72. data/lib/canvas_sync/job_batches/context_hash.rb +0 -157
  73. data/lib/canvas_sync/job_batches/hier_batch_ids.lua +0 -25
  74. data/lib/canvas_sync/job_batches/jobs/base_job.rb +0 -5
  75. data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +0 -20
  76. data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +0 -175
  77. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +0 -20
  78. data/lib/canvas_sync/job_batches/pool.rb +0 -254
  79. data/lib/canvas_sync/job_batches/pool_refill.lua +0 -47
  80. data/lib/canvas_sync/job_batches/redis_model.rb +0 -67
  81. data/lib/canvas_sync/job_batches/redis_script.rb +0 -161
  82. data/lib/canvas_sync/job_batches/schedule_callback.lua +0 -14
  83. data/lib/canvas_sync/job_batches/status.rb +0 -89
  84. data/lib/canvas_sync/job_uniqueness/compat/active_job.rb +0 -75
  85. data/lib/canvas_sync/job_uniqueness/compat/sidekiq.rb +0 -135
  86. data/lib/canvas_sync/job_uniqueness/compat.rb +0 -20
  87. data/lib/canvas_sync/job_uniqueness/configuration.rb +0 -25
  88. data/lib/canvas_sync/job_uniqueness/job_uniqueness.rb +0 -47
  89. data/lib/canvas_sync/job_uniqueness/lock_context.rb +0 -199
  90. data/lib/canvas_sync/job_uniqueness/locksmith.rb +0 -92
  91. data/lib/canvas_sync/job_uniqueness/on_conflict/base.rb +0 -32
  92. data/lib/canvas_sync/job_uniqueness/on_conflict/log.rb +0 -13
  93. data/lib/canvas_sync/job_uniqueness/on_conflict/null_strategy.rb +0 -9
  94. data/lib/canvas_sync/job_uniqueness/on_conflict/raise.rb +0 -11
  95. data/lib/canvas_sync/job_uniqueness/on_conflict/reject.rb +0 -21
  96. data/lib/canvas_sync/job_uniqueness/on_conflict/reschedule.rb +0 -20
  97. data/lib/canvas_sync/job_uniqueness/on_conflict.rb +0 -62
  98. data/lib/canvas_sync/job_uniqueness/strategy/base.rb +0 -107
  99. data/lib/canvas_sync/job_uniqueness/strategy/until_and_while_executing.rb +0 -35
  100. data/lib/canvas_sync/job_uniqueness/strategy/until_executed.rb +0 -20
  101. data/lib/canvas_sync/job_uniqueness/strategy/until_executing.rb +0 -20
  102. data/lib/canvas_sync/job_uniqueness/strategy/until_expired.rb +0 -16
  103. data/lib/canvas_sync/job_uniqueness/strategy/while_executing.rb +0 -26
  104. data/lib/canvas_sync/job_uniqueness/strategy.rb +0 -27
  105. data/lib/canvas_sync/job_uniqueness/unique_job_common.rb +0 -79
  106. data/spec/dummy/README.rdoc +0 -1
  107. data/spec/dummy/Rakefile +0 -6
  108. data/spec/dummy/app/services/live_events/assignment_created_event.rb +0 -12
  109. data/spec/dummy/app/services/live_events/assignment_updated_event.rb +0 -12
  110. data/spec/dummy/app/services/live_events/base_event.rb +0 -52
  111. data/spec/dummy/app/services/live_events/course_created_event.rb +0 -12
  112. data/spec/dummy/app/services/live_events/course_section_created_event.rb +0 -12
  113. data/spec/dummy/app/services/live_events/course_section_updated_event.rb +0 -12
  114. data/spec/dummy/app/services/live_events/course_updated_event.rb +0 -12
  115. data/spec/dummy/app/services/live_events/enrollment_created_event.rb +0 -12
  116. data/spec/dummy/app/services/live_events/enrollment_updated_event.rb +0 -12
  117. data/spec/dummy/app/services/live_events/grade_changed_event.rb +0 -12
  118. data/spec/dummy/app/services/live_events/module_created_event.rb +0 -12
  119. data/spec/dummy/app/services/live_events/module_item_created_event.rb +0 -12
  120. data/spec/dummy/app/services/live_events/module_item_updated_event.rb +0 -12
  121. data/spec/dummy/app/services/live_events/module_updated_event.rb +0 -12
  122. data/spec/dummy/app/services/live_events/submission_created_event.rb +0 -12
  123. data/spec/dummy/app/services/live_events/submission_updated_event.rb +0 -12
  124. data/spec/dummy/app/services/live_events/syllabus_updated_event.rb +0 -12
  125. data/spec/dummy/app/services/live_events/user_created_event.rb +0 -12
  126. data/spec/dummy/app/services/live_events/user_updated_event.rb +0 -12
  127. data/spec/dummy/bin/rails +0 -4
  128. data/spec/dummy/config/application.rb +0 -39
  129. data/spec/dummy/config/boot.rb +0 -5
  130. data/spec/dummy/config/database.yml +0 -25
  131. data/spec/dummy/config/environment.rb +0 -5
  132. data/spec/dummy/config/environments/development.rb +0 -41
  133. data/spec/dummy/config/environments/test.rb +0 -44
  134. data/spec/dummy/config/initializers/assets.rb +0 -11
  135. data/spec/dummy/config/initializers/session_store.rb +0 -3
  136. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  137. data/spec/dummy/config/routes.rb +0 -3
  138. data/spec/dummy/config/secrets.yml +0 -22
  139. data/spec/dummy/config.ru +0 -4
  140. data/spec/dummy/db/schema.rb +0 -563
  141. data/spec/job_batching/batch_spec.rb +0 -531
  142. data/spec/job_batching/callback_spec.rb +0 -38
  143. data/spec/job_batching/compat/active_job_spec.rb +0 -107
  144. data/spec/job_batching/compat/sidekiq_spec.rb +0 -127
  145. data/spec/job_batching/context_hash_spec.rb +0 -54
  146. data/spec/job_batching/flow_spec.rb +0 -82
  147. data/spec/job_batching/integration/fail_then_succeed.rb +0 -42
  148. data/spec/job_batching/integration/integration.rb +0 -57
  149. data/spec/job_batching/integration/nested.rb +0 -88
  150. data/spec/job_batching/integration/simple.rb +0 -47
  151. data/spec/job_batching/integration/workflow.rb +0 -134
  152. data/spec/job_batching/integration_helper.rb +0 -50
  153. data/spec/job_batching/pool_spec.rb +0 -161
  154. data/spec/job_batching/status_spec.rb +0 -76
  155. data/spec/job_batching/support/base_job.rb +0 -14
  156. data/spec/job_batching/support/sample_callback.rb +0 -2
  157. data/spec/job_uniqueness/compat/active_job_spec.rb +0 -49
  158. data/spec/job_uniqueness/compat/sidekiq_spec.rb +0 -68
  159. data/spec/job_uniqueness/lock_context_spec.rb +0 -106
  160. data/spec/job_uniqueness/on_conflict/log_spec.rb +0 -11
  161. data/spec/job_uniqueness/on_conflict/raise_spec.rb +0 -10
  162. data/spec/job_uniqueness/on_conflict/reschedule_spec.rb +0 -63
  163. data/spec/job_uniqueness/on_conflict_spec.rb +0 -16
  164. data/spec/job_uniqueness/spec_helper.rb +0 -17
  165. data/spec/job_uniqueness/strategy/base_spec.rb +0 -100
  166. data/spec/job_uniqueness/strategy/until_and_while_executing_spec.rb +0 -48
  167. data/spec/job_uniqueness/strategy/until_executed_spec.rb +0 -23
  168. data/spec/job_uniqueness/strategy/until_executing_spec.rb +0 -23
  169. data/spec/job_uniqueness/strategy/until_expired_spec.rb +0 -23
  170. data/spec/job_uniqueness/strategy/while_executing_spec.rb +0 -33
  171. data/spec/job_uniqueness/support/lock_strategy.rb +0 -28
  172. data/spec/job_uniqueness/support/on_conflict.rb +0 -24
  173. data/spec/job_uniqueness/support/test_worker.rb +0 -19
  174. data/spec/job_uniqueness/unique_job_common_spec.rb +0 -45
  175. /data/spec/{dummy → internal}/app/models/account.rb +0 -0
  176. /data/spec/{dummy → internal}/app/models/admin.rb +0 -0
  177. /data/spec/{dummy → internal}/app/models/assignment.rb +0 -0
  178. /data/spec/{dummy → internal}/app/models/assignment_group.rb +0 -0
  179. /data/spec/{dummy → internal}/app/models/assignment_override.rb +0 -0
  180. /data/spec/{dummy → internal}/app/models/context_module.rb +0 -0
  181. /data/spec/{dummy → internal}/app/models/context_module_item.rb +0 -0
  182. /data/spec/{dummy → internal}/app/models/course_progress.rb +0 -0
  183. /data/spec/{dummy → internal}/app/models/enrollment.rb +0 -0
  184. /data/spec/{dummy → internal}/app/models/grading_period.rb +0 -0
  185. /data/spec/{dummy → internal}/app/models/grading_period_group.rb +0 -0
  186. /data/spec/{dummy → internal}/app/models/group.rb +0 -0
  187. /data/spec/{dummy → internal}/app/models/group_membership.rb +0 -0
  188. /data/spec/{dummy → internal}/app/models/learning_outcome_result.rb +0 -0
  189. /data/spec/{dummy → internal}/app/models/pseudonym.rb +0 -0
  190. /data/spec/{dummy → internal}/app/models/role.rb +0 -0
  191. /data/spec/{dummy → internal}/app/models/rubric.rb +0 -0
  192. /data/spec/{dummy → internal}/app/models/rubric_assessment.rb +0 -0
  193. /data/spec/{dummy → internal}/app/models/rubric_association.rb +0 -0
  194. /data/spec/{dummy → internal}/app/models/score.rb +0 -0
  195. /data/spec/{dummy → internal}/app/models/section.rb +0 -0
  196. /data/spec/{dummy → internal}/app/models/submission.rb +0 -0
  197. /data/spec/{dummy → internal}/app/models/term.rb +0 -0
  198. /data/spec/{dummy → internal}/app/models/user.rb +0 -0
  199. /data/spec/{dummy → internal}/app/models/user_observer.rb +0 -0
  200. /data/spec/{dummy/db/migrate/20190702203621_create_courses.rb → internal/db/migrate/20250912205138_create_courses.rb} +0 -0
  201. /data/spec/{dummy/db/migrate/20190702203624_create_enrollments.rb → internal/db/migrate/20250912205143_create_enrollments.rb} +0 -0
  202. /data/spec/{dummy/db/migrate/20250319194134_create_course_nicknames.rb → internal/db/migrate/20250912205158_create_course_nicknames.rb} +0 -0
  203. /data/spec/{dummy/db/migrate/20250319194135_create_rubrics.rb → internal/db/migrate/20250912205159_create_rubrics.rb} +0 -0
  204. /data/spec/{dummy/db/migrate/20241223080202_create_assignment_overrides.rb → internal/db/migrate/20250912205163_create_assignment_overrides.rb} +0 -0
  205. /data/spec/{dummy/db/migrate/20250626194330_create_scores.rb → internal/db/migrate/20250912205164_create_scores.rb} +0 -0
@@ -1,135 +0,0 @@
1
- module CanvasSync::JobBatches
2
- class Batch
3
- module Callback
4
- mattr_accessor :worker_class
5
-
6
- VALID_CALLBACKS = %i[success complete death stagnated].freeze
7
-
8
- module CallbackWorkerCommon
9
- def perform(definition, event, opts, bid, parent_bid)
10
- return unless VALID_CALLBACKS.include?(event.to_sym)
11
-
12
- method = nil
13
- target = :instance
14
- clazz = definition
15
- if clazz.is_a?(String)
16
- if clazz.include?('#')
17
- clazz, method = clazz.split("#")
18
- elsif clazz.include?('.')
19
- clazz, method = clazz.split(".")
20
- target = :class
21
- end
22
- end
23
-
24
- method ||= "on_#{event}"
25
- status = Batch::Status.new(bid)
26
-
27
- if clazz && object = Object.const_get(clazz)
28
- target = target == :instance ? object.new : object
29
- if target.respond_to?(method, true)
30
- target.send(method, status, opts.with_indifferent_access)
31
- else
32
- Batch.logger.warn("Invalid callback method #{definition} - #{target.to_s} does not respond to #{method}")
33
- end
34
- else
35
- Batch.logger.warn("Invalid callback method #{definition} - Class #{clazz} not found")
36
- end
37
- end
38
- end
39
-
40
- class Finalize
41
- # The methods in this class are called after all same-named callbacks have been
42
- # completed for the passed Batch.
43
- # These methods mainly handle bubbling events up to the parent Batch
44
- # You could say that they are the callbacks for callbacks.
45
-
46
- def dispatch(status, opts)
47
- bid = opts["bid"]
48
- event = opts["event"].to_sym
49
-
50
- Batch.logger.debug {"Finalize #{event} batch id: #{opts["bid"]}"}
51
-
52
- batch_status = Status.new bid
53
- send(event, bid, batch_status, batch_status.parent_bid)
54
-
55
- Batch.redis do |r|
56
- r.srem("BID-#{bid}-pending_callbacks", "#{event}-finalize")
57
- end
58
-
59
- if event == :success
60
- if opts['origin'].present?
61
- # This is a callback for a callback. In this case we need to check if we should cleanup the original bid.
62
- origin_bid = opts['origin']['for_bid']
63
- _, pending, success_ran = Batch.redis do |r|
64
- r.multi do |r|
65
- r.srem("BID-#{origin_bid}-pending_callbacks", opts['origin']['event'])
66
- r.scard("BID-#{origin_bid}-pending_callbacks")
67
- r.hget("BID-#{origin_bid}", "success")
68
- end
69
- end
70
- Batch.cleanup_redis(origin_bid) if pending == 0 && success_ran == 'true'
71
- end
72
-
73
- if (Batch.redis {|r| r.scard("BID-#{bid}-pending_callbacks") }) == 0
74
- Batch.cleanup_redis(bid)
75
- end
76
- end
77
- end
78
-
79
- def success(bid, status, parent_bid)
80
- return unless parent_bid
81
-
82
- Batch.with_callback_check(parent_bid) do |r|
83
- r.sadd("BID-#{parent_bid}-batches-success", bid)
84
- r.srem("BID-#{parent_bid}-batches-failed", bid)
85
- r.sadd("BID-#{parent_bid}-batches-complete", bid)
86
- end
87
- end
88
-
89
- def complete(bid, status, parent_bid)
90
- return unless parent_bid
91
-
92
- pending, children, success = Batch.redis do |r|
93
- r.multi do |r|
94
- r.hincrby("BID-#{bid}", "pending", 0)
95
- r.hincrby("BID-#{bid}", "children", 0)
96
- r.scard("BID-#{bid}-batches-success")
97
- end
98
- end
99
-
100
- if !(pending.to_i.zero? && children == success)
101
- # If batch was not successfull check and see if its parent is complete
102
- # if the parent is complete we can trigger its complete callback.
103
- #
104
- # Otherwise, we don't want to to trigger the parent's :complete here (and
105
- # we instead opt to have success tigger parent :complete) - this
106
- # allows the success callback to add additional jobs to the parent batch
107
- # before triggering :complete.
108
-
109
- Batch.with_callback_check(parent_bid, except: [:success]) do |r|
110
- r.sadd("BID-#{parent_bid}-batches-complete", bid)
111
- r.sadd("BID-#{parent_bid}-batches-failed", bid)
112
- end
113
- end
114
- end
115
-
116
- def death(bid, status, parent_bid)
117
- return unless parent_bid
118
-
119
- # We only need to bubble the event here - other events (eg stagnation) will be checked and bubbled elsewhere.
120
-
121
- Batch.enqueue_callbacks(:death, parent_bid)
122
- end
123
-
124
- def stagnated(bid, status, parent_bid)
125
- return unless parent_bid
126
-
127
- Batch.with_callback_check(parent_bid) do |r|
128
- r.sadd("BID-#{parent_bid}-batches-stagnated", bid)
129
- r.expire("BID-#{parent_bid}-batches-stagnated", BID_EXPIRE_TTL)
130
- end
131
- end
132
- end
133
- end
134
- end
135
- end
@@ -1,247 +0,0 @@
1
- module CanvasSync::JobBatches
2
- class ChainBuilder
3
- VALID_PLACEMENT_PARAMETERS = %i[before after with].freeze
4
-
5
- attr_reader :base_job
6
-
7
- def initialize(base_type = SerialBatchJob)
8
- if base_type.is_a?(Hash)
9
- @base_job = base_type
10
- @base_job[:args] ||= @base_job[:parameters] || []
11
- @base_job[:kwargs] ||= {}
12
- else
13
- @base_job = build_job_hash(base_type)
14
- end
15
-
16
- self.class.get_chain_parameter(base_job)
17
- end
18
-
19
- def process!
20
- normalize!
21
- self.class.enqueue_job(base_job)
22
- end
23
-
24
- def [](key)
25
- if key.is_a?(Class)
26
- get_sub_chain(key)
27
- else
28
- # Legacy Support
29
- key = :args if key == :parameters
30
-
31
- @base_job[key]
32
- end
33
- end
34
-
35
- def args; return self[:args]; end
36
- def kwargs; return self[:kwargs]; end
37
-
38
- def <<(new_job)
39
- insert_at(-1, new_job)
40
- end
41
-
42
- def insert_at(position, new_jobs, *args, **kwargs, &blk)
43
- chain = self.class.get_chain_parameter(base_job)
44
- if new_jobs.is_a?(Class) || new_jobs.is_a?(String)
45
- new_jobs = build_job_hash(new_jobs, args: args, kwargs: kwargs, &blk)
46
- elsif args.count > 0 || kwargs.count > 0
47
- raise "Unexpected number of arguments"
48
- end
49
- new_jobs = [new_jobs] unless new_jobs.is_a?(Array)
50
- chain.insert(position, *new_jobs)
51
- end
52
-
53
- def insert(new_jobs, *args, **kwargs, &blk)
54
- if new_jobs.is_a?(Class) || new_jobs.is_a?(String)
55
- job_kwargs = kwargs.except(*VALID_PLACEMENT_PARAMETERS)
56
- new_jobs = build_job_hash(new_jobs, args: args, kwargs: job_kwargs, &blk)
57
- kwargs = kwargs.slice(*VALID_PLACEMENT_PARAMETERS)
58
- else
59
- invalid_params = kwargs.keys - VALID_PLACEMENT_PARAMETERS
60
- raise "Invalid placement parameters: #{invalid_params.map(&:to_s).join(', ')}" if invalid_params.present?
61
- raise "At most one placement parameter may be provided" if kwargs.values.compact.length > 1
62
- raise "Unexpected number of arguments" if args.length > 0
63
- end
64
-
65
- new_jobs = [new_jobs] unless new_jobs.is_a?(Array)
66
-
67
- if !kwargs.present?
68
- insert_at(-1, new_jobs)
69
- else
70
- placement = kwargs.keys[0]
71
- relative_to = kwargs.values[0]
72
-
73
- matching_jobs = find_matching_jobs(relative_to).to_a
74
- raise "Could not find a \"#{relative_to}\" job in the chain" if matching_jobs.count == 0
75
- raise "Found multiple \"#{relative_to}\" jobs in the chain" if matching_jobs.count > 1
76
-
77
- relative_job, parent_job, sub_index = matching_jobs[0]
78
- needed_parent_type = placement == :with ? ConcurrentBatchJob : SerialBatchJob
79
-
80
- chain = self.class.get_chain_parameter(parent_job)
81
-
82
- if parent_job[:job] != needed_parent_type
83
- old_job = chain[sub_index]
84
- parent_job = chain[sub_index] = {
85
- job: needed_parent_type,
86
- parameters: [],
87
- }
88
- sub_index = 0
89
- chain = self.class.get_chain_parameter(parent_job)
90
- chain << old_job
91
- end
92
-
93
- if placement == :with
94
- chain.insert(-1, *new_jobs)
95
- else
96
- sub_index += 1 if placement == :after
97
- chain.insert(sub_index, *new_jobs)
98
- end
99
- end
100
- end
101
-
102
- def empty?
103
- self.class.get_chain_parameter(self).empty?
104
- end
105
-
106
- def get_sub_chain(sub_type)
107
- matching_jobs = find_matching_jobs(sub_type).to_a
108
- raise "Found multiple \"#{sub_type}\" jobs in the chain" if matching_jobs.count > 1
109
- return nil if matching_jobs.count == 0
110
-
111
- job = matching_jobs[0][0]
112
- job = self.class.new(job) unless job.is_a?(ChainBuilder)
113
- job
114
- end
115
-
116
- def normalize!(job_def = self.base_job)
117
- if job_def.is_a?(ChainBuilder)
118
- job_def.normalize!
119
- else
120
- job_def[:job] = job_def[:job].to_s
121
- if (chain = self.class.get_chain_parameter(job_def, raise_error: false)).present?
122
- chain.map! { |sub_job| normalize!(sub_job) }
123
- end
124
- job_def
125
- end
126
- end
127
-
128
- def apply_block(&blk)
129
- return unless blk.present?
130
- instance_exec(&blk)
131
- end
132
-
133
- private
134
-
135
- def build_job_hash(job, args: [], kwargs: {}, &blk)
136
- hsh = {
137
- job: job,
138
- args: args,
139
- kwargs: kwargs,
140
- }
141
- self.class.new(hsh).apply_block(&blk) if blk.present?
142
- hsh
143
- end
144
-
145
- def find_matching_jobs(search_job, parent_job = self.base_job)
146
- return to_enum(:find_matching_jobs, search_job, parent_job) unless block_given?
147
-
148
- sub_jobs = self.class.get_chain_parameter(parent_job)
149
- sub_jobs.each_with_index do |sub_job, i|
150
- if sub_job[:job].to_s == search_job.to_s
151
- yield [sub_job, parent_job, i]
152
- elsif self.class._job_type_definitions[sub_job[:job].to_s]
153
- find_matching_jobs(search_job, sub_job) { |item| yield item }
154
- end
155
- end
156
- end
157
-
158
- def find_parent_job(job_def)
159
- iterate_job_tree do |job, path|
160
- return path[-1] if job == job_def
161
- end
162
- nil
163
- end
164
-
165
- def iterate_job_tree(root: self.base_job, path: [], &blk)
166
- blk.call(root, path)
167
-
168
- if self.class._job_type_definitions[root[:job]]
169
- sub_jobs = self.class.get_chain_parameter(root)
170
- sub_jobs.each_with_index do |sub_job, i|
171
- iterate_job_tree(root: sub_job, path: [*path, root], &blk)
172
- end
173
- end
174
- end
175
-
176
- class << self
177
- # Support builder syntaxt/DSL
178
- # Chain.build(ConcurrentBatchJob) do
179
- # insert(SomeJob, arg1, kwarg: 1)
180
- # insert(SerialBatchJob) do
181
- # insert(SomeJob, arg1, kwarg: 1)
182
- # end
183
- # end
184
- def build(job, *args, **kwargs, &blk)
185
- new(job).tap do |ch|
186
- ch.base_job[:args] = args
187
- ch.base_job[:kwargs] = kwargs
188
- ch.apply_block(&blk)
189
- end
190
- end
191
-
192
- def _job_type_definitions
193
- @job_type_definitions ||= {}
194
- end
195
-
196
- def register_chain_job(job_class, chain_parameter, **options)
197
- _job_type_definitions[job_class.to_s] = {
198
- **options,
199
- chain_parameter: chain_parameter,
200
- }
201
- end
202
-
203
- def get_chain_parameter(job_def, raise_error: true)
204
- unless _job_type_definitions[job_def[:job].to_s].present?
205
- raise "Job Type #{job_def[:job].to_s} does not accept a sub-chain" if raise_error
206
- return nil
207
- end
208
-
209
- key = _job_type_definitions[job_def[:job].to_s][:chain_parameter]
210
- if key.is_a?(Numeric)
211
- job_def[:args][key] ||= []
212
- else
213
- job_def[:kwargs][key] ||= []
214
- end
215
- end
216
-
217
- # TODO: Add a Chain progress web View
218
- # Augment Batch tree-view with Chain data
219
- # > [DONE] Tree view w/o Chain will only show Parent/Current batches and Job Counts
220
- # > If augmented with Chain data, the above will be annotated with Chain-related info and will be able to show Jobs defined in the Chain
221
- # > Chain-jobs will be supplied chain_id and chain_step_id metadata
222
- # > Using server-middleware, if a Chain-job (has chain_id and chain_step_id) creates a Batch, tag the Batch w/ the chain_id and chain_step_id
223
- # > UI will map Batches to Chain-steps using the chain_step_id. UI will add entries for any Chain-steps that were not tied to a Batch
224
- # > [DONE] Use a Lua script to find child batch IDs. Support max_depth, items_per_depth, top_depth_slice parameters
225
- def enqueue_job(job_def)
226
- job_class = job_def[:job].constantize
227
- job_args = job_def[:args] || job_def[:parameters] || []
228
- job_kwargs = job_def[:kwargs] || {}
229
-
230
- # Legacy Support
231
- if job_def[:options]
232
- job_args << {} unless job_args[-1].is_a?(Hash)
233
- job_args[-1].merge!(job_def[:options])
234
- end
235
-
236
- if job_class.respond_to? :perform_async
237
- job_class.perform_async(*job_args, **job_kwargs)
238
- else
239
- job_class.perform_later(*job_args, **job_kwargs)
240
- end
241
- end
242
- end
243
- end
244
-
245
- ChainBuilder.register_chain_job(ConcurrentBatchJob, 0)
246
- ChainBuilder.register_chain_job(SerialBatchJob, 0)
247
- end
@@ -1,108 +0,0 @@
1
-
2
- module CanvasSync::JobBatches
3
- module Compat
4
- module ActiveJob
5
- module BatchAwareJob
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- around_perform do |job, block|
10
- if (@bid) # This _must_ be @bid - not just bid
11
- prev_batch = Thread.current[CURRENT_BATCH_THREAD_KEY]
12
- begin
13
- Thread.current[CURRENT_BATCH_THREAD_KEY] = Batch.new(@bid)
14
- block.call
15
- Thread.current[CURRENT_BATCH_THREAD_KEY].save_context_changes
16
- Batch.process_successful_job(@bid, job_id)
17
- rescue
18
- Batch.process_failed_job(@bid, job_id)
19
- raise
20
- ensure
21
- Thread.current[CURRENT_BATCH_THREAD_KEY] = prev_batch
22
- end
23
- else
24
- block.call
25
- end
26
- end
27
-
28
- around_enqueue do |job, block|
29
- if (batch = Thread.current[CURRENT_BATCH_THREAD_KEY])
30
- @bid = batch.bid
31
- batch.append_jobs(job_id) if @bid
32
- end
33
- block.call
34
- end
35
- end
36
-
37
- def bid
38
- @bid || Thread.current[CURRENT_BATCH_THREAD_KEY]&.bid
39
- end
40
-
41
- def batch
42
- Thread.current[CURRENT_BATCH_THREAD_KEY]
43
- end
44
-
45
- def batch_context
46
- batch&.context || {}
47
- end
48
-
49
- def valid_within_batch?
50
- batch.valid?
51
- end
52
-
53
- def serialize
54
- super.tap do |data|
55
- data['batch_id'] = @bid # This _must_ be @bid - not just bid
56
- data
57
- end
58
- end
59
-
60
- def deserialize(data)
61
- super
62
- @bid = data['batch_id']
63
- end
64
- end
65
-
66
- class ActiveJobCallbackWorker < ::ActiveJob::Base
67
- include Batch::Callback::CallbackWorkerCommon
68
-
69
- def self.enqueue_all(args, queue)
70
- args.each do |arg_set|
71
- set(queue: queue).perform_later(*arg_set)
72
- end
73
- end
74
- end
75
-
76
- def self.handle_job_death(job, error = nil)
77
- if job.is_a?(Array)
78
- event = ActiveSupport::Notifications::Event.new(*job)
79
- payload = event.payload
80
- job = payload[:job].serialize
81
- error = payload[:error]
82
- end
83
-
84
- if job["job_id"].present? && job["batch_id"].present?
85
- CanvasSync::JobBatches::Batch.process_dead_job(job['batch_id'], job['job_id'])
86
- end
87
- end
88
-
89
- def self.configure
90
- ::ActiveJob::Base.include BatchAwareJob
91
-
92
- begin
93
- ActiveSupport::Notifications.subscribe "discard.active_job" do |*args|
94
- handle_job_death(args)
95
- end
96
-
97
- ActiveSupport::Notifications.subscribe "retry_stopped.active_job" do |*args|
98
- handle_job_death(args)
99
- end
100
- rescue => err
101
- Rails.logger.warn(err)
102
- end
103
-
104
- Batch::Callback.worker_class ||= ActiveJobCallbackWorker
105
- end
106
- end
107
- end
108
- end
@@ -1,182 +0,0 @@
1
-
2
- @color-green: #25c766;
3
- @color-darkred: #8f0000;
4
- @color-red: #e03963;
5
- @color-yellow: #c4c725;
6
-
7
- .code-wrap.batch-context .args-extended {
8
- white-space: pre;
9
-
10
- .key {
11
- white-space: pre-wrap;
12
- margin-left: 2em;
13
- text-indent: -2em;
14
- display: inline-block;
15
- }
16
-
17
- .own {
18
- color: @color-green;
19
- }
20
- }
21
-
22
-
23
- .batch-tree {
24
- .status-block {
25
- .tree-stat {
26
- margin: 0 4px;
27
-
28
- &.pending {
29
- color: @color-yellow;
30
- }
31
- &.failed {
32
- color: @color-red;
33
- }
34
- &.dead {
35
- color: @color-darkred;
36
- }
37
- &.success {
38
- color: @color-green;
39
- }
40
- &.total {
41
-
42
- }
43
- }
44
- }
45
-
46
- .text-inactive {
47
- color: darken(#fff, 35%);
48
- font-size: 80%;
49
- }
50
-
51
- .tree-header {
52
- position: relative;
53
-
54
- .status-block {
55
- position: absolute;
56
- bottom: 0;
57
- width: 100%;
58
-
59
- margin-right: 8px;
60
- font-size: 90%;
61
- text-align: right;
62
-
63
- .tree-stat {
64
- font-style: italic;
65
- }
66
- }
67
- }
68
-
69
- .tree-entry {
70
- > .header {
71
- display: flex;
72
- align-items: center;
73
-
74
- .header-inner {
75
- padding: 4px 0;
76
- border-bottom: 1px dashed white;
77
- display: flex;
78
- align-items: center;
79
- flex: 1;
80
- }
81
-
82
- &:hover {
83
- background-color: rgba(0,0,0,0.20);
84
- border-radius: 3px;
85
- }
86
-
87
- .row-toggle {
88
- width: 16px;
89
- height: 16px;
90
- text-align: center;
91
- align-self: center;
92
- border-radius: 50%;
93
- border: 1px solid #999;
94
- text-decoration: none;
95
- margin: 0 4px;
96
- font-size: 16px;
97
- line-height: 15px;
98
-
99
- &.not_applicable {
100
- opacity: 0;
101
- pointer-events: none;
102
- }
103
- }
104
-
105
- .main {
106
- flex: 1;
107
- display: flex;
108
- align-items: baseline;
109
-
110
- .bid {
111
- font-family: monospace;
112
- padding: 3px 6px;
113
- background: rgba(0,0,0,0.2);
114
- border-radius: 3px;
115
- font-size: 12px;
116
- margin: 0 12px 0 0;
117
-
118
- &:hover {
119
- .bid-goto {
120
- display: inline-block;
121
- padding: 0 0 0 4px;
122
- font-size: 200%;
123
- line-height: 10px;
124
- vertical-align: sub;
125
- text-decoration: dotted;
126
- }
127
- }
128
-
129
- .bid-goto {
130
- display: none;
131
- }
132
- }
133
- }
134
-
135
- .goto-link {
136
- margin: 0 8px;
137
- display: inline-block;
138
- height: 16px;
139
- font-size: 90%;
140
- border-bottom: 1px dotted white;
141
- }
142
-
143
- .status-label {
144
- font-family: monospace;
145
- padding: 3px 6px;
146
- background: rgba(0,0,0,0.2);
147
- border-radius: 3px;
148
- font-size: 12px;
149
- margin: 0 12px 0 0;
150
-
151
- &.deleted {
152
- background: #99999933;
153
- }
154
- &.failed, &.complete {
155
- background: #99000033;
156
- }
157
- &.success {
158
- background: #00990033;
159
- }
160
- }
161
-
162
- .status-block {
163
- width: 12em;
164
- text-align: center;
165
- }
166
- }
167
-
168
- > .subitems {
169
- padding-left: 16px;
170
-
171
- >.load-more {
172
- padding: 4px 0;
173
- text-align: center;
174
- border-bottom: 1px dashed white;
175
- a {
176
- border-bottom: 1px dotted white;
177
- text-decoration: none;
178
- }
179
- }
180
- }
181
- }
182
- }