panda_pal 4.1.0.beta3 → 5.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +81 -71
- data/app/models/panda_pal/organization.rb +46 -17
- data/app/views/panda_pal/lti/iframe_cookie_authorize.html.erb +19 -0
- data/db/618eef7c0380ba654ad16f867a919e72.sqlite3 +0 -0
- data/db/9ff93d4f7e0e9dc80a43f68997caf4a1.sqlite3 +0 -0
- data/db/a3fda4044a7215bc2c9eb01a4b9e517a.sqlite3 +0 -0
- data/db/daa0e6378a5ec76fcce83b7070dad219.sqlite3 +0 -0
- data/lib/panda_pal/helpers/controller_helper.rb +60 -15
- data/lib/panda_pal/version.rb +1 -1
- data/panda_pal.gemspec +0 -3
- data/spec/dummy/config/application.rb +1 -7
- data/spec/dummy/config/environments/development.rb +14 -0
- data/spec/dummy/config/environments/production.rb +11 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +15058 -0
- data/spec/dummy/log/test.log +0 -0
- data/spec/models/panda_pal/organization_spec.rb +89 -0
- data/spec/spec_helper.rb +0 -4
- metadata +18 -38
- data/app/models/panda_pal/organization/settings_validation.rb +0 -111
- data/app/models/panda_pal/organization/task_scheduling.rb +0 -172
- data/spec/models/panda_pal/organization/settings_validation_spec.rb +0 -175
- data/spec/models/panda_pal/organization/task_scheduling_spec.rb +0 -144
File without changes
|
@@ -2,6 +2,35 @@ require 'rails_helper'
|
|
2
2
|
|
3
3
|
module PandaPal
|
4
4
|
RSpec.describe Organization, type: :model do
|
5
|
+
|
6
|
+
def set_test_settings_structure
|
7
|
+
PandaPal.lti_options = {
|
8
|
+
title: 'Test App',
|
9
|
+
settings_structure: YAML.load("
|
10
|
+
canvas:
|
11
|
+
is_required: true
|
12
|
+
data_type: Hash
|
13
|
+
api_token:
|
14
|
+
is_required: true
|
15
|
+
data_type: String
|
16
|
+
base_url:
|
17
|
+
is_required: true
|
18
|
+
data_type: String
|
19
|
+
reports:
|
20
|
+
is_required: true
|
21
|
+
data_type: Hash
|
22
|
+
active_term_allowance:
|
23
|
+
submissions_report_time_length:
|
24
|
+
is_required: true
|
25
|
+
data_type: ActiveSupport::Duration
|
26
|
+
recheck_wait:
|
27
|
+
data_type: ActiveSupport::Duration
|
28
|
+
max_recheck_time:
|
29
|
+
is_required: true
|
30
|
+
").deep_symbolize_keys
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
5
34
|
it 'creates a schema upon creation' do
|
6
35
|
expect(Apartment::Tenant).to receive(:create)
|
7
36
|
create :panda_pal_organization
|
@@ -30,5 +59,65 @@ module PandaPal
|
|
30
59
|
org2 = build :panda_pal_organization, salesforce_id: 'salesforce'
|
31
60
|
expect(org2.valid?).to be_falsey
|
32
61
|
end
|
62
|
+
|
63
|
+
context 'settings validation' do
|
64
|
+
let!(:org) { create :panda_pal_organization }
|
65
|
+
|
66
|
+
it 'does not perform any validations if settings is not defined' do
|
67
|
+
PandaPal.lti_options = {}
|
68
|
+
expect_any_instance_of(PandaPal::Organization).not_to receive(:validate_level)
|
69
|
+
expect(org.valid?).to be_truthy
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does not perform any validations if options is not defined' do
|
73
|
+
PandaPal.lti_options = nil
|
74
|
+
expect_any_instance_of(PandaPal::Organization).not_to receive(:validate_level)
|
75
|
+
expect(org.valid?).to be_truthy
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'does perform validations if settings_structure is defined' do
|
79
|
+
set_test_settings_structure
|
80
|
+
org.valid?
|
81
|
+
expect(org.valid?).to be_falsey
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'will fail if a required setting is not present' do
|
85
|
+
set_test_settings_structure
|
86
|
+
expect(org.valid?).to be_falsey
|
87
|
+
errors = org.errors.messages[:settings]
|
88
|
+
expect(errors[0]).to eq("PandaPal::Organization.settings requires key [:canvas]. It was not found.")
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'will fail if a setting is supplied but data_type is wrong' do
|
92
|
+
set_test_settings_structure
|
93
|
+
org.settings = {canvas: "Dog"}
|
94
|
+
expect(org.valid?).to be_falsey
|
95
|
+
errors = org.errors.messages[:settings]
|
96
|
+
expect(errors[0]).to eq("PandaPal::Organization.settings expected key [:canvas] to be Hash but it was instead String.")
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'will fail if a required subsetting is missing' do
|
100
|
+
set_test_settings_structure
|
101
|
+
org.settings = {canvas: {base_url: 'http://'}, reports: {}}
|
102
|
+
expect(org.valid?).to be_falsey
|
103
|
+
errors = org.errors.messages[:settings]
|
104
|
+
expect(errors[0]).to eq("PandaPal::Organization.settings requires key [:canvas][:api_token]. It was not found.")
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'will fail if extra options are specified' do
|
108
|
+
set_test_settings_structure
|
109
|
+
org.settings = {canvas: {base_url: 'http://'}, reports: {}, unknown_option: "WHAT IS THIS?"}
|
110
|
+
expect(org.valid?).to be_falsey
|
111
|
+
errors = org.errors.messages[:settings]
|
112
|
+
expect(errors[0]).to eq("PandaPal::Organization.settings had unexpected key: unknown_option. If settings have expanded please update your lti_options accordingly.")
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'will pass if all structure is maintained' do
|
116
|
+
set_test_settings_structure
|
117
|
+
org.settings = {canvas: {base_url: 'http://', api_token: 'TEST'}, reports: {submissions_report_time_length: 30.minutes, max_recheck_time: 10.hours}}
|
118
|
+
expect(org.valid?).to be_truthy
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
33
122
|
end
|
34
123
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: panda_pal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Instructure ProServe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -114,34 +114,6 @@ dependencies:
|
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: 6.1.2
|
117
|
-
- !ruby/object:Gem::Dependency
|
118
|
-
name: sidekiq
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
120
|
-
requirements:
|
121
|
-
- - ">="
|
122
|
-
- !ruby/object:Gem::Version
|
123
|
-
version: '0'
|
124
|
-
type: :development
|
125
|
-
prerelease: false
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
127
|
-
requirements:
|
128
|
-
- - ">="
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
version: '0'
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: sidekiq-scheduler
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
134
|
-
requirements:
|
135
|
-
- - ">="
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
version: '0'
|
138
|
-
type: :development
|
139
|
-
prerelease: false
|
140
|
-
version_requirements: !ruby/object:Gem::Requirement
|
141
|
-
requirements:
|
142
|
-
- - ">="
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '0'
|
145
117
|
- !ruby/object:Gem::Dependency
|
146
118
|
name: rspec-rails
|
147
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,14 +163,17 @@ files:
|
|
191
163
|
- app/lib/lti_xml/bridge_platform.rb
|
192
164
|
- app/lib/lti_xml/canvas_platform.rb
|
193
165
|
- app/models/panda_pal/organization.rb
|
194
|
-
- app/models/panda_pal/organization/settings_validation.rb
|
195
|
-
- app/models/panda_pal/organization/task_scheduling.rb
|
196
166
|
- app/models/panda_pal/session.rb
|
197
167
|
- app/views/layouts/panda_pal/application.html.erb
|
168
|
+
- app/views/panda_pal/lti/iframe_cookie_authorize.html.erb
|
198
169
|
- app/views/panda_pal/lti/iframe_cookie_fix.html.erb
|
199
170
|
- app/views/panda_pal/lti/launch.html.erb
|
200
171
|
- config/initializers/apartment.rb
|
201
172
|
- config/routes.rb
|
173
|
+
- db/618eef7c0380ba654ad16f867a919e72.sqlite3
|
174
|
+
- db/9ff93d4f7e0e9dc80a43f68997caf4a1.sqlite3
|
175
|
+
- db/a3fda4044a7215bc2c9eb01a4b9e517a.sqlite3
|
176
|
+
- db/daa0e6378a5ec76fcce83b7070dad219.sqlite3
|
202
177
|
- db/migrate/20160412205931_create_panda_pal_organizations.rb
|
203
178
|
- db/migrate/20160413135653_create_panda_pal_sessions.rb
|
204
179
|
- db/migrate/20160425130344_add_panda_pal_organization_to_session.rb
|
@@ -233,6 +208,7 @@ files:
|
|
233
208
|
- spec/dummy/config/environments/development.rb
|
234
209
|
- spec/dummy/config/environments/production.rb
|
235
210
|
- spec/dummy/config/environments/test.rb
|
211
|
+
- spec/dummy/config/initializers/assets.rb
|
236
212
|
- spec/dummy/config/initializers/backtrace_silencers.rb
|
237
213
|
- spec/dummy/config/initializers/cookies_serializer.rb
|
238
214
|
- spec/dummy/config/initializers/filter_parameter_logging.rb
|
@@ -243,15 +219,17 @@ files:
|
|
243
219
|
- spec/dummy/config/locales/en.yml
|
244
220
|
- spec/dummy/config/routes.rb
|
245
221
|
- spec/dummy/config/secrets.yml
|
222
|
+
- spec/dummy/db/development.sqlite3
|
246
223
|
- spec/dummy/db/schema.rb
|
224
|
+
- spec/dummy/db/test.sqlite3
|
225
|
+
- spec/dummy/log/development.log
|
226
|
+
- spec/dummy/log/test.log
|
247
227
|
- spec/dummy/public/404.html
|
248
228
|
- spec/dummy/public/422.html
|
249
229
|
- spec/dummy/public/500.html
|
250
230
|
- spec/dummy/public/favicon.ico
|
251
231
|
- spec/factories/panda_pal_organizations.rb
|
252
232
|
- spec/factories/panda_pal_sessions.rb
|
253
|
-
- spec/models/panda_pal/organization/settings_validation_spec.rb
|
254
|
-
- spec/models/panda_pal/organization/task_scheduling_spec.rb
|
255
233
|
- spec/models/panda_pal/organization_spec.rb
|
256
234
|
- spec/models/panda_pal/session_spec.rb
|
257
235
|
- spec/rails_helper.rb
|
@@ -275,8 +253,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
275
253
|
- !ruby/object:Gem::Version
|
276
254
|
version: 1.3.1
|
277
255
|
requirements: []
|
278
|
-
|
279
|
-
rubygems_version: 2.6.11
|
256
|
+
rubygems_version: 3.1.2
|
280
257
|
signing_key:
|
281
258
|
specification_version: 4
|
282
259
|
summary: LTI mountable engine
|
@@ -306,6 +283,7 @@ test_files:
|
|
306
283
|
- spec/dummy/config/initializers/filter_parameter_logging.rb
|
307
284
|
- spec/dummy/config/initializers/session_store.rb
|
308
285
|
- spec/dummy/config/initializers/wrap_parameters.rb
|
286
|
+
- spec/dummy/config/initializers/assets.rb
|
309
287
|
- spec/dummy/config/initializers/cookies_serializer.rb
|
310
288
|
- spec/dummy/config/initializers/inflections.rb
|
311
289
|
- spec/dummy/config.ru
|
@@ -315,9 +293,11 @@ test_files:
|
|
315
293
|
- spec/dummy/public/500.html
|
316
294
|
- spec/dummy/public/404.html
|
317
295
|
- spec/dummy/db/schema.rb
|
296
|
+
- spec/dummy/db/test.sqlite3
|
297
|
+
- spec/dummy/db/development.sqlite3
|
298
|
+
- spec/dummy/log/test.log
|
299
|
+
- spec/dummy/log/development.log
|
318
300
|
- spec/dummy/README.rdoc
|
319
|
-
- spec/models/panda_pal/organization/settings_validation_spec.rb
|
320
|
-
- spec/models/panda_pal/organization/task_scheduling_spec.rb
|
321
301
|
- spec/models/panda_pal/session_spec.rb
|
322
302
|
- spec/models/panda_pal/organization_spec.rb
|
323
303
|
- spec/factories/panda_pal_sessions.rb
|
@@ -1,111 +0,0 @@
|
|
1
|
-
module PandaPal
|
2
|
-
module OrganizationConcerns
|
3
|
-
module SettingsValidation
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
included do
|
7
|
-
validate :validate_settings
|
8
|
-
end
|
9
|
-
|
10
|
-
class_methods do
|
11
|
-
def settings_structure
|
12
|
-
if PandaPal.lti_options&.[](:settings_structure).present?
|
13
|
-
normalize_settings_structure(PandaPal.lti_options[:settings_structure])
|
14
|
-
else
|
15
|
-
{
|
16
|
-
type: Hash,
|
17
|
-
allow_additional: true,
|
18
|
-
properties: {},
|
19
|
-
}
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def normalize_settings_structure(struc)
|
24
|
-
return {} unless struc.present?
|
25
|
-
return struc if struc[:properties] || struc[:type] || struc.key?(:required)
|
26
|
-
|
27
|
-
struc = struc.dup
|
28
|
-
nstruc = {}
|
29
|
-
|
30
|
-
nstruc[:type] = struc.delete(:data_type) if struc.key?(:data_type)
|
31
|
-
nstruc[:required] = struc.delete(:is_required) if struc.key?(:is_required)
|
32
|
-
nstruc[:properties] = struc.map { |k, sub| [k, normalize_settings_structure(sub)] }.to_h if struc.present?
|
33
|
-
|
34
|
-
nstruc
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def settings_structure
|
39
|
-
self.class.settings_structure
|
40
|
-
end
|
41
|
-
|
42
|
-
def validate_settings
|
43
|
-
validate_settings_level(settings || {}, settings_structure).each do |err|
|
44
|
-
errors[:settings] << err
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def validate_settings_level(settings, spec, path: [], errors: [])
|
51
|
-
human_path = "[:#{path.join('][:')}]"
|
52
|
-
|
53
|
-
if settings.nil?
|
54
|
-
errors << "Entry #{human_path} is required" if spec[:required]
|
55
|
-
return
|
56
|
-
end
|
57
|
-
|
58
|
-
if spec[:type]
|
59
|
-
resolved_type = spec[:type]
|
60
|
-
resolved_type = resolved_type.constantize if resolved_type.is_a?(String)
|
61
|
-
unless settings.is_a?(resolved_type)
|
62
|
-
errors << "Expected #{human_path} to be a #{spec[:type]}. Was a #{settings.class.to_s}"
|
63
|
-
return
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
if spec[:validate].present?
|
68
|
-
val_errors = []
|
69
|
-
if spec[:validate].is_a?(Symbol)
|
70
|
-
proc_result = send(spec[:validate], settings, spec, path: path, errors: val_errors)
|
71
|
-
elsif spec[:validate].is_a?(String)
|
72
|
-
split_val = spec[:validate].split?('.')
|
73
|
-
split_val << 'validate_settings' if split_val.count == 1
|
74
|
-
resolved_module = split_val[0].constantize
|
75
|
-
proc_result = resolved_module.send(split_val[1].to_sym, settings, spec, path: path, errors: val_errors)
|
76
|
-
elsif spec[:validate].is_a?(Proc)
|
77
|
-
proc_result = instance_exec(settings, spec, path: path, errors: val_errors, &spec[:validate])
|
78
|
-
end
|
79
|
-
val_errors << proc_result unless val_errors.present? || proc_result == val_errors
|
80
|
-
val_errors = val_errors.flatten.uniq.compact.map do |ve|
|
81
|
-
ve.gsub('<path>', human_path)
|
82
|
-
end
|
83
|
-
errors.concat(val_errors)
|
84
|
-
end
|
85
|
-
|
86
|
-
if settings.is_a?(Hash)
|
87
|
-
if spec[:properties] != nil
|
88
|
-
spec[:properties].each do |key, pspec|
|
89
|
-
validate_settings_level(settings[key], pspec, path: [*path, key], errors: errors)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
if spec[:properties] != nil || spec[:allow_additional] != nil
|
94
|
-
extra_keys = settings.keys - (spec[:properties]&.keys || [])
|
95
|
-
if extra_keys.present?
|
96
|
-
if spec[:allow_additional].is_a?(Hash)
|
97
|
-
extra_keys.each do |key|
|
98
|
-
validate_settings_level(settings[key], spec[:allow_additional], path: [*path, key], errors: errors)
|
99
|
-
end
|
100
|
-
elsif !spec[:allow_additional]
|
101
|
-
errors << "Did not expect #{human_path} to contain [#{extra_keys.join(', ')}]"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
errors
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
@@ -1,172 +0,0 @@
|
|
1
|
-
return unless defined?(Sidekiq.schedule)
|
2
|
-
|
3
|
-
require_relative 'settings_validation'
|
4
|
-
|
5
|
-
module PandaPal
|
6
|
-
module OrganizationConcerns
|
7
|
-
module TaskScheduling
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
include OrganizationConcerns::SettingsValidation
|
10
|
-
|
11
|
-
included do
|
12
|
-
after_commit :sync_schedule, on: [:create, :update]
|
13
|
-
after_commit :unschedule_tasks, on: :destroy
|
14
|
-
end
|
15
|
-
|
16
|
-
class_methods do
|
17
|
-
def _schedule_descriptors
|
18
|
-
@_schedule_descriptors ||= {}
|
19
|
-
end
|
20
|
-
|
21
|
-
def settings_structure
|
22
|
-
return super unless _schedule_descriptors.present?
|
23
|
-
|
24
|
-
super.tap do |struc|
|
25
|
-
struc[:properties] ||= {}
|
26
|
-
|
27
|
-
struc[:properties][:timezone] ||= {
|
28
|
-
type: 'String',
|
29
|
-
required: false,
|
30
|
-
validate: ->(timezone, *args) {
|
31
|
-
ActiveSupport::TimeZone[timezone].present? ? nil : "<path> Invalid Timezone '#{timezone}'"
|
32
|
-
},
|
33
|
-
}
|
34
|
-
|
35
|
-
struc[:properties][:task_schedules] = {
|
36
|
-
type: 'Hash',
|
37
|
-
required: false,
|
38
|
-
properties: _schedule_descriptors.keys.reduce({}) do |hash, k|
|
39
|
-
hash.tap do |hash|
|
40
|
-
hash[k.to_sym] = hash[k.to_s] = {
|
41
|
-
required: false,
|
42
|
-
validate: ->(value, *args, errors:, **kwargs) {
|
43
|
-
begin
|
44
|
-
Rufus::Scheduler.parse(value) if value
|
45
|
-
nil
|
46
|
-
rescue ArgumentError
|
47
|
-
errors << "<path> must be false or a Crontab string"
|
48
|
-
end
|
49
|
-
}
|
50
|
-
}
|
51
|
-
end
|
52
|
-
end,
|
53
|
-
}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def scheduled_task(cron_time, name_or_method = nil, worker: nil, queue: nil, &block)
|
58
|
-
task_key = (name_or_method.presence || "scheduled_task_#{caller_locations[0].lineno}").to_s
|
59
|
-
raise "Task key '#{task_key}' already taken!" if _schedule_descriptors.key?(task_key)
|
60
|
-
|
61
|
-
_schedule_descriptors[task_key] = {
|
62
|
-
key: task_key,
|
63
|
-
schedule: cron_time,
|
64
|
-
worker: worker || block || name_or_method.to_sym,
|
65
|
-
queue: queue || 'default',
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
def sync_schedules
|
70
|
-
# Ensure deleted Orgs are removed
|
71
|
-
existing_orgs = pluck(:name)
|
72
|
-
old_schedules = Sidekiq.get_schedule.select do |k, v|
|
73
|
-
m = k.match(/^org:([a-z0-9_]+)\-/i)
|
74
|
-
m.present? && !existing_orgs.include?(m[1])
|
75
|
-
end
|
76
|
-
old_schedules.keys.each do |k|
|
77
|
-
Sidekiq.remove_schedule(k)
|
78
|
-
end
|
79
|
-
|
80
|
-
find_each(&:sync_schedule)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def generate_schedule
|
85
|
-
schedule = {}
|
86
|
-
self.class._schedule_descriptors.values.each do |desc|
|
87
|
-
cron_time = schedule_task_cron_time(desc)
|
88
|
-
next unless cron_time.present?
|
89
|
-
|
90
|
-
schedule["org:#{name}-#{desc[:key]}"] = {
|
91
|
-
'cron' => cron_time,
|
92
|
-
'queue' => desc[:queue],
|
93
|
-
'class' => ScheduledTaskExecutor.to_s,
|
94
|
-
'args' => [name, desc[:key]],
|
95
|
-
}
|
96
|
-
end
|
97
|
-
schedule
|
98
|
-
end
|
99
|
-
|
100
|
-
def sync_schedule
|
101
|
-
new_schedules = generate_schedule
|
102
|
-
unschedule_tasks(new_schedules.keys)
|
103
|
-
new_schedules.each do |k, v|
|
104
|
-
Sidekiq.set_schedule(k, v)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def unschedule_tasks(new_task_keys = nil)
|
111
|
-
current_schedules = Sidekiq.get_schedule.select { |k,v| k.starts_with?("org:#{name}-") }
|
112
|
-
del_tasks = current_schedules.keys
|
113
|
-
del_tasks -= new_task_keys if new_task_keys
|
114
|
-
del_tasks.each do |k|
|
115
|
-
Sidekiq.remove_schedule(k)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def schedule_task_cron_time(desc)
|
120
|
-
cron_time = nil
|
121
|
-
cron_time = settings&.dig(:task_schedules, desc[:key].to_s) if cron_time.nil?
|
122
|
-
cron_time = settings&.dig(:task_schedules, desc[:key].to_sym) if cron_time.nil?
|
123
|
-
cron_time = desc[:schedule] if cron_time.nil?
|
124
|
-
|
125
|
-
return nil unless cron_time.present?
|
126
|
-
|
127
|
-
cron_time = instance_exec(&cron_time) if cron_time.is_a?(Proc)
|
128
|
-
if !Rufus::Scheduler.parse(cron_time).zone.present? && settings && settings[:timezone]
|
129
|
-
cron_time += " #{settings[:timezone]}"
|
130
|
-
end
|
131
|
-
|
132
|
-
cron_time
|
133
|
-
end
|
134
|
-
|
135
|
-
class ScheduledTaskExecutor
|
136
|
-
include Sidekiq::Worker
|
137
|
-
|
138
|
-
def perform(org_name, task_key)
|
139
|
-
org = Organization.find_by!(name: org_name)
|
140
|
-
task = Organization._schedule_descriptors[task_key]
|
141
|
-
worker = task[:worker]
|
142
|
-
|
143
|
-
Apartment::Tenant.switch(org.name) do
|
144
|
-
if worker.is_a?(Proc)
|
145
|
-
org.instance_exec(&worker)
|
146
|
-
elsif worker.is_a?(Symbol)
|
147
|
-
org.send(worker)
|
148
|
-
elsif worker.is_a?(String)
|
149
|
-
worker.constantize.perform_async
|
150
|
-
elsif worker.is_a?(Class)
|
151
|
-
worker.perform_async
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
SidekiqScheduler::Scheduler.instance.dynamic = true
|
161
|
-
|
162
|
-
module SidekiqScheduler
|
163
|
-
module Schedule
|
164
|
-
original_schedule_setter = instance_method(:schedule=)
|
165
|
-
|
166
|
-
define_method :schedule= do |sched|
|
167
|
-
original_schedule_setter.bind(self).(sched).tap do
|
168
|
-
PandaPal::Organization.sync_schedules
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
@@ -1,175 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
module PandaPal
|
4
|
-
PandaPal::Organization
|
5
|
-
RSpec.describe OrganizationConcerns::SettingsValidation, type: :model do
|
6
|
-
let!(:org) { create :panda_pal_organization }
|
7
|
-
|
8
|
-
def set_test_settings_structure
|
9
|
-
PandaPal.lti_options = {
|
10
|
-
title: 'Test App',
|
11
|
-
settings_structure: structure,
|
12
|
-
}
|
13
|
-
end
|
14
|
-
|
15
|
-
RSpec.shared_examples "shared stuff" do
|
16
|
-
it 'does not perform any validations if settings is not defined' do
|
17
|
-
PandaPal.lti_options = {}
|
18
|
-
expect(org.valid?).to be_truthy
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'does not perform any validations if options is not defined' do
|
22
|
-
PandaPal.lti_options = nil
|
23
|
-
expect(org.valid?).to be_truthy
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'does perform validations if settings_structure is defined' do
|
27
|
-
set_test_settings_structure
|
28
|
-
org.valid?
|
29
|
-
expect(org.valid?).to be_falsey
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'will fail if a required setting is not present' do
|
33
|
-
set_test_settings_structure
|
34
|
-
expect(org.valid?).to be_falsey
|
35
|
-
errors = org.errors.messages[:settings]
|
36
|
-
expect(errors[0]).to eq("Entry [:canvas] is required")
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'will fail if a setting is supplied but data_type is wrong' do
|
40
|
-
set_test_settings_structure
|
41
|
-
org.settings = {canvas: "Dog", reports: {}}
|
42
|
-
expect(org.valid?).to be_falsey
|
43
|
-
errors = org.errors.messages[:settings]
|
44
|
-
expect(errors[0]).to eq("Expected [:canvas] to be a Hash. Was a String")
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'will fail if a required subsetting is missing' do
|
48
|
-
set_test_settings_structure
|
49
|
-
org.settings = {canvas: {base_url: 'http://'}, reports: {}}
|
50
|
-
expect(org.valid?).to be_falsey
|
51
|
-
errors = org.errors.messages[:settings]
|
52
|
-
expect(errors[0]).to eq("Entry [:canvas][:api_token] is required")
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'will fail if extra options are specified' do
|
56
|
-
set_test_settings_structure
|
57
|
-
org.settings = {canvas: {base_url: 'http://', api_token: 'TEST'}, reports: {}, unknown_option: "WHAT IS THIS?"}
|
58
|
-
expect(org.valid?).to be_falsey
|
59
|
-
errors = org.errors.messages[:settings]
|
60
|
-
expect(errors[0]).to eq("Did not expect [:] to contain [unknown_option]")
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'will pass if all structure is maintained' do
|
64
|
-
set_test_settings_structure
|
65
|
-
org.settings = {canvas: {base_url: 'http://', api_token: 'TEST'}, reports: {submissions_report_time_length: 30.minutes, max_recheck_time: 10.hours}}
|
66
|
-
expect(org.valid?).to be_truthy
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'new settings_structure' do
|
71
|
-
let!(:properties) do
|
72
|
-
{
|
73
|
-
canvas: {
|
74
|
-
required: true,
|
75
|
-
type: 'Hash',
|
76
|
-
properties: {
|
77
|
-
api_token: { type: 'String', required: true },
|
78
|
-
base_url: { type: 'String', required: true },
|
79
|
-
},
|
80
|
-
},
|
81
|
-
reports: {
|
82
|
-
required: true,
|
83
|
-
type: 'Hash',
|
84
|
-
allow_additional: true,
|
85
|
-
}
|
86
|
-
}
|
87
|
-
end
|
88
|
-
|
89
|
-
let!(:structure) do
|
90
|
-
{
|
91
|
-
properties: properties,
|
92
|
-
}
|
93
|
-
end
|
94
|
-
|
95
|
-
it_behaves_like("shared stuff")
|
96
|
-
|
97
|
-
describe ':allow_additional' do
|
98
|
-
it 'passes extra properties if allow_additional is a matching Hash' do
|
99
|
-
structure[:allow_additional] = { type: 'String' }
|
100
|
-
set_test_settings_structure
|
101
|
-
org.settings = {canvas: {base_url: 'http://', api_token: 'TEST'}, reports: {}, unknown_option: "WHAT IS THIS?"}
|
102
|
-
expect(org).to be_valid
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'fails extra properties if allow_additional is an unmatching Hash' do
|
106
|
-
structure[:allow_additional] = { type: 'Hash' }
|
107
|
-
set_test_settings_structure
|
108
|
-
org.settings = {canvas: {base_url: 'http://', api_token: 'TEST'}, reports: {}, unknown_option: "WHAT IS THIS?"}
|
109
|
-
expect(org).not_to be_valid
|
110
|
-
end
|
111
|
-
|
112
|
-
it 'passes extra properties if allow_additional is a blank Hash' do
|
113
|
-
structure[:allow_additional] = { }
|
114
|
-
set_test_settings_structure
|
115
|
-
org.settings = {canvas: {base_url: 'http://', api_token: 'TEST'}, reports: {}, unknown_option: "WHAT IS THIS?"}
|
116
|
-
expect(org).to be_valid
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe ':validate' do
|
121
|
-
let!(:properties) do
|
122
|
-
{
|
123
|
-
blah: {
|
124
|
-
type: 'String',
|
125
|
-
validate: -> (v, *args) { v == 'blah' ? nil : '<path> failed validation' },
|
126
|
-
}
|
127
|
-
}
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'supports a custom validation proc' do
|
131
|
-
set_test_settings_structure
|
132
|
-
org.settings = { blah: 'blah' }
|
133
|
-
expect(org).to be_valid
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'replaces <path> with the human readable path' do
|
137
|
-
set_test_settings_structure
|
138
|
-
org.settings = { blah: '' }
|
139
|
-
expect(org.valid?).to be_falsey
|
140
|
-
errors = org.errors.messages[:settings]
|
141
|
-
expect(errors[0]).to eq("[:blah] failed validation")
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
context 'old settings_structure' do
|
147
|
-
let!(:structure) do
|
148
|
-
YAML.load("
|
149
|
-
canvas:
|
150
|
-
is_required: true
|
151
|
-
data_type: Hash
|
152
|
-
api_token:
|
153
|
-
is_required: true
|
154
|
-
data_type: String
|
155
|
-
base_url:
|
156
|
-
is_required: true
|
157
|
-
data_type: String
|
158
|
-
reports:
|
159
|
-
is_required: true
|
160
|
-
data_type: Hash
|
161
|
-
active_term_allowance:
|
162
|
-
submissions_report_time_length:
|
163
|
-
is_required: false
|
164
|
-
data_type: ActiveSupport::Duration
|
165
|
-
recheck_wait:
|
166
|
-
data_type: ActiveSupport::Duration
|
167
|
-
max_recheck_time:
|
168
|
-
is_required: false
|
169
|
-
").deep_symbolize_keys
|
170
|
-
end
|
171
|
-
|
172
|
-
it_behaves_like("shared stuff")
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|