canvas_sync 0.27.12 → 0.27.14

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 (234) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/canvas_sync/api/v1/health_check_controller.rb +0 -0
  3. data/app/models/canvas_sync/job_log.rb +0 -0
  4. data/app/models/canvas_sync/sync_batch.rb +0 -0
  5. data/config/initializers/apartment.rb +0 -0
  6. data/config/routes.rb +0 -0
  7. data/db/migrate/20170915210836_create_canvas_sync_job_log.rb +0 -0
  8. data/db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb +0 -0
  9. data/db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb +0 -0
  10. data/db/migrate/20201018210836_create_canvas_sync_sync_batches.rb +0 -0
  11. data/db/migrate/20201030210836_add_full_sync_to_canvas_sync_sync_batch.rb +0 -0
  12. data/lib/canvas_sync/api_syncable.rb +0 -0
  13. data/lib/canvas_sync/batch_processor.rb +0 -0
  14. data/lib/canvas_sync/class_callback_executor.rb +0 -0
  15. data/lib/canvas_sync/concerns/ability_helper.rb +43 -2
  16. data/lib/canvas_sync/concerns/account/ancestry.rb +0 -0
  17. data/lib/canvas_sync/concerns/account/base.rb +0 -0
  18. data/lib/canvas_sync/concerns/api_syncable.rb +0 -0
  19. data/lib/canvas_sync/concerns/canvas_id_as_primary.rb +0 -0
  20. data/lib/canvas_sync/concerns/jit_syncing.rb +0 -0
  21. data/lib/canvas_sync/concerns/legacy_columns.rb +0 -0
  22. data/lib/canvas_sync/concerns/role/base.rb +0 -0
  23. data/lib/canvas_sync/generators/install_generator.rb +0 -0
  24. data/lib/canvas_sync/generators/install_live_events_generator.rb +0 -0
  25. data/lib/canvas_sync/generators/templates/migrations/create_accounts.rb +0 -0
  26. data/lib/canvas_sync/generators/templates/migrations/create_admins.rb +0 -0
  27. data/lib/canvas_sync/generators/templates/migrations/create_assignment_groups.rb +0 -0
  28. data/lib/canvas_sync/generators/templates/migrations/create_assignments.rb +0 -0
  29. data/lib/canvas_sync/generators/templates/migrations/create_content_migrations.rb +0 -0
  30. data/lib/canvas_sync/generators/templates/migrations/create_context_module_items.rb +0 -0
  31. data/lib/canvas_sync/generators/templates/migrations/create_context_modules.rb +0 -0
  32. data/lib/canvas_sync/generators/templates/migrations/create_course_nicknames.rb +0 -0
  33. data/lib/canvas_sync/generators/templates/migrations/create_course_progresses.rb +0 -0
  34. data/lib/canvas_sync/generators/templates/migrations/create_grading_period_groups.rb +0 -0
  35. data/lib/canvas_sync/generators/templates/migrations/create_grading_periods.rb +0 -0
  36. data/lib/canvas_sync/generators/templates/migrations/create_group_memberships.rb +0 -0
  37. data/lib/canvas_sync/generators/templates/migrations/create_groups.rb +0 -0
  38. data/lib/canvas_sync/generators/templates/migrations/create_learning_outcome_results.rb +0 -0
  39. data/lib/canvas_sync/generators/templates/migrations/create_learning_outcomes.rb +0 -0
  40. data/lib/canvas_sync/generators/templates/migrations/create_pseudonyms.rb +0 -0
  41. data/lib/canvas_sync/generators/templates/migrations/create_rubric_assessments.rb +0 -0
  42. data/lib/canvas_sync/generators/templates/migrations/create_rubric_associations.rb +0 -0
  43. data/lib/canvas_sync/generators/templates/migrations/create_rubrics.rb +0 -0
  44. data/lib/canvas_sync/generators/templates/migrations/create_sections.rb +0 -0
  45. data/lib/canvas_sync/generators/templates/migrations/create_submissions.rb +0 -0
  46. data/lib/canvas_sync/generators/templates/migrations/create_terms.rb +0 -0
  47. data/lib/canvas_sync/generators/templates/migrations/create_user_observers.rb +0 -0
  48. data/lib/canvas_sync/generators/templates/migrations/create_users.rb +0 -0
  49. data/lib/canvas_sync/generators/templates/models/content_migration.rb +0 -0
  50. data/lib/canvas_sync/generators/templates/models/course_nickname.rb +0 -0
  51. data/lib/canvas_sync/generators/templates/models/rubric.rb +0 -0
  52. data/lib/canvas_sync/generators/templates/models/rubric_assessment.rb +0 -0
  53. data/lib/canvas_sync/generators/templates/models/rubric_association.rb +0 -0
  54. data/lib/canvas_sync/generators/templates/services/live_events/assignment_event.rb +0 -0
  55. data/lib/canvas_sync/generators/templates/services/live_events/assignment_group_event.rb +0 -0
  56. data/lib/canvas_sync/generators/templates/services/live_events/course_event.rb +0 -0
  57. data/lib/canvas_sync/generators/templates/services/live_events/course_section_event.rb +0 -0
  58. data/lib/canvas_sync/generators/templates/services/live_events/enrollment_event.rb +0 -0
  59. data/lib/canvas_sync/generators/templates/services/live_events/grade_event.rb +0 -0
  60. data/lib/canvas_sync/generators/templates/services/live_events/module_event.rb +0 -0
  61. data/lib/canvas_sync/generators/templates/services/live_events/module_item_event.rb +0 -0
  62. data/lib/canvas_sync/generators/templates/services/live_events/submission_event.rb +0 -0
  63. data/lib/canvas_sync/generators/templates/services/live_events/syllabus_event.rb +0 -0
  64. data/lib/canvas_sync/generators/templates/services/live_events/user_event.rb +0 -0
  65. data/lib/canvas_sync/importers/legacy_importer.rb +0 -0
  66. data/lib/canvas_sync/jobs/begin_sync_chain_job.rb +0 -0
  67. data/lib/canvas_sync/jobs/canvas_process_waiter.rb +0 -0
  68. data/lib/canvas_sync/jobs/report_sync_task.rb +6 -3
  69. data/lib/canvas_sync/jobs/sync_admins_job.rb +0 -0
  70. data/lib/canvas_sync/jobs/sync_assignment_groups_job.rb +0 -0
  71. data/lib/canvas_sync/jobs/sync_assignments_job.rb +0 -0
  72. data/lib/canvas_sync/jobs/sync_content_migrations_job.rb +0 -0
  73. data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +0 -0
  74. data/lib/canvas_sync/jobs/sync_context_modules_job.rb +0 -0
  75. data/lib/canvas_sync/jobs/sync_rubrics_job.rb +0 -0
  76. data/lib/canvas_sync/jobs/sync_simple_table_job.rb +0 -0
  77. data/lib/canvas_sync/live_events/base_handler.rb +0 -0
  78. data/lib/canvas_sync/live_events.rb +0 -0
  79. data/lib/canvas_sync/misc_helper.rb +0 -0
  80. data/lib/canvas_sync/processors/normal_processor.rb +0 -0
  81. data/lib/canvas_sync/sidekiq_job.rb +0 -0
  82. data/lib/canvas_sync/version.rb +1 -1
  83. data/spec/canvas_sync/concerns/ability_helper_spec.rb +165 -0
  84. data/spec/canvas_sync/jobs/canvas_process_waiter_spec.rb +0 -0
  85. data/spec/canvas_sync/jobs/report_starter_spec.rb +0 -0
  86. data/spec/canvas_sync/jobs/report_sync_task_spec.rb +56 -0
  87. data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +0 -0
  88. data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +0 -0
  89. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +0 -0
  90. data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +0 -0
  91. data/spec/canvas_sync/jobs/sync_terms_job_spec.rb +0 -0
  92. data/spec/canvas_sync/live_events/live_event_sync_spec.rb +0 -0
  93. data/spec/canvas_sync/live_events/live_events_controller_spec.rb +0 -0
  94. data/spec/canvas_sync/misc_helper_spec.rb +0 -0
  95. data/spec/canvas_sync/models/accounts_spec.rb +0 -0
  96. data/spec/canvas_sync/models/admins_spec.rb +0 -0
  97. data/spec/canvas_sync/models/assignment_group_spec.rb +0 -0
  98. data/spec/canvas_sync/models/assignment_spec.rb +0 -0
  99. data/spec/canvas_sync/models/context_module_item_spec.rb +0 -0
  100. data/spec/canvas_sync/models/context_module_spec.rb +0 -0
  101. data/spec/canvas_sync/models/course_spec.rb +0 -0
  102. data/spec/canvas_sync/models/enrollment_spec.rb +0 -0
  103. data/spec/canvas_sync/models/group_membership_spec.rb +0 -0
  104. data/spec/canvas_sync/models/group_spec.rb +0 -0
  105. data/spec/canvas_sync/models/roles_spec.rb +0 -0
  106. data/spec/canvas_sync/models/section_spec.rb +0 -0
  107. data/spec/canvas_sync/models/submission_spec.rb +0 -0
  108. data/spec/canvas_sync/models/term_spec.rb +0 -0
  109. data/spec/canvas_sync/models/user_spec.rb +0 -0
  110. data/spec/canvas_sync/processors/assignment_groups_processor_spec.rb +0 -0
  111. data/spec/canvas_sync/processors/assignments_processor_spec.rb +0 -0
  112. data/spec/canvas_sync/processors/content_migrations_processor_spec.rb +0 -0
  113. data/spec/canvas_sync/processors/context_module_items_processor_spec.rb +0 -0
  114. data/spec/canvas_sync/processors/context_modules_processor_spec.rb +0 -0
  115. data/spec/canvas_sync/processors/course_completion_report_processor_spec.rb +0 -0
  116. data/spec/canvas_sync/processors/rubric_assessments_spec.rb +0 -0
  117. data/spec/canvas_sync/processors/rubric_associations_spec.rb +0 -0
  118. data/spec/canvas_sync/processors/rubrics_processor_spec.rb +0 -0
  119. data/spec/canvas_sync/processors/submissions_processor_spec.rb +0 -0
  120. data/spec/canvas_sync/services/module_event_spec.rb +0 -0
  121. data/spec/canvas_sync/services/module_item_event_spec.rb +0 -0
  122. data/spec/factories/admin_factory.rb +0 -0
  123. data/spec/factories/assignment_factory.rb +0 -0
  124. data/spec/factories/assignment_group_factory.rb +0 -0
  125. data/spec/factories/context_module_factory.rb +0 -0
  126. data/spec/factories/context_module_item_factory.rb +0 -0
  127. data/spec/factories/course_factory.rb +0 -0
  128. data/spec/factories/enrollment_factory.rb +0 -0
  129. data/spec/factories/group_factory.rb +0 -0
  130. data/spec/factories/group_membership_factory.rb +0 -0
  131. data/spec/factories/role_factory.rb +0 -0
  132. data/spec/factories/section_factory.rb +0 -0
  133. data/spec/factories/submission_factory.rb +0 -0
  134. data/spec/factories/term_factory.rb +0 -0
  135. data/spec/factories/user_factory.rb +0 -0
  136. data/spec/support/fixtures/canvas_responses/admins.json +0 -0
  137. data/spec/support/fixtures/canvas_responses/module.json +0 -0
  138. data/spec/support/fixtures/canvas_responses/module_item.json +0 -0
  139. data/spec/support/fixtures/canvas_responses/roles.json +0 -0
  140. data/spec/support/fixtures/canvas_responses/terms.json +0 -0
  141. data/spec/support/fixtures/reports/assignment_groups.csv +0 -0
  142. data/spec/support/fixtures/reports/assignments.csv +0 -0
  143. data/spec/support/fixtures/reports/content_migrations.csv +0 -0
  144. data/spec/support/fixtures/reports/context_module_items.csv +0 -0
  145. data/spec/support/fixtures/reports/context_modules.csv +0 -0
  146. data/spec/support/fixtures/reports/course_nicknames.csv +0 -0
  147. data/spec/support/fixtures/reports/course_progresses.csv +0 -0
  148. data/spec/support/fixtures/reports/course_progresses_conflict.csv +0 -0
  149. data/spec/support/fixtures/reports/grading_period_groups.csv +0 -0
  150. data/spec/support/fixtures/reports/grading_periods.csv +0 -0
  151. data/spec/support/fixtures/reports/group_memberships.csv +0 -0
  152. data/spec/support/fixtures/reports/groups.csv +0 -0
  153. data/spec/support/fixtures/reports/learning_outcome_results.csv +0 -0
  154. data/spec/support/fixtures/reports/learning_outcomes.csv +0 -0
  155. data/spec/support/fixtures/reports/provisioning_csv +0 -0
  156. data/spec/support/fixtures/reports/rubric_assessments.csv +0 -0
  157. data/spec/support/fixtures/reports/rubric_associations.csv +0 -0
  158. data/spec/support/fixtures/reports/rubrics.csv +0 -0
  159. data/spec/support/fixtures/reports/sections.csv +0 -0
  160. data/spec/support/fixtures/reports/submissions.csv +0 -0
  161. data/spec/support/fixtures/reports/user_observers.csv +0 -0
  162. data/spec/support/fixtures/reports/users.csv +0 -0
  163. data/spec/support/fixtures/reports/xlist.csv +0 -0
  164. metadata +5 -143
  165. data/spec/internal/app/models/account.rb +0 -35
  166. data/spec/internal/app/models/admin.rb +0 -35
  167. data/spec/internal/app/models/assignment.rb +0 -39
  168. data/spec/internal/app/models/assignment_group.rb +0 -31
  169. data/spec/internal/app/models/assignment_override.rb +0 -34
  170. data/spec/internal/app/models/content_migration.rb +0 -18
  171. data/spec/internal/app/models/context_module.rb +0 -69
  172. data/spec/internal/app/models/context_module_item.rb +0 -33
  173. data/spec/internal/app/models/course.rb +0 -57
  174. data/spec/internal/app/models/course_nickname.rb +0 -20
  175. data/spec/internal/app/models/course_progress.rb +0 -29
  176. data/spec/internal/app/models/enrollment.rb +0 -48
  177. data/spec/internal/app/models/grading_period.rb +0 -17
  178. data/spec/internal/app/models/grading_period_group.rb +0 -16
  179. data/spec/internal/app/models/group.rb +0 -28
  180. data/spec/internal/app/models/group_membership.rb +0 -26
  181. data/spec/internal/app/models/learning_outcome.rb +0 -30
  182. data/spec/internal/app/models/learning_outcome_result.rb +0 -54
  183. data/spec/internal/app/models/pseudonym.rb +0 -19
  184. data/spec/internal/app/models/role.rb +0 -27
  185. data/spec/internal/app/models/rubric.rb +0 -36
  186. data/spec/internal/app/models/rubric_assessment.rb +0 -23
  187. data/spec/internal/app/models/rubric_association.rb +0 -20
  188. data/spec/internal/app/models/score.rb +0 -18
  189. data/spec/internal/app/models/section.rb +0 -36
  190. data/spec/internal/app/models/submission.rb +0 -32
  191. data/spec/internal/app/models/term.rb +0 -46
  192. data/spec/internal/app/models/user.rb +0 -43
  193. data/spec/internal/app/models/user_observer.rb +0 -22
  194. data/spec/internal/app/services/live_events/assignment_event.rb +0 -43
  195. data/spec/internal/app/services/live_events/course_event.rb +0 -28
  196. data/spec/internal/app/services/live_events/course_section_event.rb +0 -28
  197. data/spec/internal/app/services/live_events/enrollment_event.rb +0 -30
  198. data/spec/internal/app/services/live_events/grade_event.rb +0 -36
  199. data/spec/internal/app/services/live_events/module_event.rb +0 -26
  200. data/spec/internal/app/services/live_events/module_item_event.rb +0 -25
  201. data/spec/internal/app/services/live_events/submission_event.rb +0 -28
  202. data/spec/internal/app/services/live_events/syllabus_event.rb +0 -31
  203. data/spec/internal/app/services/live_events/user_event.rb +0 -27
  204. data/spec/internal/db/migrate/20251029222307_create_users.rb +0 -26
  205. data/spec/internal/db/migrate/20251029222308_create_pseudonyms.rb +0 -24
  206. data/spec/internal/db/migrate/20251029222309_create_courses.rb +0 -30
  207. data/spec/internal/db/migrate/20251029222310_create_groups.rb +0 -29
  208. data/spec/internal/db/migrate/20251029222311_create_group_memberships.rb +0 -24
  209. data/spec/internal/db/migrate/20251029222312_create_accounts.rb +0 -23
  210. data/spec/internal/db/migrate/20251029222313_create_terms.rb +0 -24
  211. data/spec/internal/db/migrate/20251029222314_create_enrollments.rb +0 -31
  212. data/spec/internal/db/migrate/20251029222315_create_sections.rb +0 -27
  213. data/spec/internal/db/migrate/20251029222316_create_assignments.rb +0 -37
  214. data/spec/internal/db/migrate/20251029222317_create_submissions.rb +0 -32
  215. data/spec/internal/db/migrate/20251029222318_create_roles.rb +0 -24
  216. data/spec/internal/db/migrate/20251029222319_create_admins.rb +0 -26
  217. data/spec/internal/db/migrate/20251029222320_create_assignment_groups.rb +0 -28
  218. data/spec/internal/db/migrate/20251029222321_create_context_modules.rb +0 -28
  219. data/spec/internal/db/migrate/20251029222322_create_context_module_items.rb +0 -26
  220. data/spec/internal/db/migrate/20251029222324_create_grading_periods.rb +0 -28
  221. data/spec/internal/db/migrate/20251029222325_create_grading_period_groups.rb +0 -24
  222. data/spec/internal/db/migrate/20251029222326_create_content_migrations.rb +0 -30
  223. data/spec/internal/db/migrate/20251029222327_create_learning_outcomes.rb +0 -36
  224. data/spec/internal/db/migrate/20251029222328_create_learning_outcome_results.rb +0 -52
  225. data/spec/internal/db/migrate/20251029222330_create_rubrics.rb +0 -44
  226. data/spec/internal/db/migrate/20251029222331_create_rubric_associations.rb +0 -42
  227. data/spec/internal/db/migrate/20251029222332_create_rubric_assessments.rb +0 -37
  228. data/spec/internal/db/migrate/20251029222334_create_assignment_overrides.rb +0 -25
  229. data/spec/internal/db/migrate/20251029222335_create_scores.rb +0 -42
  230. data/spec/internal/db/migrate/20251029223411_create_user_observers.rb +0 -23
  231. data/spec/internal/db/migrate/20251029224009_create_course_nicknames.rb +0 -23
  232. data/spec/internal/db/migrate/20251029224010_create_course_progresses.rb +0 -26
  233. data/spec/support/fixtures/reports/provisioning_csv_unzipped/courses.csv +0 -3
  234. data/spec/support/fixtures/reports/provisioning_csv_unzipped/users.csv +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0cb609547db9a99f1706791e88585c33a02d1d23d0163550ca042fe7a6fc6c2
4
- data.tar.gz: 16685b41f9bbea87d8c3df59e26ede34d1de8d087204727d0a62af4b8ccdc1de
3
+ metadata.gz: 2d0d5545af44d472c6222d3d26833a90f795d05ce045eb029bc493b542d16799
4
+ data.tar.gz: caffe0593b173610b3a56f60459b2ba4fb8dc8d7239e018f766856f9b49cc1a1
5
5
  SHA512:
6
- metadata.gz: 7815652b4cfdf053ee6f30e8546af592bf60c4e0c21538667d3608d8dd90557f3b4543552a6ea901a0d2027073fe764f06a014afba5ab72004e50b1fc10da325
7
- data.tar.gz: 7db6393f537f9f585086166baf39f988da1f146e0cbf79ada0a7e015734f7e139757593e344940e65160add7efc8090638b1990a1e575ff772ee53c7949f00d5
6
+ metadata.gz: e507c8517386c294feaa221a8d7b28dd82ac9b578b08bf8dfce7398b73f653f79169251768a80e3f6fecd83000b0cce99448c505178538b0700ace4596513e0f
7
+ data.tar.gz: a543d2656184f1150bbb5ff38bdaeb6a4fe2e3d18efa7b94ff1364080715ef92f12ea43ede0059c7c104f8c9878b151559fffaa3865de4555114095706fdf223
File without changes
File without changes
File without changes
data/config/routes.rb CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -98,18 +98,59 @@ module CanvasSync::Concerns
98
98
  canvas_roles.where.not(base_role_type: 'AccountMembership')
99
99
  end
100
100
 
101
+ ACCOUNT_ADMIN_ROLE_LABELS = ["AccountAdmin", "Account Admin"].freeze
102
+
103
+ # True for an Admin of the Account this Tool is installed on. Note that
104
+ # `:root` is PandaPal's install-Account sense of "root" — the top of what
105
+ # this Tool can see — and not necessarily Canvas's own root Account.
101
106
  def canvas_super_user?
102
107
  canvas_account_admin?(:root)
103
108
  end
104
109
 
110
+ # True when the User administers +account+, INCLUDING by way of an Admin
111
+ # role held on an Account above it: in Canvas an Account Admin administers
112
+ # their Account and everything beneath it, so checking only the exact
113
+ # Account under-reports. Where a Tool is installed on a sub-account, that
114
+ # under-reporting inverted this check both ways — a genuine higher-level
115
+ # admin was rejected while a lower-scoped one was accepted.
105
116
  def canvas_account_admin?(account = launch_account)
106
- panda_pal_session.cache([:canvas_account_admin?, account]) do
107
- panda_pal_session.canvas_site_admin? || (panda_pal_session.canvas_account_role_labels(account) & ["AccountAdmin", "Account Admin"]).present?
117
+ # The `:inherited` cache-key segment is load-bearing, not decoration:
118
+ # #cache persists into the Session row, so sessions created before the
119
+ # traversal existed hold a cached `false` under the old key. Keying the
120
+ # new semantics separately retires those answers instead of serving
121
+ # stale denials to already-launched sessions.
122
+ panda_pal_session.cache([:canvas_account_admin?, account, :inherited]) do
123
+ panda_pal_session.canvas_site_admin? ||
124
+ (account_role_labels(account) & ACCOUNT_ADMIN_ROLE_LABELS).present?
108
125
  end
109
126
  end
110
127
 
111
128
  def canvas_user_id
112
129
  user&.canvas_id || panda_pal_session.get_lti_cust_param('canvas_user_id')
113
130
  end
131
+
132
+ private
133
+
134
+ # Feature-detected rather than version-pinned: CanvasSync deliberately
135
+ # declares no dependency on PandaPal (only this concern assumes one), so it
136
+ # cannot require >= 5.17 where `inherited:` was introduced. Detecting the
137
+ # keyword makes the upgrade order between the two gems irrelevant, instead
138
+ # of failing an authorization check with ArgumentError.
139
+ def account_role_labels(account)
140
+ if accepts_inherited_kwarg?
141
+ panda_pal_session.canvas_account_role_labels(account, inherited: true)
142
+ else
143
+ panda_pal_session.canvas_account_role_labels(account)
144
+ end
145
+ end
146
+
147
+ # `**opts` counts as support: a host app that wraps or decorates the Session
148
+ # commonly forwards keywords that way, and treating it as "too old" would
149
+ # silently drop the traversal for exactly the apps most likely to subclass.
150
+ def accepts_inherited_kwarg?
151
+ panda_pal_session.method(:canvas_account_role_labels).parameters.any? do |type, name|
152
+ type == :keyrest || ((type == :key || type == :keyreq) && name == :inherited)
153
+ end
154
+ end
114
155
  end
115
156
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -27,12 +27,15 @@ module CanvasSync
27
27
  class << self
28
28
  def from_context
29
29
  report_batch = JobBatches::Batch.current
30
- base_context = report_batch&.parent&.context
31
30
  state = report_batch&.context
32
- return nil unless state.present?
31
+
32
+ # Only a report Batch carries :report_class. Any other Batch in the chain inherits a
33
+ # context from its parents, so check for the key itself rather than asking whether the
34
+ # context is present.
35
+ return nil if state.nil? || state[:report_class].blank?
33
36
 
34
37
  cls = state[:report_class].constantize
35
- cls.new(state[:report_options], base_context, state)
38
+ cls.new(state[:report_options], report_batch.parent&.context, state)
36
39
  end
37
40
 
38
41
  def perform_later(options)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.27.12".freeze
2
+ VERSION = "0.27.14".freeze
3
3
  end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Focused on what this concern contributes on top of PandaPal: that it asks for
6
+ # INHERITED role labels, and that it keys its Session-persisted cache so the
7
+ # pre-traversal answers don't outlive the change. The traversal itself belongs
8
+ # to (and is tested by) PandaPal::Session#canvas_account_role_labels.
9
+ #
10
+ # panda_pal is not in this gem's bundle, so the session is a hand-rolled double
11
+ # — which is also the only way to assert the exact call that gets made.
12
+ RSpec.describe CanvasSync::Concerns::AbilityHelper do
13
+ let(:helper_class) do
14
+ Class.new do
15
+ include CanvasSync::Concerns::AbilityHelper
16
+
17
+ attr_reader :panda_pal_session
18
+
19
+ def initialize(session)
20
+ @panda_pal_session = session
21
+ end
22
+ end
23
+ end
24
+
25
+ # Shared bookkeeping: real #cache semantics (memoised into the session under
26
+ # an arbitrary key), plus a record of every call made.
27
+ session_base = Class.new do
28
+ attr_reader :cache_keys, :label_calls
29
+
30
+ def initialize(labels: [], site_admin: false, preset_cache: {})
31
+ @labels = labels
32
+ @site_admin = site_admin
33
+ @cache = preset_cache
34
+ @cache_keys = []
35
+ @label_calls = []
36
+ end
37
+
38
+ def cache(key)
39
+ @cache_keys << key
40
+ return @cache[key] if @cache.key?(key)
41
+
42
+ @cache[key] = yield
43
+ end
44
+
45
+ def canvas_site_admin?
46
+ @site_admin
47
+ end
48
+ end
49
+
50
+ # Declared exactly as PandaPal >= 5.17 declares it, so keyword detection runs
51
+ # against a real signature rather than a catch-all that would pass for the
52
+ # wrong reason.
53
+ let(:modern_session) do
54
+ Class.new(session_base) do
55
+ def canvas_account_role_labels(account = 'self', inherited: false)
56
+ @label_calls << [account, { inherited: inherited }]
57
+ @labels
58
+ end
59
+ end
60
+ end
61
+
62
+ # PandaPal < 5.17: no keyword at all. Passing one must not reach it.
63
+ let(:legacy_session) do
64
+ Class.new(session_base) do
65
+ def canvas_account_role_labels(account = 'self')
66
+ @label_calls << [account, {}]
67
+ @labels
68
+ end
69
+ end
70
+ end
71
+
72
+ # A host app decorating the Session and forwarding keywords.
73
+ let(:forwarding_session) do
74
+ Class.new(session_base) do
75
+ def canvas_account_role_labels(account = 'self', **opts)
76
+ @label_calls << [account, opts]
77
+ @labels
78
+ end
79
+ end
80
+ end
81
+
82
+ def helper_for(klass = nil, **kwargs)
83
+ helper_class.new((klass || modern_session).new(**kwargs))
84
+ end
85
+
86
+ describe "#canvas_account_admin?" do
87
+ it "asks PandaPal for inherited role labels" do
88
+ helper = helper_for(labels: ["AccountAdmin"])
89
+
90
+ expect(helper.canvas_account_admin?(300)).to be true
91
+ expect(helper.panda_pal_session.label_calls).to eq([[300, { inherited: true }]])
92
+ end
93
+
94
+ it "accepts the human-readable \"Account Admin\" label" do
95
+ expect(helper_for(labels: ["Account Admin"]).canvas_account_admin?(300)).to be true
96
+ end
97
+
98
+ it "is false when the user holds only non-admin roles anywhere in the chain" do
99
+ expect(helper_for(labels: ["Ops", "Department Admin"]).canvas_account_admin?(300)).to be false
100
+ end
101
+
102
+ it "is true for a Canvas site admin regardless of role labels" do
103
+ expect(helper_for(labels: [], site_admin: true).canvas_account_admin?(300)).to be true
104
+ end
105
+
106
+ it "keys its cache separately from the pre-traversal answer" do
107
+ helper = helper_for(labels: ["AccountAdmin"])
108
+
109
+ helper.canvas_account_admin?(:root)
110
+
111
+ expect(helper.panda_pal_session.cache_keys).to eq([[:canvas_account_admin?, :root, :inherited]])
112
+ end
113
+
114
+ # The regression this guards: a Session launched before the traversal
115
+ # existed has `false` persisted under the OLD key. Reusing that key would
116
+ # keep denying an admin who should now pass.
117
+ it "ignores a stale pre-traversal cache entry" do
118
+ helper = helper_class.new(
119
+ modern_session.new(labels: ["AccountAdmin"], preset_cache: { [:canvas_account_admin?, :root] => false })
120
+ )
121
+
122
+ expect(helper.canvas_account_admin?(:root)).to be true
123
+ end
124
+
125
+ it "still memoises within its own key" do
126
+ helper = helper_for(labels: ["AccountAdmin"])
127
+
128
+ helper.canvas_account_admin?(:root)
129
+ helper.canvas_account_admin?(:root)
130
+
131
+ expect(helper.panda_pal_session.label_calls.size).to eq(1)
132
+ end
133
+ end
134
+
135
+ describe "#canvas_super_user?" do
136
+ it "delegates to canvas_account_admin? for the install Account" do
137
+ helper = helper_for(labels: ["AccountAdmin"])
138
+
139
+ expect(helper.canvas_super_user?).to be true
140
+ expect(helper.panda_pal_session.label_calls).to eq([[:root, { inherited: true }]])
141
+ end
142
+
143
+ it "is false for a user with no admin role above or at the install Account" do
144
+ expect(helper_for(labels: []).canvas_super_user?).to be false
145
+ end
146
+ end
147
+
148
+ # Upgrade-order safety: an app on a CanvasSync with this change but PandaPal
149
+ # < 5.17 must not blow up an authorization check with ArgumentError.
150
+ describe "keyword detection" do
151
+ it "falls back to the non-inherited call against an older PandaPal" do
152
+ helper = helper_for(legacy_session, labels: ["AccountAdmin"])
153
+
154
+ expect(helper.canvas_account_admin?(300)).to be true
155
+ expect(helper.panda_pal_session.label_calls).to eq([[300, {}]])
156
+ end
157
+
158
+ it "treats a keyword-forwarding Session as supporting inherited" do
159
+ helper = helper_for(forwarding_session, labels: ["AccountAdmin"])
160
+
161
+ expect(helper.canvas_account_admin?(300)).to be true
162
+ expect(helper.panda_pal_session.label_calls).to eq([[300, { inherited: true }]])
163
+ end
164
+ end
165
+ end
File without changes
File without changes
@@ -86,6 +86,30 @@ RSpec.describe CanvasSync::Jobs::ReportSyncTask do
86
86
  end
87
87
 
88
88
  describe ".from_context" do
89
+ # Stands in for JobBatches::ContextHash, which reads keys via #[] but routes every other Hash
90
+ # method through #flatten. Flattening loads (and locks down) the whole ancestor chain, so
91
+ # from_context has to stay on #[] - anything else costs the Batch the ability to record its
92
+ # own progress later in the job.
93
+ class FlattenTattlingContext
94
+ attr_reader :flatten_count
95
+
96
+ def initialize(hash)
97
+ @hash = hash.with_indifferent_access
98
+ @flatten_count = 0
99
+ end
100
+
101
+ def [](key)
102
+ @hash[key]
103
+ end
104
+
105
+ def flatten
106
+ @flatten_count += 1
107
+ @hash
108
+ end
109
+
110
+ delegate_missing_to :flatten
111
+ end
112
+
89
113
  it "reconstructs task from batch context" do
90
114
  stub_batch_context({ canvas_term_id: 1 })
91
115
  allow(CanvasSync::JobBatches::Batch.current).to receive(:context)
@@ -96,6 +120,38 @@ RSpec.describe CanvasSync::Jobs::ReportSyncTask do
96
120
  expect(task).to be_a(SyncSpecsTask)
97
121
  expect(task.options).to eq({ include_all: true })
98
122
  end
123
+
124
+ it "returns nil when the context belongs to a non-report Batch" do
125
+ # A Batch elsewhere in the sync chain inherits a context from its parents, so the context is
126
+ # populated - it just has no :report_class.
127
+ stub_batch_context({ canvas_term_id: 1 })
128
+ allow(CanvasSync::JobBatches::Batch.current).to receive(:context)
129
+ .and_return({ sync_batch_id: 42, account_id: 'self' }.with_indifferent_access)
130
+
131
+ expect(CanvasSync::Jobs::ReportSyncTask.from_context).to be_nil
132
+ end
133
+
134
+ it "returns nil when there is no current Batch" do
135
+ allow(CanvasSync::JobBatches::Batch).to receive(:current).and_return(nil)
136
+
137
+ expect(CanvasSync::Jobs::ReportSyncTask.from_context).to be_nil
138
+ end
139
+
140
+ it "does not flatten the context, for a report Batch or any other" do
141
+ report_context = FlattenTattlingContext.new(
142
+ report_class: 'SyncSpecsTask', report_options: { include_all: true }
143
+ )
144
+ other_context = FlattenTattlingContext.new(sync_batch_id: 42)
145
+
146
+ [report_context, other_context].each do |context|
147
+ stub_batch_context({ canvas_term_id: 1 })
148
+ allow(CanvasSync::JobBatches::Batch.current).to receive(:context).and_return(context)
149
+
150
+ CanvasSync::Jobs::ReportSyncTask.from_context
151
+
152
+ expect(context.flatten_count).to eq(0)
153
+ end
154
+ end
99
155
  end
100
156
 
101
157
  describe described_class::StarterJob do
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes