canvas_sync 0.27.11 → 0.27.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83ea16b4fb4ba5a744d39a92d8145e5ebf1123355e5c6562094f91fe5d523176
4
- data.tar.gz: 7505711d374c3f86648ce435234668df10a6c032dc6546cdf144c4b6d9d944f9
3
+ metadata.gz: 465e968a497bd2dc0bbba5d978bc37a42257122f74132e2f9d7a72c4ed7f9082
4
+ data.tar.gz: 113288ee3a4bea44ba48d837f917f111e8e0bb9de2f8e43c0d5e30eb78aa0361
5
5
  SHA512:
6
- metadata.gz: 9906d9f164b5cdae293e6ca6e249a7793988c951ea7f3d84500364e8e88f2184e8f1fc58d54e762f4854d4fa5f755115a99759fdeea1f3b5d56bde94181aca1a
7
- data.tar.gz: a0b592ee83e841af466cd06d66f564d9e731f7398a9f400fa96da4807a0027dd5575163b60cc10351730b017bc15ff18fbbc8f2073ef48a2724d5529d70bbfd4
6
+ metadata.gz: 2afe2d77cc726e26bdce33058d89bfd279f890dd5832c30c33575960387111f309ac15f6f2632406861cb1f3a4e34ce0ff16509612bb987f3e96974ceabc327b
7
+ data.tar.gz: 3c926ea08522792eae8fd6daaafcc343888aed77b55e943d1fca08a8ddf174d01db1fc26ebe786cd613eab18dc0d9d15221a2fe739a5a46d4dda4e626e48f498
@@ -27,10 +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
- state = report_batch.context
30
+ state = report_batch&.context
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?
36
+
32
37
  cls = state[:report_class].constantize
33
- cls.new(state[:report_options], base_context, state)
38
+ cls.new(state[:report_options], report_batch.parent&.context, state)
34
39
  end
35
40
 
36
41
  def perform_later(options)
@@ -10,7 +10,8 @@ module CanvasSync
10
10
  # @param options [Hash]
11
11
  def perform(options)
12
12
  unless options[:root_account] == false
13
- accid = report_task.options[:account_id] || batch_context[:account_id] || "self"
13
+ task = ReportSyncTask.from_context || nil
14
+ accid = options[:account_id] || task&.options&.dig(:account_id) || batch_context[:account_id] || "self"
14
15
  acc_params = CanvasSync.get_canvas_sync_client(batch_context).account(accid)
15
16
  update_or_create_model(Account, acc_params)
16
17
  end
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.27.11".freeze
2
+ VERSION = "0.27.13".freeze
3
3
  end
data/lib/canvas_sync.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "active_support"
1
2
  require "bearcat"
2
3
 
3
4
  require "canvas_sync/version"
@@ -170,22 +170,28 @@ RSpec.describe CanvasSync::Jobs::BeginSyncChainJob do
170
170
  end
171
171
 
172
172
  context 'a "<weekday>/<skip weeks>" schedule (e.g. "sunday/2")' do
173
+ # should_full_sync? asks DateTime.now (the system zone) which weekday it is, so
174
+ # these travel to local noon. Midnight in any single zone lands on the adjacent
175
+ # day for someone, which flips the weekday and the assertion with it.
176
+ let(:sunday) { Time.local(2024, 1, 7, 12, 0, 0) }
177
+ let(:monday) { Time.local(2024, 1, 8, 12, 0, 0) }
178
+
173
179
  it 'is true only on the matching weekday once the skip period has elapsed' do
174
- Timecop.travel(DateTime.new(2024, 1, 7)) do # a Sunday
180
+ Timecop.travel(sunday) do
175
181
  CanvasSync::SyncBatch.create!(status: 'completed', full_sync: true, batch_genre: 'default', started_at: 3.weeks.ago)
176
182
  expect(job.should_full_sync?('sunday/2')).to eq(true)
177
183
  end
178
184
  end
179
185
 
180
186
  it 'is false on the matching weekday if the skip period has not yet elapsed' do
181
- Timecop.travel(DateTime.new(2024, 1, 7)) do # a Sunday
187
+ Timecop.travel(sunday) do
182
188
  CanvasSync::SyncBatch.create!(status: 'completed', full_sync: true, batch_genre: 'default', started_at: 1.week.ago)
183
189
  expect(job.should_full_sync?('sunday/2')).to eq(false)
184
190
  end
185
191
  end
186
192
 
187
193
  it 'is false on a non-matching weekday even if the skip period has elapsed' do
188
- Timecop.travel(DateTime.new(2024, 1, 8)) do # a Monday
194
+ Timecop.travel(monday) do
189
195
  CanvasSync::SyncBatch.create!(status: 'completed', full_sync: true, batch_genre: 'default', started_at: 3.weeks.ago)
190
196
  expect(job.should_full_sync?('sunday/2')).to eq(false)
191
197
  end
@@ -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
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::Jobs::SyncAccountsJob do
4
+ describe '#perform' do
5
+ before do
6
+ allow(CanvasSync::Jobs::SyncAccountsJob::ReportTask).to receive(:perform_later)
7
+ end
8
+
9
+ it 'syncs the root account and then invokes the report task' do
10
+ expect {
11
+ described_class.perform_now({})
12
+ }.to change { Account.count }.by(1)
13
+
14
+ expect(a_request(:get, 'http://test.instructure.com/api/v1/accounts/self')).to have_been_made
15
+ expect(CanvasSync::Jobs::SyncAccountsJob::ReportTask).to have_received(:perform_later).with({})
16
+ end
17
+
18
+ it 'uses account_id from the options when present' do
19
+ described_class.perform_now({ account_id: 42 })
20
+
21
+ expect(a_request(:get, 'http://test.instructure.com/api/v1/accounts/42')).to have_been_made
22
+ end
23
+
24
+ it 'falls back to account_id from the batch context' do
25
+ set_batch_context(account_id: 7)
26
+
27
+ described_class.perform_now({})
28
+
29
+ expect(a_request(:get, 'http://test.instructure.com/api/v1/accounts/7')).to have_been_made
30
+ end
31
+
32
+ it 'skips the root account sync when root_account is false' do
33
+ expect {
34
+ described_class.perform_now({ root_account: false })
35
+ }.not_to change { Account.count }
36
+
37
+ expect(a_request(:get, %r{/api/v1/accounts/})).not_to have_been_made
38
+ expect(CanvasSync::Jobs::SyncAccountsJob::ReportTask).to have_received(:perform_later)
39
+ end
40
+ end
41
+ end
data/spec/spec_helper.rb CHANGED
@@ -78,7 +78,9 @@ def open_canvas_fixture(name)
78
78
  JSON.parse(data)
79
79
  end
80
80
 
81
- def canvas_sync_client
81
+ # Mirrors the host-app hook CanvasSync.get_canvas_sync_client calls. It passes an
82
+ # account ID when one is configured, so this has to accept an optional argument.
83
+ def canvas_sync_client(account_id = nil)
82
84
  Bearcat::Client.new(token: 'cool-token', prefix: 'http://test.instructure.com')
83
85
  end
84
86
 
@@ -16,6 +16,10 @@ class FakeCanvas < Sinatra::Base
16
16
  json_response 200, 'admins.json'
17
17
  end
18
18
 
19
+ get '/api/v1/accounts/:id' do
20
+ json_response 200, 'account.json'
21
+ end
22
+
19
23
  get '/sample_report_download' do
20
24
  json_response 200, 'terms.json'
21
25
  end
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": 1,
3
+ "name": "Test Root Account",
4
+ "workflow_state": "active",
5
+ "parent_account_id": null,
6
+ "root_account_id": null,
7
+ "uuid": "abcDEF1234567890abcDEF1234567890abcDEF12",
8
+ "default_storage_quota_mb": 500,
9
+ "default_user_storage_quota_mb": 50,
10
+ "default_group_storage_quota_mb": 50,
11
+ "default_time_zone": "America/Denver"
12
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canvas_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.11
4
+ version: 0.27.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure CustomDev
@@ -413,14 +413,14 @@ dependencies:
413
413
  requirements:
414
414
  - - "~>"
415
415
  - !ruby/object:Gem::Version
416
- version: 0.2.0
416
+ version: 0.2.1
417
417
  type: :runtime
418
418
  prerelease: false
419
419
  version_requirements: !ruby/object:Gem::Requirement
420
420
  requirements:
421
421
  - - "~>"
422
422
  - !ruby/object:Gem::Version
423
- version: 0.2.0
423
+ version: 0.2.1
424
424
  email:
425
425
  - pseng@instructure.com
426
426
  executables: []
@@ -577,6 +577,7 @@ files:
577
577
  - spec/canvas_sync/jobs/job_spec.rb
578
578
  - spec/canvas_sync/jobs/report_starter_spec.rb
579
579
  - spec/canvas_sync/jobs/report_sync_task_spec.rb
580
+ - spec/canvas_sync/jobs/sync_accounts_job_spec.rb
580
581
  - spec/canvas_sync/jobs/sync_admins_job_spec.rb
581
582
  - spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb
582
583
  - spec/canvas_sync/jobs/sync_roles_job_spec.rb
@@ -711,6 +712,7 @@ files:
711
712
  - spec/internal/db/schema.rb
712
713
  - spec/spec_helper.rb
713
714
  - spec/support/fake_canvas.rb
715
+ - spec/support/fixtures/canvas_responses/account.json
714
716
  - spec/support/fixtures/canvas_responses/admins.json
715
717
  - spec/support/fixtures/canvas_responses/module.json
716
718
  - spec/support/fixtures/canvas_responses/module_item.json
@@ -776,6 +778,7 @@ test_files:
776
778
  - spec/canvas_sync/jobs/job_spec.rb
777
779
  - spec/canvas_sync/jobs/report_starter_spec.rb
778
780
  - spec/canvas_sync/jobs/report_sync_task_spec.rb
781
+ - spec/canvas_sync/jobs/sync_accounts_job_spec.rb
779
782
  - spec/canvas_sync/jobs/sync_admins_job_spec.rb
780
783
  - spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb
781
784
  - spec/canvas_sync/jobs/sync_roles_job_spec.rb
@@ -910,6 +913,7 @@ test_files:
910
913
  - spec/internal/db/schema.rb
911
914
  - spec/spec_helper.rb
912
915
  - spec/support/fake_canvas.rb
916
+ - spec/support/fixtures/canvas_responses/account.json
913
917
  - spec/support/fixtures/canvas_responses/admins.json
914
918
  - spec/support/fixtures/canvas_responses/module.json
915
919
  - spec/support/fixtures/canvas_responses/module_item.json