panda_pal 5.6.11 → 5.7.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c795ed3f22dd6907af02906b115d21f5a3e0b178278ac94a6a54fd3e41a838a
4
- data.tar.gz: d9e8d0152d0c58cf8473519cd5f92b8e5946c235f72c0f2448a9c22c646c9af1
3
+ metadata.gz: de960e9dd44bfec67832afe2e1cde116ea8a9e608f9658dea7cced1d45b1d934
4
+ data.tar.gz: 8795f3fed5583c49d3ff729ef18e1e4b4ab21214b4d6319fe13a28e90073e00f
5
5
  SHA512:
6
- metadata.gz: 06ac4df41b5b2cc268e06dff1a98322445474947c30b88da4770ec532ea834c62a106368e5ea77c64289bd7b6af7b0a837e1de1d6c71786f45f30ba226face9f
7
- data.tar.gz: def73050fcecfacb79c4956188b933f81abfd5cd6791c05143df984473b3cde9e149eccc0f0b36729085d58b781160d2a5eddd5bda8b8ef384d4c5b66476fcdd
6
+ metadata.gz: 65b30bd9f2bfca02fcf92b451f85fd98e701a5251e0952d4468b4b84178b2a7149705fd7dd20783f6d89c8f1b02e4630ced8d00a0ceb22928927e19aa405c72e
7
+ data.tar.gz: 320abed36d66d03e24c7c1cb38f545d094779559107f10fa084d464989ebb473a07b497b75eeb9d84c3febc16bfadd88f0002ca298ab67456e4c657261fdc8d6
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
  require 'jwt'
2
2
 
3
3
  module PandaPal
4
- class ApiCall < ActiveRecord::Base
4
+ class ApiCall < PandaPalRecord
5
5
  # TODO Add Rate Limiting?
6
6
 
7
7
  def self.decode_jwt(jwt)
@@ -1,7 +1,7 @@
1
1
  module PandaPal
2
2
  module OrganizationConcerns; end
3
3
 
4
- # Nwer versions of SymmetricEncryption introduced it's own version of attr_encrypted
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 Organization < ActiveRecord::Base
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
- serialize :settings, Hash
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
- def platform_extensions(platform_type)
102
- return self unless defined?(platform_type::OrgExtension)
103
- return self if self.class < platform_type::OrgExtension
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
- if !PandaPal.lti_options[:platform].present? || PandaPal.lti_options[:platform].is_a?(String)
110
- platform_cls = Platform.resolve_platform_class(nil) rescue nil
111
- if platform_cls && defined?(platform_cls::OrgExtension)
112
- include platform_cls::OrgExtension
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
- PandaPal.resolved_extensions_for(self).each do |ext|
117
- include ext
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
- extra_keys = settings.keys - (spec[:properties]&.keys || [])
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] = hash[k.to_s] = PandaPal::OrganizationConcerns::TaskScheduling.build_settings_entry(desc)
45
+ hash[k.to_sym] = PandaPal::OrganizationConcerns::TaskScheduling.build_settings_entry(desc)
46
46
  end
47
47
  end,
48
48
  }
@@ -0,0 +1,5 @@
1
+ module PandaPal
2
+ class PandaPalRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -44,11 +44,13 @@ 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
+
47
49
  run_callbacks :lti_install do
48
50
  ctype, cid = _parse_lti_context(context)
49
51
 
50
52
  if version == 'v1p0'
51
- existing_installs = _find_existing_installs(exists: exists) do |lti|
53
+ existing_installs = _find_existing_installs(context, exists: exists) do |lti|
52
54
  lti[:consumer_key] == self.key
53
55
  end
54
56
 
@@ -110,9 +112,6 @@ module PandaPal
110
112
  end
111
113
 
112
114
  ekey = existing_keys[0]
113
- # if ekey && exists == :error
114
- # raise "Tool with key #{self.key} already installed"
115
- # end
116
115
 
117
116
  lti_json_url = PandaPal::LaunchUrlHelpers.resolve_route(:v1p3_config_url, host: host)
118
117
  lti_json = JSON.parse(HTTParty.get(lti_json_url, format: :plain).body)
@@ -266,6 +265,7 @@ module PandaPal
266
265
 
267
266
  if defined?(Bearcat)
268
267
  def bearcat_client
268
+ # 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
269
269
  return canvas_sync_client if defined?(canvas_sync_client)
270
270
 
271
271
  Bearcat::Client.new(
@@ -72,11 +72,13 @@ module PandaPal
72
72
  raise "Unknown platform '#{platform}'"
73
73
  end
74
74
 
75
- def self.org_subclass
76
- return nil unless defined?(self::OrgExtension)
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
- @org_subclass ||= Class.new(PandaPal::Organization) do
81
+ @organization_api ||= self::OrganizationApi ||= Class.new(PandaPal::Organization) do
80
82
  include oext
81
83
  end
82
84
  end
@@ -1,5 +1,5 @@
1
1
  module PandaPal
2
- class Session < ActiveRecord::Base
2
+ class Session < PandaPalRecord
3
3
  belongs_to :panda_pal_organization, class_name: 'PandaPal::Organization', optional: true
4
4
 
5
5
  after_initialize do
@@ -69,41 +69,105 @@ module PandaPal
69
69
  end
70
70
  end
71
71
 
72
- initializer "panda_pal.autoswitch" do |app|
72
+ initializer "panda_pal.prompts" do |app|
73
73
  class ::Object
74
- def _panda_pal_console_autoswitch_tenant
75
- org = nil
76
- if Rails.env.development?
77
- org = PandaPal::Organization.find_by(name: 'local') || PandaPal::Organization.first
78
- elsif PandaPal::Organization.count == 1
79
- org = PandaPal::Organization.first
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
- if org
83
- org.switch_tenant
84
- puts "PandaPal: Auto Switched to tenant '#{org.name}'"
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
- module Rails::ConsoleMethods
92
- def self.included(base)
93
- _panda_pal_console_autoswitch_tenant
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
- if defined?(Pry)
98
- Pry.hooks.add_hook(:before_session, "switch PandaPal tenant") do |output, binding, pry|
99
- _panda_pal_console_autoswitch_tenant
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 |= [Symbol, ActiveSupport::Duration]
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
 
@@ -1,3 +1,3 @@
1
1
  module PandaPal
2
- VERSION = "5.6.11"
2
+ VERSION = "5.7.0.beta2"
3
3
  end
@@ -0,0 +1,162 @@
1
+  (3.9ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
2
+  (0.8ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 [["key", "environment"]]
3
+  (0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
4
+  (0.2ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 [["key", "environment"]]
5
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
6
+  (0.2ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 [["key", "environment"]]
7
+  (1.4ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
8
+  (29.2ms) DROP DATABASE IF EXISTS "panda_pal_development"
9
+  (28.0ms) DROP DATABASE IF EXISTS "panda_pal_test"
10
+  (124.7ms) CREATE DATABASE "panda_pal_development" ENCODING = 'utf8'
11
+  (84.3ms) CREATE DATABASE "panda_pal_test" ENCODING = 'utf8'
12
+ SQL (0.2ms) CREATE EXTENSION IF NOT EXISTS "plpgsql"
13
+  (0.2ms) DROP TABLE IF EXISTS "panda_pal_organizations" CASCADE
14
+  (15.0ms) CREATE TABLE "panda_pal_organizations" ("id" bigserial primary key, "name" character varying, "key" character varying, "secret" character varying, "canvas_account_id" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, "salesforce_id" character varying, "encrypted_settings" text, "encrypted_settings_iv" character varying)
15
+  (5.0ms) CREATE UNIQUE INDEX "index_panda_pal_organizations_on_key" ON "panda_pal_organizations" ("key")
16
+  (5.0ms) CREATE UNIQUE INDEX "index_panda_pal_organizations_on_name" ON "panda_pal_organizations" ("name")
17
+  (1.0ms) DROP TABLE IF EXISTS "panda_pal_sessions" CASCADE
18
+  (9.0ms) CREATE TABLE "panda_pal_sessions" ("id" bigserial primary key, "session_key" character varying, "data" text, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, "panda_pal_organization_id" integer)
19
+  (4.8ms) CREATE INDEX "index_panda_pal_sessions_on_panda_pal_organization_id" ON "panda_pal_sessions" ("panda_pal_organization_id")
20
+  (4.2ms) CREATE UNIQUE INDEX "index_panda_pal_sessions_on_session_key" ON "panda_pal_sessions" ("session_key")
21
+  (8.6ms) CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY)
22
+  (0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
23
+  (1.2ms) INSERT INTO "schema_migrations" (version) VALUES (20171205194657)
24
+  (9.8ms) CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
25
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
26
+  (0.1ms) BEGIN
27
+ ActiveRecord::InternalMetadata Create (0.3ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key" [["key", "environment"], ["value", "development"], ["created_at", "2022-07-21 15:53:16.068888"], ["updated_at", "2022-07-21 15:53:16.068888"]]
28
+  (1.1ms) COMMIT
29
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
30
+  (0.2ms) BEGIN
31
+  (0.2ms) COMMIT
32
+ SQL (0.3ms) CREATE EXTENSION IF NOT EXISTS "plpgsql"
33
+  (0.4ms) DROP TABLE IF EXISTS "panda_pal_organizations" CASCADE
34
+  (11.1ms) CREATE TABLE "panda_pal_organizations" ("id" bigserial primary key, "name" character varying, "key" character varying, "secret" character varying, "canvas_account_id" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, "salesforce_id" character varying, "encrypted_settings" text, "encrypted_settings_iv" character varying)
35
+  (4.6ms) CREATE UNIQUE INDEX "index_panda_pal_organizations_on_key" ON "panda_pal_organizations" ("key")
36
+  (5.1ms) CREATE UNIQUE INDEX "index_panda_pal_organizations_on_name" ON "panda_pal_organizations" ("name")
37
+  (0.2ms) DROP TABLE IF EXISTS "panda_pal_sessions" CASCADE
38
+  (9.6ms) CREATE TABLE "panda_pal_sessions" ("id" bigserial primary key, "session_key" character varying, "data" text, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, "panda_pal_organization_id" integer)
39
+  (5.0ms) CREATE INDEX "index_panda_pal_sessions_on_panda_pal_organization_id" ON "panda_pal_sessions" ("panda_pal_organization_id")
40
+  (5.0ms) CREATE UNIQUE INDEX "index_panda_pal_sessions_on_session_key" ON "panda_pal_sessions" ("session_key")
41
+  (8.9ms) CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY)
42
+  (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
43
+  (1.4ms) INSERT INTO "schema_migrations" (version) VALUES (20171205194657)
44
+  (8.8ms) CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
45
+ ActiveRecord::InternalMetadata Load (0.3ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
46
+  (0.1ms) BEGIN
47
+ ActiveRecord::InternalMetadata Create (0.5ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key" [["key", "environment"], ["value", "development"], ["created_at", "2022-07-21 15:53:16.159537"], ["updated_at", "2022-07-21 15:53:16.159537"]]
48
+  (1.0ms) COMMIT
49
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
50
+  (0.1ms) BEGIN
51
+ ActiveRecord::InternalMetadata Update (0.2ms) UPDATE "ar_internal_metadata" SET "value" = $1, "updated_at" = $2 WHERE "ar_internal_metadata"."key" = $3 [["value", "test"], ["updated_at", "2022-07-21 15:53:16.162899"], ["key", "environment"]]
52
+  (1.1ms) COMMIT
53
+  (0.1ms) SELECT pg_try_advisory_lock(7878782013693407355)
54
+  (0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
55
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
56
+  (0.2ms) BEGIN
57
+  (0.1ms) COMMIT
58
+  (0.2ms) SELECT pg_advisory_unlock(7878782013693407355)
59
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
60
+  (0.3ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
61
+  (0.2ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
62
+  (0.1ms) SELECT pg_try_advisory_lock(7878782013693407355)
63
+  (0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
64
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
65
+  (0.1ms) BEGIN
66
+  (0.1ms) COMMIT
67
+  (0.2ms) SELECT pg_advisory_unlock(7878782013693407355)
68
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
69
+  (0.3ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
70
+  (0.2ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
71
+  (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
72
+  (0.3ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 [["key", "environment"]]
73
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
74
+  (0.2ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 [["key", "environment"]]
75
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
76
+  (0.1ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 [["key", "environment"]]
77
+  (0.3ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
78
+  (70.1ms) DROP DATABASE IF EXISTS "panda_pal_development"
79
+  (34.0ms) DROP DATABASE IF EXISTS "panda_pal_test"
80
+  (76.8ms) CREATE DATABASE "panda_pal_development" ENCODING = 'utf8'
81
+  (74.8ms) CREATE DATABASE "panda_pal_test" ENCODING = 'utf8'
82
+  (10.7ms) CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY)
83
+  (9.0ms) CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
84
+  (0.1ms) SELECT pg_try_advisory_lock(7878782013693407355)
85
+  (0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
86
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
87
+  (0.1ms) BEGIN
88
+ ActiveRecord::InternalMetadata Create (0.3ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key" [["key", "environment"], ["value", "development"], ["created_at", "2022-07-21 16:11:17.765993"], ["updated_at", "2022-07-21 16:11:17.765993"]]
89
+  (1.3ms) COMMIT
90
+  (0.4ms) SELECT pg_advisory_unlock(7878782013693407355)
91
+  (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
92
+  (0.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
93
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
94
+  (31.2ms) DROP DATABASE IF EXISTS "panda_pal_development"
95
+  (27.1ms) DROP DATABASE IF EXISTS "panda_pal_test"
96
+  (81.3ms) CREATE DATABASE "panda_pal_development" ENCODING = 'utf8'
97
+  (81.1ms) CREATE DATABASE "panda_pal_test" ENCODING = 'utf8'
98
+  (10.3ms) CREATE TABLE "schema_migrations" ("version" character varying NOT NULL PRIMARY KEY)
99
+  (8.7ms) CREATE TABLE "ar_internal_metadata" ("key" character varying NOT NULL PRIMARY KEY, "value" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
100
+  (0.1ms) SELECT pg_try_advisory_lock(7878782013693407355)
101
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
102
+ Migrating to CreatePandaPalOrganizations (20160412205931)
103
+  (0.0ms) BEGIN
104
+  (8.6ms) CREATE TABLE "panda_pal_organizations" ("id" serial NOT NULL PRIMARY KEY, "name" character varying, "key" character varying, "secret" character varying, "canvas_account_id" character varying, "settings" text, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
105
+  (3.8ms) CREATE UNIQUE INDEX "index_panda_pal_organizations_on_name" ON "panda_pal_organizations" ("name")
106
+  (3.4ms) CREATE UNIQUE INDEX "index_panda_pal_organizations_on_key" ON "panda_pal_organizations" ("key")
107
+ ActiveRecord::SchemaMigration Create (0.5ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20160412205931"]]
108
+  (1.3ms) COMMIT
109
+ Migrating to CreatePandaPalSessions (20160413135653)
110
+  (0.2ms) BEGIN
111
+  (7.4ms) CREATE TABLE "panda_pal_sessions" ("id" serial NOT NULL PRIMARY KEY, "session_key" character varying, "data" text, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
112
+  (3.7ms) CREATE UNIQUE INDEX "index_panda_pal_sessions_on_session_key" ON "panda_pal_sessions" ("session_key")
113
+ ActiveRecord::SchemaMigration Create (0.4ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20160413135653"]]
114
+  (1.2ms) COMMIT
115
+ Migrating to AddPandaPalOrganizationToSession (20160425130344)
116
+  (0.1ms) BEGIN
117
+  (0.2ms) ALTER TABLE "panda_pal_sessions" ADD "panda_pal_organization_id" integer
118
+  (3.5ms) CREATE INDEX "index_panda_pal_sessions_on_panda_pal_organization_id" ON "panda_pal_sessions" ("panda_pal_organization_id")
119
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20160425130344"]]
120
+  (1.3ms) COMMIT
121
+ Migrating to AddSalesforceIdToOrganizations (20170106165533)
122
+  (0.1ms) BEGIN
123
+  (0.2ms) ALTER TABLE "panda_pal_organizations" ADD "salesforce_id" character varying
124
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20170106165533"]]
125
+  (1.0ms) COMMIT
126
+ Migrating to EncryptOrganizationSettings (20171205183457)
127
+  (0.1ms) BEGIN
128
+  (0.2ms) SELECT * from schema_migrations where version = '30171205183457'
129
+  (0.2ms) ALTER TABLE "panda_pal_organizations" RENAME COLUMN "settings" TO "old_settings"
130
+  (0.2ms) ALTER TABLE "panda_pal_organizations" ADD "encrypted_settings" text
131
+  (0.2ms) ALTER TABLE "panda_pal_organizations" ADD "encrypted_settings_iv" character varying
132
+ ActiveRecord::SchemaMigration Create (0.3ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20171205183457"]]
133
+  (1.0ms) COMMIT
134
+ Migrating to RemoveOldOrganizationSettings (20171205194657)
135
+  (0.1ms) BEGIN
136
+  (0.2ms) SELECT * from schema_migrations where version = '30171205194657'
137
+ PandaPal::Organization Load (0.3ms) SELECT "public"."panda_pal_organizations".* FROM "public"."panda_pal_organizations" ORDER BY "public"."panda_pal_organizations"."id" ASC LIMIT $1 [["LIMIT", 1000]]
138
+  (0.5ms) ALTER TABLE "panda_pal_organizations" DROP COLUMN "old_settings"
139
+ ActiveRecord::SchemaMigration Create (0.3ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20171205194657"]]
140
+  (1.1ms) COMMIT
141
+ Migrating to CreatePandaPalApiCalls (20220721095653)
142
+  (0.2ms) BEGIN
143
+  (8.2ms) CREATE TABLE "panda_pal_api_calls" ("id" serial NOT NULL PRIMARY KEY, "logic" text, "expiration" character varying, "rate_limit" character varying, "uses_remaining" integer, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
144
+ ActiveRecord::SchemaMigration Create (0.2ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20220721095653"]]
145
+  (1.1ms) COMMIT
146
+ ActiveRecord::InternalMetadata Load (0.2ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
147
+  (0.1ms) BEGIN
148
+ ActiveRecord::InternalMetadata Create (0.3ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "key" [["key", "environment"], ["value", "development"], ["created_at", "2022-07-21 16:14:19.544810"], ["updated_at", "2022-07-21 16:14:19.544810"]]
149
+  (1.3ms) COMMIT
150
+  (0.3ms) SELECT pg_advisory_unlock(7878782013693407355)
151
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
152
+  (0.4ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
153
+  (0.2ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
154
+  (0.2ms) SELECT pg_try_advisory_lock(7878782013693407355)
155
+  (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
156
+ ActiveRecord::InternalMetadata Load (0.4ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
157
+  (0.1ms) BEGIN
158
+  (0.1ms) COMMIT
159
+  (0.2ms) SELECT pg_advisory_unlock(7878782013693407355)
160
+  (0.2ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
161
+  (0.3ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"
162
+  (0.2ms) SELECT "public"."panda_pal_organizations"."name" FROM "public"."panda_pal_organizations"