panda_pal 5.6.12 → 5.7.0.beta1
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 +4 -4
- data/README.md +1 -1
- data/app/models/panda_pal/api_call.rb +1 -1
- data/app/models/panda_pal/organization.rb +73 -20
- data/app/models/panda_pal/organization_concerns/settings_validation.rb +6 -1
- data/app/models/panda_pal/organization_concerns/task_scheduling.rb +1 -1
- data/app/models/panda_pal/panda_pal_record.rb +5 -0
- data/app/models/panda_pal/platform/canvas.rb +2 -6
- data/app/models/panda_pal/platform.rb +5 -3
- data/app/models/panda_pal/session.rb +1 -1
- data/lib/panda_pal/engine.rb +83 -19
- data/lib/panda_pal/version.rb +1 -1
- data/spec/models/panda_pal/organization_spec.rb +16 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c17abc690e56e5bedffa292170eee88041e9d3214200a9bcf75571fc18b10a69
|
4
|
+
data.tar.gz: 40c41edfe6b016d6f9c8db8316fa44f1d7c9e7cff03ab9eb88fda74e6d695712
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48ea51186371b9941434937ed23cfcf8b44f0726fd8d89cf280e7e65edc132702ccaec6e98917194c0c29d320f77f1218287906d9a3489b4efcf8fc8546d0332
|
7
|
+
data.tar.gz: 2005bc3182ffdf509da90ae5e29363a6bc79964e71a558386fb8c9ab0de8bf16a33f071ea0e40d7ab44bf5c9eb1f74549491b37eb12010bf6d0820b1acffe4eb
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ org.install_lti(
|
|
29
29
|
context: "account/self", # (Optional) Or "account/3", "course/1", etc
|
30
30
|
exists: :error, # (Optional) Action to take if an LTI with the same Key already exists. Options are :error, :replace, :duplicate
|
31
31
|
version: "v1p3", # (Optional, default `v1p3`) LTI Version. Accepts `v1p0` or `v1p3`.
|
32
|
-
dedicated_deployment: false, # (Optional) If true, the Organization will be updated to link to a single deployment rather then to the general LTI Key. (experimental)
|
32
|
+
dedicated_deployment: false, # (Optional) If true, the Organization will be updated to link to a single deployment rather then to the general LTI Key. (experimental, LTI 1.3 only)
|
33
33
|
)
|
34
34
|
```
|
35
35
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module PandaPal
|
2
2
|
module OrganizationConcerns; end
|
3
3
|
|
4
|
-
#
|
4
|
+
# Newer versions of SymmetricEncryption introduced it's own version of attr_encrypted
|
5
5
|
# that is completely incompatible with the attr_encrypted Gem that PandaPal uses.
|
6
6
|
if defined?(::SymmetricEncryption::ActiveRecord::AttrEncrypted)
|
7
7
|
module SkipSymmetricEncAttrEncrypted
|
@@ -21,16 +21,34 @@ module PandaPal
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
class
|
24
|
+
class SettingsMarshaler
|
25
|
+
def self.load(data)
|
26
|
+
return nil unless data.present?
|
27
|
+
loaded = Marshal.load(data)
|
28
|
+
loaded = loaded.with_indifferent_access if loaded.is_a?(Hash) && !loaded.is_a?(HashWithIndifferentAccess)
|
29
|
+
loaded
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.dump(obj)
|
33
|
+
Marshal.dump(obj)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Organization < PandaPalRecord
|
25
38
|
include SkipSymmetricEncAttrEncrypted if defined?(SkipSymmetricEncAttrEncrypted)
|
26
39
|
|
27
40
|
include OrganizationConcerns::SettingsValidation
|
28
41
|
include OrganizationConcerns::TaskScheduling if defined?(Sidekiq.schedule)
|
29
42
|
|
30
|
-
|
31
|
-
attr_encrypted :settings, marshal: true, key: :encryption_key
|
43
|
+
attr_encrypted :settings, marshal: true, key: :encryption_key, marshaler: SettingsMarshaler
|
32
44
|
before_save {|a| a.settings = a.settings} # this is a hacky work-around to a bug where attr_encrypted is not saving settings in place
|
33
45
|
|
46
|
+
alias_method "settings_panda_pal_super=", "settings="
|
47
|
+
def settings=(settings)
|
48
|
+
settings = settings.with_indifferent_access if settings.is_a?(Hash) && !settings.is_a?(HashWithIndifferentAccess)
|
49
|
+
self.settings_panda_pal_super = settings
|
50
|
+
end
|
51
|
+
|
34
52
|
validates :key, uniqueness: { case_sensitive: false }, presence: true
|
35
53
|
validates :secret, presence: true
|
36
54
|
validates :name, uniqueness: { case_sensitive: false }, presence: true, format: { with: /\A[a-z0-9_]+\z/i }
|
@@ -84,10 +102,6 @@ module PandaPal
|
|
84
102
|
end
|
85
103
|
end
|
86
104
|
|
87
|
-
def self.resolve_platform(hint)
|
88
|
-
iss = hint["iss"]
|
89
|
-
end
|
90
|
-
|
91
105
|
def rename!(new_name)
|
92
106
|
do_switch = Apartment::Tenant.current == name
|
93
107
|
ActiveRecord::Base.connection.execute(
|
@@ -98,23 +112,56 @@ module PandaPal
|
|
98
112
|
switch_tenant if do_switch
|
99
113
|
end
|
100
114
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
scl = platform_type.org_subclass
|
106
|
-
scl ? becomes(scl) : self
|
115
|
+
if !PandaPal.lti_options[:platform].present? || PandaPal.lti_options[:platform].is_a?(String)
|
116
|
+
CONST_PLATFORM_TYPE = Platform.resolve_platform_class(nil) rescue nil
|
117
|
+
else
|
118
|
+
CONST_PLATFORM_TYPE = nil
|
107
119
|
end
|
108
120
|
|
109
|
-
|
110
|
-
|
111
|
-
if
|
112
|
-
|
121
|
+
# Include the Platform API...
|
122
|
+
if CONST_PLATFORM_TYPE
|
123
|
+
# ... directly if this is a single-platform tool
|
124
|
+
include CONST_PLATFORM_TYPE::OrgExtension if defined?(CONST_PLATFORM_TYPE::OrgExtension)
|
125
|
+
else
|
126
|
+
# ... via method_missing/delegation if this is a multi-platform tool
|
127
|
+
def respond_to_missing?(name, include_private = false)
|
128
|
+
if (self.class == PandaPal::Organization) && (plat_api = platform_api).present?
|
129
|
+
plat_api.respond_to?(name, include_private)
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def method_missing(method, *args, **kwargs, &block)
|
136
|
+
if (self.class == PandaPal::Organization) && (plat_api = platform_api).present?
|
137
|
+
plat_api.send(method, *args, **kwargs, &block)
|
138
|
+
else
|
139
|
+
super
|
140
|
+
end
|
113
141
|
end
|
114
142
|
end
|
115
143
|
|
116
|
-
|
117
|
-
|
144
|
+
# Extend a particular type of Platform API.
|
145
|
+
def self.extend_platform_api(platform_type = CONST_PLATFORM_TYPE, &blk)
|
146
|
+
scl = platform_type.organization_api
|
147
|
+
scl.class_eval(&blk)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Retrieve the specified Platform API for this Organization.
|
151
|
+
# If only a single Platform is used (when lti_options[:platform] is a constant string or nil), passing the platform_type is unnecessary
|
152
|
+
#
|
153
|
+
# The API is currently an Organization subclass (using `becomes()`), but such may change.
|
154
|
+
def platform_api(platform_type = primary_platform)
|
155
|
+
scl = platform_type.organization_api
|
156
|
+
return self if scl == self.class
|
157
|
+
becomes(scl)
|
158
|
+
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
|
162
|
+
# OrgExtension-Overridable method to allow multi-platform tool Orgs to implicitly include a Platform API
|
163
|
+
def primary_platform
|
164
|
+
CONST_PLATFORM_TYPE
|
118
165
|
end
|
119
166
|
|
120
167
|
private
|
@@ -126,5 +173,11 @@ module PandaPal
|
|
126
173
|
def destroy_schema
|
127
174
|
Apartment::Tenant.drop name
|
128
175
|
end
|
176
|
+
|
177
|
+
public
|
178
|
+
|
179
|
+
PandaPal.resolved_extensions_for(self).each do |ext|
|
180
|
+
include ext
|
181
|
+
end
|
129
182
|
end
|
130
183
|
end
|
@@ -155,7 +155,12 @@ module PandaPal
|
|
155
155
|
end
|
156
156
|
|
157
157
|
if spec[:properties] != nil || spec[:allow_additional] != nil
|
158
|
-
|
158
|
+
set_keys = settings.keys
|
159
|
+
expected_keys = spec[:properties]&.keys || []
|
160
|
+
expected_keys = expected_keys.map(&:to_s) if settings.is_a?(HashWithIndifferentAccess)
|
161
|
+
|
162
|
+
extra_keys = set_keys - expected_keys
|
163
|
+
|
159
164
|
if extra_keys.present?
|
160
165
|
if spec[:allow_additional].is_a?(Hash)
|
161
166
|
extra_keys.each do |key|
|
@@ -42,7 +42,7 @@ module PandaPal
|
|
42
42
|
|
43
43
|
hash.tap do |hash|
|
44
44
|
kl = ' ' * (k.to_s.length - 4)
|
45
|
-
hash[k.to_sym] =
|
45
|
+
hash[k.to_sym] = PandaPal::OrganizationConcerns::TaskScheduling.build_settings_entry(desc)
|
46
46
|
end
|
47
47
|
end,
|
48
48
|
}
|
@@ -44,13 +44,11 @@ module PandaPal
|
|
44
44
|
def install_lti(host: nil, context: :root_account, version: 'v1p3', exists: :error, dedicated_deployment: false)
|
45
45
|
raise "Automatically installing this LTI requires Bearcat." unless defined?(Bearcat)
|
46
46
|
|
47
|
-
version = version.to_s
|
48
|
-
|
49
47
|
run_callbacks :lti_install do
|
50
48
|
ctype, cid = _parse_lti_context(context)
|
51
49
|
|
52
50
|
if version == 'v1p0'
|
53
|
-
existing_installs = _find_existing_installs(
|
51
|
+
existing_installs = _find_existing_installs(exists: exists) do |lti|
|
54
52
|
lti[:consumer_key] == self.key
|
55
53
|
end
|
56
54
|
|
@@ -112,9 +110,6 @@ module PandaPal
|
|
112
110
|
end
|
113
111
|
|
114
112
|
ekey = existing_keys[0]
|
115
|
-
# if ekey && exists == :error
|
116
|
-
# raise "Tool with key #{self.key} already installed"
|
117
|
-
# end
|
118
113
|
|
119
114
|
lti_json_url = PandaPal::LaunchUrlHelpers.resolve_route(:v1p3_config_url, host: host)
|
120
115
|
lti_json = JSON.parse(HTTParty.get(lti_json_url, format: :plain).body)
|
@@ -268,6 +263,7 @@ module PandaPal
|
|
268
263
|
|
269
264
|
if defined?(Bearcat)
|
270
265
|
def bearcat_client
|
266
|
+
# Less than ideal, but `canvas_sync_client` has been the long-adopted tradition so we check for it so that we can continue to drop-in new versions of PandaPal
|
271
267
|
return canvas_sync_client if defined?(canvas_sync_client)
|
272
268
|
|
273
269
|
Bearcat::Client.new(
|
@@ -72,11 +72,13 @@ module PandaPal
|
|
72
72
|
raise "Unknown platform '#{platform}'"
|
73
73
|
end
|
74
74
|
|
75
|
-
def self.
|
76
|
-
return
|
75
|
+
def self.organization_api
|
76
|
+
return PandaPal::Organization unless defined?(self::OrgExtension)
|
77
|
+
return PandaPal::Organization if PandaPal::Organization < self::OrgExtension
|
78
|
+
|
77
79
|
oext = self::OrgExtension
|
78
80
|
|
79
|
-
@
|
81
|
+
@organization_api ||= self::OrganizationApi ||= Class.new(PandaPal::Organization) do
|
80
82
|
include oext
|
81
83
|
end
|
82
84
|
end
|
data/lib/panda_pal/engine.rb
CHANGED
@@ -69,41 +69,105 @@ module PandaPal
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
initializer "panda_pal.
|
72
|
+
initializer "panda_pal.prompts" do |app|
|
73
73
|
class ::Object
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
74
|
+
def _panda_pal_console_app_name
|
75
|
+
app_class = Rails.application.class
|
76
|
+
app_name = app_class.respond_to?(:parent) ? app_class.parent : app_class.module_parent
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if defined? IRB
|
81
|
+
module PandaPalIrbTimePrompt
|
82
|
+
def prompt(prompt, ltype, indent, line_no)
|
83
|
+
formatted = super(prompt, ltype, indent, line_no)
|
84
|
+
app_bit = Pry::Helpers::Text.cyan("#{_panda_pal_console_app_name}-#{Apartment::Tenant.current}")
|
85
|
+
"[#{app_bit}] #{formatted}"
|
80
86
|
end
|
87
|
+
end
|
81
88
|
|
82
|
-
|
83
|
-
|
84
|
-
|
89
|
+
module ::IRB
|
90
|
+
class Irb
|
91
|
+
prepend PandaPalIrbTimePrompt
|
85
92
|
end
|
86
|
-
rescue => err
|
87
|
-
puts "PadaPal: Error occurred auto-switching tenant: #{err}"
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
91
|
-
|
92
|
-
|
93
|
-
|
96
|
+
if defined?(Pry)
|
97
|
+
default_prompt = Pry::Prompt[:default]
|
98
|
+
env = Pry::Helpers::Text.red(Rails.env.upcase)
|
99
|
+
|
100
|
+
app_name = _panda_pal_console_app_name
|
101
|
+
|
102
|
+
Pry.config.prompt = Pry::Prompt.new(
|
103
|
+
'custom',
|
104
|
+
'my custom prompt',
|
105
|
+
[
|
106
|
+
->(*args) {
|
107
|
+
app_bit = Pry::Helpers::Text.cyan("#{app_name}-#{Apartment::Tenant.current}")
|
108
|
+
"#{app_bit}#{default_prompt.wait_proc.call(*args)}"
|
109
|
+
},
|
110
|
+
->(*args) {
|
111
|
+
app_bit = Pry::Helpers::Text.cyan("#{app_name}-#{Apartment::Tenant.current}")
|
112
|
+
"#{app_bit}#{default_prompt.incomplete_proc.call(*args)}"
|
113
|
+
},
|
114
|
+
],
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
if defined? Rails::Console
|
119
|
+
module PandaPal::ConsoleExtPrefix
|
120
|
+
extend ActiveSupport::Concern
|
121
|
+
|
122
|
+
def start(*args)
|
123
|
+
print "\033];#{_panda_pal_console_app_name} Rails Console\007"
|
124
|
+
super
|
125
|
+
end
|
94
126
|
end
|
127
|
+
|
128
|
+
Rails::Console.prepend(PandaPal::ConsoleExtPrefix)
|
95
129
|
end
|
130
|
+
end
|
96
131
|
|
97
|
-
|
98
|
-
|
99
|
-
|
132
|
+
initializer "panda_pal.autoswitch" do |app|
|
133
|
+
if defined? Rails::Console
|
134
|
+
module PandaPal::ConsoleExtAutoSwitch
|
135
|
+
extend ActiveSupport::Concern
|
136
|
+
|
137
|
+
def start(*args)
|
138
|
+
begin
|
139
|
+
org = nil
|
140
|
+
if Rails.env.development?
|
141
|
+
org = PandaPal::Organization.find_by(name: 'local') || PandaPal::Organization.first
|
142
|
+
elsif PandaPal::Organization.count == 1
|
143
|
+
org = PandaPal::Organization.first
|
144
|
+
end
|
145
|
+
|
146
|
+
if org
|
147
|
+
org.switch_tenant
|
148
|
+
puts "PandaPal: Auto Switched to tenant '#{org.name}'"
|
149
|
+
end
|
150
|
+
rescue => err
|
151
|
+
puts "PandaPal: Error occurred auto-switching tenant: #{err}"
|
152
|
+
end
|
153
|
+
|
154
|
+
super
|
155
|
+
end
|
100
156
|
end
|
157
|
+
|
158
|
+
Rails::Console.prepend(PandaPal::ConsoleExtAutoSwitch)
|
101
159
|
end
|
102
160
|
end
|
103
161
|
|
104
162
|
initializer "panda_pal.serialze_symbols" do |app|
|
105
163
|
app.config.active_record.yaml_column_permitted_classes ||= []
|
106
|
-
app.config.active_record.yaml_column_permitted_classes |= [
|
164
|
+
app.config.active_record.yaml_column_permitted_classes |= [
|
165
|
+
Symbol,
|
166
|
+
ActiveSupport::Duration,
|
167
|
+
ActiveSupport::TimeWithZone,
|
168
|
+
ActiveSupport::TimeZone,
|
169
|
+
Time,
|
170
|
+
]
|
107
171
|
rescue
|
108
172
|
end
|
109
173
|
|
data/lib/panda_pal/version.rb
CHANGED
@@ -30,5 +30,21 @@ module PandaPal
|
|
30
30
|
org2 = build :panda_pal_organization, salesforce_id: 'salesforce'
|
31
31
|
expect(org2.valid?).to be_falsey
|
32
32
|
end
|
33
|
+
|
34
|
+
it 'stores setting as an indifferent Hash' do
|
35
|
+
org = create :panda_pal_organization
|
36
|
+
org.update!(settings: { a: 1 })
|
37
|
+
|
38
|
+
expect(Organization.last.settings).to be_a (ActiveSupport::HashWithIndifferentAccess)
|
39
|
+
|
40
|
+
org.settings = {}
|
41
|
+
expect(org.settings).to be_a (ActiveSupport::HashWithIndifferentAccess)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'stores settings' do
|
45
|
+
org = create :panda_pal_organization
|
46
|
+
org.update!(settings: { a: 1 })
|
47
|
+
expect(Organization.last.settings[:a]).to eql(1)
|
48
|
+
end
|
33
49
|
end
|
34
50
|
end
|
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: 5.
|
4
|
+
version: 5.7.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Instructure ProServe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -313,6 +313,7 @@ files:
|
|
313
313
|
- app/models/panda_pal/organization.rb
|
314
314
|
- app/models/panda_pal/organization_concerns/settings_validation.rb
|
315
315
|
- app/models/panda_pal/organization_concerns/task_scheduling.rb
|
316
|
+
- app/models/panda_pal/panda_pal_record.rb
|
316
317
|
- app/models/panda_pal/platform.rb
|
317
318
|
- app/models/panda_pal/platform/canvas.rb
|
318
319
|
- app/models/panda_pal/session.rb
|
@@ -403,9 +404,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
403
404
|
version: '0'
|
404
405
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
405
406
|
requirements:
|
406
|
-
- - "
|
407
|
+
- - ">"
|
407
408
|
- !ruby/object:Gem::Version
|
408
|
-
version:
|
409
|
+
version: 1.3.1
|
409
410
|
requirements: []
|
410
411
|
rubygems_version: 3.1.6
|
411
412
|
signing_key:
|