panda_pal 5.11.0 → 5.12.1

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: 75028dd065a6203af07a828ad23b78340b2b98c467ddc4def4b3935b6af31f15
4
- data.tar.gz: dda65af42c0372f49c029508f9701093b99b81ca76c3cefa5448a35ba776031a
3
+ metadata.gz: c7b257eddedb1431e4a8030cae79245ef3c0da635ac47cd7e3d1b7ac223d630c
4
+ data.tar.gz: cff44cd6c69723032155a0e82a952f1ceaec1bbcc991014ab5ea5fd75a3404db
5
5
  SHA512:
6
- metadata.gz: 911ca0334b047224f269121dd0a65e363c6239d763b73ef7ada18c439a66f29a11c51eae57ca1edfa8ffe18aa6b88bd7677aa41a5239c3517a2bc286668d30be
7
- data.tar.gz: 25e362c44747b95d1316bec3c50ed508d6bf125a4b62328d1bc0c1a5bc1a498013d67db896422ff2e526ce111a29338f02481972b0a2fbe28d743baa4015ac07
6
+ metadata.gz: 398fedeae33152e8691af37e3c32d3fc353e379839e42837779652e48d8ec2e22112eb6da0b0e24f942aaf84a727102e67d85ee573b12b5ccd42595675413a97
7
+ data.tar.gz: cf6e76fa5dc3aa9c338d8ba118c2f4107f945eebb4e5ebeaf059db4e2119abc69879ee38467452f0d6852855a72606e148c6ba0d05da4c60001cf3145376ba9f
data/README.md CHANGED
@@ -179,6 +179,18 @@ Rails.application.config.middleware.use 'Apartment::Elevators::Generic', lambda
179
179
  It is also possible to switch tenants by implementing `around_action :switch_tenant` in the controller.
180
180
  However, it should be noted that calling `switch_tenant` must be used in an `around_action` hook (or given a block), and is only intended to be used for LTI launches, and not subsequent requests.
181
181
 
182
+ ### Sharding
183
+ PandaPal 5.12 added support for multi-DB sharding. If you wish to use this, add a `shard` column to the `PandaPal::Organization` model. The implementation should be fairly transparent and should behave as normal PandaPal/Apartment - the only difference is that tenants can be created on different DB shards.
184
+
185
+ The list of available shards is computed from the Environment variables. On startup, the application looks for variables with the following formats:
186
+ - `SHARD_DB_XYZ_URL`
187
+ - `HEROKU_POSTGRESQL_XYZ_URL`
188
+ (where `XYZ` is the shard identifier)
189
+
190
+ When an `Organization` is created, it is assigned to a random shard (or if no shards were discovered, it is placed in the default database as normal). Alternatively, a specific shard can be specified: `PandaPal::Organization.new(shard: "shard_a")`. An `Organization` can be added to the primary shard with `shard: "default"`
191
+
192
+ The `PandaPal::Organization` record is still created in the `public` schema of the default database, as per usual.
193
+
182
194
  ### Rake tasks and jobs
183
195
  **Delayed Job Support has been removed. This allows each project to assess it's need to handle background jobs.**
184
196
 
@@ -41,6 +41,9 @@ module PandaPal
41
41
  include OrganizationConcerns::OrganizationBuilder if $stdin&.tty?
42
42
  include OrganizationConcerns::TaskScheduling if defined?(Sidekiq.schedule)
43
43
 
44
+ include OrganizationConcerns::TenantHandling
45
+ include OrganizationConcerns::MultiDatabaseSharding if (column_names rescue []).include?('shard')
46
+
44
47
  attr_encrypted :settings, marshal: true, key: :encryption_key, marshaler: SettingsMarshaler
45
48
  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
46
49
 
@@ -56,9 +59,6 @@ module PandaPal
56
59
  validates :canvas_account_id, presence: true
57
60
  validates :salesforce_id, presence: true, uniqueness: true
58
61
 
59
- after_create :create_schema
60
- after_commit :destroy_schema, on: :destroy
61
-
62
62
  define_setting("lti", {
63
63
  type: 'Hash',
64
64
  required: false,
@@ -71,10 +71,6 @@ module PandaPal
71
71
  end
72
72
  end
73
73
 
74
- before_validation on: [:update] do
75
- errors.add(:name, 'should not be changed after creation') if name_changed?
76
- end
77
-
78
74
  def encryption_key
79
75
  # production environment might not have loaded secret_key_base yet.
80
76
  # In that case, just read it from env.
@@ -85,18 +81,6 @@ module PandaPal
85
81
  end
86
82
  end
87
83
 
88
- def switch_tenant(&block)
89
- if block_given?
90
- Apartment::Tenant.switch(name, &block)
91
- else
92
- Apartment::Tenant.switch!(name)
93
- end
94
- end
95
-
96
- def self.current
97
- find_by_name(Apartment::Tenant.current)
98
- end
99
-
100
84
  def create_api(logic, expiration: nil, uses: nil, host: nil)
101
85
  switch_tenant do
102
86
  logic = "current_organization.#{logic}" if logic.is_a?(Symbol)
@@ -109,16 +93,6 @@ module PandaPal
109
93
  end
110
94
  end
111
95
 
112
- def rename!(new_name)
113
- do_switch = Apartment::Tenant.current == name
114
- ActiveRecord::Base.connection.execute(
115
- "ALTER SCHEMA \"#{name}\" RENAME TO \"#{new_name}\";"
116
- )
117
- self.class.where(id: id).update_all(name: new_name)
118
- reload
119
- switch_tenant if do_switch
120
- end
121
-
122
96
  if !PandaPal.lti_options[:platform].present? || PandaPal.lti_options[:platform].is_a?(String)
123
97
  CONST_PLATFORM_TYPE = Platform.resolve_platform_class(nil) rescue nil
124
98
  else
@@ -192,16 +166,6 @@ module PandaPal
192
166
  CONST_PLATFORM_TYPE
193
167
  end
194
168
 
195
- private
196
-
197
- def create_schema
198
- Apartment::Tenant.create name
199
- end
200
-
201
- def destroy_schema
202
- Apartment::Tenant.drop name
203
- end
204
-
205
169
  public
206
170
 
207
171
  PandaPal.resolved_extensions_for(self).each do |ext|
@@ -0,0 +1,22 @@
1
+ module PandaPal
2
+ module OrganizationConcerns
3
+ module MultiDatabaseSharding
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ if column_names.include?('shard')
8
+ validates :shard, format: { with: /\A[a-z0-9_]+\z/i }, allow_blank: true
9
+
10
+ before_validation on: [:update] do
11
+ errors.add(:shard, 'should not be changed after creation') if shard_changed?
12
+ end
13
+ end
14
+ end
15
+
16
+ def tenant_name
17
+ return "#{shard}:#{name}" if shard.present?
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ module PandaPal
2
+ module OrganizationConcerns
3
+ module TenantHandling
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def current
8
+ find_by_name(Apartment::Tenant.current)
9
+ end
10
+ end
11
+
12
+ included do
13
+ after_create :create_schema
14
+ after_commit :destroy_schema, on: :destroy
15
+
16
+ before_validation on: [:update] do
17
+ errors.add(:name, 'should not be changed after creation') if name_changed?
18
+ end
19
+ end
20
+
21
+ def tenant_name
22
+ name
23
+ end
24
+
25
+ def switch_tenant(&block)
26
+ if block_given?
27
+ Apartment::Tenant.switch(tenant_name, &block)
28
+ else
29
+ Apartment::Tenant.switch!(tenant_name)
30
+ end
31
+ end
32
+
33
+ def rename!(new_name)
34
+ do_switch = Apartment::Tenant.current == name
35
+ ActiveRecord::Base.connection.execute(
36
+ "ALTER SCHEMA \"#{name}\" RENAME TO \"#{new_name}\";"
37
+ )
38
+ self.class.where(id: id).update_all(name: new_name)
39
+ reload
40
+ switch_tenant if do_switch
41
+ end
42
+
43
+ private
44
+
45
+ def create_schema
46
+ Apartment::Tenant.create tenant_name
47
+ end
48
+
49
+ def destroy_schema
50
+ Apartment::Tenant.drop tenant_name
51
+ end
52
+ end
53
+ end
54
+ end
@@ -5,13 +5,212 @@ begin
5
5
  rescue LoadError
6
6
  end
7
7
 
8
+ require "apartment/adapters/postgresql_adapter"
9
+
10
+ module Apartment
11
+ SHARD_PREFIXES = ["SHARD_DB", "HEROKU_POSTGRESQL"]
12
+
13
+ def self.shard_configurations
14
+ $shard_configurations ||= begin
15
+ shard_to_env = {}
16
+
17
+ ENV.keys.each do |k|
18
+ m = /^(#{SHARD_PREFIXES.join("|")})_(\w+)_URL$/.match(k)
19
+ next unless m
20
+
21
+ url = ENV[k]
22
+ shard_to_env[m[2].downcase] = ActiveRecord::Base.configurations.resolve(url).configuration_hash
23
+ end
24
+
25
+ shard_to_env.freeze unless Rails.env.test?
26
+
27
+ shard_to_env
28
+ end
29
+ end
30
+
31
+ module Tenant
32
+ self.singleton_class.send(:alias_method, :original_postgresql_adapter, :postgresql_adapter)
33
+
34
+ def self.postgresql_adapter(config)
35
+ if Apartment.with_multi_server_setup
36
+ adapter = Adapters::PostgresMultiDBSchemaAdapter
37
+ adapter.new(config)
38
+ else
39
+ original_postgresql_adapter(config)
40
+ end
41
+ end
42
+
43
+ def self.split_tenant(tenant)
44
+ bits = tenant.split(":", 2)
45
+ bits.unshift(nil) if bits.length == 1
46
+ bits[0] = "default" if bits[0].nil? || bits[0].empty?
47
+ bits
48
+ end
49
+ end
50
+
51
+ module Adapters
52
+ class PostgresMultiDBSchemaAdapter < Apartment::Adapters::PostgresqlSchemaAdapter
53
+ def initialize(*args, **kwargs)
54
+ super
55
+ @excluded_model_set = Set.new(Apartment.excluded_models)
56
+ end
57
+
58
+ def process_excluded_model(excluded_model)
59
+ @excluded_model_set << excluded_model
60
+ super
61
+ end
62
+
63
+ def is_excluded_model?(model)
64
+ @excluded_model_set.include?(model.to_s)
65
+ end
66
+
67
+ def db_connection_config(tenant)
68
+ shard, schema = Tenant.split_tenant(tenant)
69
+ if shard == "default"
70
+ @config
71
+ else
72
+ Apartment.shard_configurations[shard.downcase]
73
+ end
74
+ end
75
+
76
+ def drop_command(conn, tenant)
77
+ shard, schema = Tenant.split_tenant(tenant)
78
+ conn.execute(%(DROP SCHEMA "#{schema}" CASCADE))
79
+ end
80
+
81
+ def tenant_exists?(tenant)
82
+ return true unless Apartment.tenant_presence_check
83
+ shard, schema = Tenant.split_tenant(tenant)
84
+
85
+ Apartment.connection.schema_exists?(schema)
86
+ end
87
+
88
+ def create_tenant_command(conn, tenant)
89
+ shard, schema = Tenant.split_tenant(tenant)
90
+ # NOTE: This was causing some tests to fail because of the database strategy for rspec
91
+ if conn.open_transactions.positive?
92
+ conn.execute(%(CREATE SCHEMA "#{schema}")).inspect
93
+ else
94
+ schema = %(BEGIN;
95
+ CREATE SCHEMA "#{schema}";
96
+ COMMIT;)
97
+
98
+ conn.execute(schema)
99
+ end
100
+ rescue *rescuable_exceptions => e
101
+ rollback_transaction(conn)
102
+ raise e
103
+ end
104
+
105
+ def connect_to_new(tenant = nil)
106
+ return reset if tenant.nil?
107
+
108
+ current_tenant = @current
109
+ tenants_array = tenant.is_a?(Array) ? tenant.map(&:to_s) : tenant.to_s
110
+ tenant_schemas = map_to_schemas(Array(tenants_array))
111
+ query_cache_enabled = ActiveRecord::Base.connection.query_cache_enabled
112
+
113
+ @current = tenants_array
114
+
115
+ raise ActiveRecord::StatementInvalid, "PandaPal/Apartment Mutli-DB support does not currently support DB roles" if ActiveRecord::Base.current_role != ActiveRecord::Base.default_role
116
+
117
+ unless ActiveRecord::Base.connected?
118
+ Apartment.establish_connection multi_tenantify(tenant, false)
119
+ Apartment.connection.verify!
120
+ end
121
+
122
+ Apartment.connection.enable_query_cache! if query_cache_enabled
123
+
124
+ raise ActiveRecord::StatementInvalid, "Could not find schema for tenant #{tenant} (#{tenant_schemas.inspect})" unless schema_exists?(tenant_schemas)
125
+
126
+ Apartment.connection.schema_search_path = full_search_path
127
+ rescue *rescuable_exceptions => e
128
+ @current = current_tenant
129
+ raise_schema_connect_to_new(tenant, e)
130
+ end
131
+
132
+ protected
133
+
134
+ def persistent_schemas
135
+ map_to_schemas(super)
136
+ end
137
+
138
+ def map_to_schemas(tenants)
139
+ all_shard = nil
140
+ tenants.map do |schema|
141
+ shard, schema = Tenant.split_tenant(schema)
142
+ all_shard ||= shard
143
+ raise "Cannot mix shards in persistent_schemas" if shard != all_shard
144
+ schema
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ module CorePatches
151
+ module ActiveRecord
152
+ module FutureResult
153
+ # called in the original thread that knows the tenant
154
+ def initialize(_pool, *_args, **_kwargs)
155
+ @tenant = Apartment::Tenant.current
156
+ super
157
+ end
158
+
159
+ # called in the new thread with a connection that needs switching
160
+ def exec_query(_conn, *_args, **_kwargs)
161
+ Apartment::Tenant.switch!(@tenant) unless Apartment::Tenant.current == @tenant
162
+ super
163
+ end
164
+ end
165
+
166
+ module Base
167
+ extend ActiveSupport::Concern
168
+
169
+ included do
170
+ self.singleton_class.send(:alias_method, :pre_apartment_current_shard, :current_shard)
171
+
172
+ def self.current_shard
173
+ # This implementation is definitely a hack, but it should be fairly compatible. If you need to leverage
174
+ # Rails' sharding natively - you should just need to add models to the excluded_models list in Apartment
175
+ # to effectively disable this patch for that model.
176
+ if (adapter = Thread.current[:apartment_adapter]) && adapter.is_a?(Apartment::Adapters::PostgresMultiDBSchemaAdapter) && !adapter.is_excluded_model?(self)
177
+ shard, schema = Apartment::Tenant.split_tenant(adapter.current)
178
+ return "apt:#{shard}" unless shard == "default"
179
+ end
180
+
181
+ pre_apartment_current_shard
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ # Fix Apartment's lack of support for load_async
189
+ ::ActiveRecord::FutureResult.prepend CorePatches::ActiveRecord::FutureResult
190
+
191
+ # Hack ActiveRecord shards/connection_pooling to support our multi-DB approach
192
+ ::ActiveRecord::Base.include CorePatches::ActiveRecord::Base
193
+ end
194
+
8
195
  Apartment.configure do |config|
9
196
  config.excluded_models ||= []
10
197
  config.excluded_models |= ['PandaPal::Organization', 'PandaPal::Session']
11
198
 
12
- config.tenant_names = lambda {
13
- PandaPal::Organization.pluck(:name)
14
- }
199
+ config.with_multi_server_setup = true unless Rails.env.test?
200
+
201
+ config.tenant_names = lambda do
202
+ if PandaPal::Organization < PandaPal::OrganizationConcerns::MultiDatabaseSharding
203
+ base_config = Apartment.connection_config
204
+ shard_configurations = Apartment.shard_configurations
205
+
206
+ PandaPal::Organization.all.to_a.each_with_object({}) do |org, hash|
207
+ shard = org.shard || "default"
208
+ hash[org.tenant_name] = shard == "default" ? base_config : shard_configurations[shard.downcase]
209
+ end
210
+ else
211
+ PandaPal::Organization.pluck(:name)
212
+ end
213
+ end
15
214
  end
16
215
 
17
216
  Rails.application.config.middleware.use Apartment::Elevators::Generic, lambda { |request|
@@ -7,8 +7,6 @@ require_relative './helpers/console_helpers'
7
7
 
8
8
  module PandaPal
9
9
  class Engine < ::Rails::Engine
10
- config.autoload_once_paths += Dir["#{config.root}/lib/**/"]
11
-
12
10
  isolate_namespace PandaPal
13
11
 
14
12
  config.generators do |g|
@@ -0,0 +1,85 @@
1
+ module PandaPal::SpecHelper
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def with_multiple_shards(shards:, tenant_names: [], determine_shard: nil, transactional: true)
6
+ self.use_transactional_tests = transactional
7
+
8
+ original_settings = {}
9
+
10
+ before :all do
11
+ if defined?(DatabaseCleaner)
12
+ original_settings[:cleaner_strategy] = DatabaseCleaner.strategy
13
+ DatabaseCleaner.strategy = :truncation
14
+ end
15
+
16
+ Apartment.configure do |config|
17
+ original_settings[:with_multi_server_setup] = config.with_multi_server_setup
18
+ config.with_multi_server_setup = true
19
+ end
20
+
21
+ shards.each do |k, v|
22
+ Apartment.shard_configurations[k.to_s] = Apartment.connection_config.merge(database: v)
23
+ end
24
+
25
+ @existing_org_ids = PandaPal::Organization.pluck(:id)
26
+
27
+ Apartment::Tenant.reload!
28
+
29
+ clean_schemas(tenant_names)
30
+ end
31
+
32
+ after :all do
33
+ Apartment::Tenant.reset
34
+
35
+ Apartment.configure do |config|
36
+ config.with_multi_server_setup = false
37
+ end
38
+
39
+ shards.each do |k, _|
40
+ Apartment.shard_configurations.delete(k.to_s)
41
+ end
42
+
43
+ PandaPal::Organization.where.not(id: @existing_org_ids).delete_all
44
+
45
+ if defined?(DatabaseCleaner)
46
+ DatabaseCleaner.strategy = original_settings[:cleaner_strategy]
47
+ end
48
+ end
49
+
50
+ before :each do
51
+ clean_schemas(tenant_names)
52
+
53
+ @each_existing_org_ids = PandaPal::Organization.pluck(:id)
54
+
55
+ if determine_shard
56
+ allow_any_instance_of(PandaPal::Organization).to receive(:tenant_name) do |org|
57
+ shard = determine_shard.call(org)
58
+ tenant = org.name
59
+ tenant = "#{shard}:#{tenant}" if shard.present?
60
+ tenant
61
+ end
62
+ end
63
+ end
64
+
65
+ after :each do
66
+ Apartment::Tenant.reset
67
+ PandaPal::Organization.where.not(id: @each_existing_org_ids).destroy_all
68
+ end
69
+
70
+ define_method :clean_schemas do |schemas|
71
+ # Clean known schemas if an Organization record does not exist for them
72
+ schema_to_fqt = schemas.index_by { |s| Apartment::Tenant.split_tenant(s)[1] }
73
+ schemas_to_clean = schema_to_fqt.keys - PandaPal::Organization.pluck(:name)
74
+
75
+ schemas_to_clean.each do |schema|
76
+ # fqt = schema_to_fqt[schema]
77
+ Apartment::Tenant.drop(schema) rescue nil
78
+ shards.each do |k, v|
79
+ Apartment::Tenant.drop("#{k}:#{schema}") rescue nil
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,3 @@
1
1
  module PandaPal
2
- VERSION = "5.11.0"
2
+ VERSION = "5.12.1"
3
3
  end
data/panda_pal.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.test_files = Dir["spec/**/*"].reject{|f| f =~ /\/log\//}
22
22
 
23
23
  s.add_dependency "rails", ">= 4.2"
24
- s.add_dependency 'ros-apartment', '~> 2.2'
24
+ s.add_dependency 'ros-apartment', '~> 3.0'
25
25
  s.add_dependency 'ims-lti', '~> 1.2.4'
26
26
  s.add_dependency 'browser', '2.5.0'
27
27
  s.add_dependency 'attr_encrypted', '~> 4.0.0'
@@ -30,11 +30,11 @@ Gem::Specification.new do |s|
30
30
  s.add_dependency 'jwt'
31
31
  s.add_dependency 'httparty'
32
32
 
33
- s.add_development_dependency "rails", "~> 5.2"
33
+ s.add_development_dependency "rails", "~> 7.0"
34
34
  s.add_development_dependency 'pg'
35
35
  s.add_development_dependency 'sidekiq', "< 7.0"
36
36
  s.add_development_dependency 'sidekiq-scheduler'
37
- s.add_development_dependency 'ros-apartment', '~> 2.11'
37
+ s.add_development_dependency 'ros-apartment', '~> 3.0'
38
38
  s.add_development_dependency 'ros-apartment-sidekiq', '~> 1.2'
39
39
  s.add_development_dependency 'rspec-rails'
40
40
  s.add_development_dependency 'factory_girl_rails'
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe PandaPal::Organization, type: :model do
6
+ context "across multiple DBs" do
7
+ with_multiple_shards(
8
+ shards: { alt: "panda_pal_test2" },
9
+ tenant_names: %w[on_primary alt:on_alt],
10
+ transactional: false,
11
+ determine_shard: ->(org) { org.name.include?("alt") ? "alt" : nil }
12
+ )
13
+
14
+ let!(:org1) { create(:panda_pal_organization, name: "on_primary") }
15
+ let!(:org2) { create(:panda_pal_organization, name: "on_alt") }
16
+
17
+ it "successfully creates tenants on multiple shards" do
18
+ expect(1).to eql(1)
19
+ end
20
+
21
+ it "only creates schemas on the target shard" do
22
+ org1.switch_tenant do
23
+ schemas = PandaPal::ApiCall.connection.exec_query("SELECT schema_name FROM information_schema.schemata").pluck("schema_name")
24
+ expect(schemas).to include("on_primary")
25
+ expect(schemas).to_not include("on_alt")
26
+ end
27
+ org2.switch_tenant do
28
+ schemas = PandaPal::ApiCall.connection.exec_query("SELECT schema_name FROM information_schema.schemata").pluck("schema_name")
29
+ expect(schemas).to include("on_alt")
30
+ expect(schemas).to_not include("on_primary")
31
+ end
32
+ end
33
+
34
+ context "load_async" do
35
+ it "works across shards" do
36
+ qs = []
37
+ PandaPal::Organization.all.each do |org|
38
+ org.switch_tenant do
39
+ PandaPal::ApiCall.create!(logic: org.name)
40
+ qs << PandaPal::ApiCall.select("logic, pg_sleep(0.5)").load_async
41
+ end
42
+ end
43
+ calls = qs.map(&:to_a).flatten
44
+ expect(calls.pluck(:logic)).to match_array(["on_primary", "on_alt"])
45
+ end
46
+ end
47
+ end
48
+ end
@@ -14,8 +14,12 @@ development:
14
14
  database: panda_pal_development
15
15
 
16
16
  test:
17
- <<: *default
18
- database: panda_pal_test
17
+ test1:
18
+ <<: *default
19
+ database: panda_pal_test1
20
+ test2:
21
+ <<: *default
22
+ database: panda_pal_test2
19
23
 
20
24
  production:
21
25
  <<: *default
@@ -37,6 +37,11 @@ Rails.application.configure do
37
37
  # Print deprecation notices to the stderr.
38
38
  config.active_support.deprecation = :stderr
39
39
 
40
+ config.log_level = :debug
41
+ config.active_record.verbose_query_logs = true
42
+ config.active_record.async_query_executor = :multi_thread_pool
43
+ # config.active_record.global_executor_concurrency = 4
44
+
40
45
  # Raises error for missing translations
41
46
  # config.action_view.raise_on_missing_translations = true
42
47
  end
@@ -0,0 +1,18 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your
6
+ # database schema. If you need to create the application database on another
7
+ # system, you should be using db:schema:load, not running all the migrations
8
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(version: 2022_07_21_095653) do
14
+
15
+ # These are extensions that must be enabled in order to support this database
16
+ enable_extension "plpgsql"
17
+
18
+ end
@@ -17,7 +17,7 @@ module PandaPal
17
17
 
18
18
  describe "#call" do
19
19
  it "calls the function with parameters" do
20
- expect(subject.call(foo: 'FOO')).to eq 'FOO_bar'
20
+ expect(subject.call({ foo: 'FOO' })).to eq 'FOO_bar'
21
21
  end
22
22
  end
23
23
 
@@ -5,6 +5,10 @@ module PandaPal
5
5
  RSpec.describe OrganizationConcerns::SettingsValidation, type: :model do
6
6
  let!(:org) { create :panda_pal_organization }
7
7
 
8
+ after :all do
9
+ PandaPal.lti_options[:settings_structure] = nil
10
+ end
11
+
8
12
  def set_test_settings_structure
9
13
  PandaPal.lti_options = {
10
14
  title: 'Test App',
data/spec/rails_helper.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  ActiveRecord::Migration.maintain_test_schema!
2
2
 
3
+ require "panda_pal/spec_helper"
4
+
3
5
  RSpec.configure do |config|
6
+ config.include PandaPal::SpecHelper
7
+
4
8
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
5
9
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
6
10
 
data/spec/spec_helper.rb CHANGED
@@ -11,7 +11,6 @@ require 'factory_girl_rails'
11
11
 
12
12
  ActiveRecord::Migration.maintain_test_schema!
13
13
  ActiveRecord::Schema.verbose = false
14
- load 'dummy/db/schema.rb' # db agnostic
15
14
 
16
15
  RSpec.configure do |config|
17
16
  config.include FactoryGirl::Syntax::Methods
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.11.0
4
+ version: 5.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure CustomDev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-25 00:00:00.000000000 Z
11
+ date: 2024-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.2'
33
+ version: '3.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.2'
40
+ version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: ims-lti
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '5.2'
145
+ version: '7.0'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '5.2'
152
+ version: '7.0'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: pg
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -198,14 +198,14 @@ dependencies:
198
198
  requirements:
199
199
  - - "~>"
200
200
  - !ruby/object:Gem::Version
201
- version: '2.11'
201
+ version: '3.0'
202
202
  type: :development
203
203
  prerelease: false
204
204
  version_requirements: !ruby/object:Gem::Requirement
205
205
  requirements:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
- version: '2.11'
208
+ version: '3.0'
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: ros-apartment-sidekiq
211
211
  requirement: !ruby/object:Gem::Requirement
@@ -282,7 +282,7 @@ dependencies:
282
282
  - - '='
283
283
  - !ruby/object:Gem::Version
284
284
  version: 2.7.1
285
- description:
285
+ description:
286
286
  email:
287
287
  - pseng@instructure.com
288
288
  executables: []
@@ -311,9 +311,11 @@ files:
311
311
  - app/lib/panda_pal/lti_jwt_validator.rb
312
312
  - app/models/panda_pal/api_call.rb
313
313
  - app/models/panda_pal/organization.rb
314
+ - app/models/panda_pal/organization_concerns/multi_database_sharding.rb
314
315
  - app/models/panda_pal/organization_concerns/organization_builder.rb
315
316
  - app/models/panda_pal/organization_concerns/settings_validation.rb
316
317
  - app/models/panda_pal/organization_concerns/task_scheduling.rb
318
+ - app/models/panda_pal/organization_concerns/tenant_handling.rb
317
319
  - app/models/panda_pal/panda_pal_record.rb
318
320
  - app/models/panda_pal/platform.rb
319
321
  - app/models/panda_pal/platform/canvas.rb
@@ -343,21 +345,23 @@ files:
343
345
  - lib/panda_pal/helpers/secure_headers.rb
344
346
  - lib/panda_pal/helpers/session_replacement.rb
345
347
  - lib/panda_pal/plugins.rb
348
+ - lib/panda_pal/spec_helper.rb
346
349
  - lib/panda_pal/version.rb
347
350
  - lib/tasks/panda_pal_tasks.rake
348
351
  - panda_pal.gemspec
349
352
  - spec/controllers/panda_pal/api_call_controller_spec.rb
353
+ - spec/core/apartment_multidb_spec.rb
350
354
  - spec/dummy/README.rdoc
351
355
  - spec/dummy/Rakefile
352
356
  - spec/dummy/app/assets/javascripts/application.js
353
357
  - spec/dummy/app/assets/stylesheets/application.css
358
+ - spec/dummy/app/bin/bundle
359
+ - spec/dummy/app/bin/rails
360
+ - spec/dummy/app/bin/rake
361
+ - spec/dummy/app/bin/setup
354
362
  - spec/dummy/app/controllers/application_controller.rb
355
363
  - spec/dummy/app/helpers/application_helper.rb
356
364
  - spec/dummy/app/views/layouts/application.html.erb
357
- - spec/dummy/bin/bundle
358
- - spec/dummy/bin/rails
359
- - spec/dummy/bin/rake
360
- - spec/dummy/bin/setup
361
365
  - spec/dummy/config.ru
362
366
  - spec/dummy/config/application.rb
363
367
  - spec/dummy/config/boot.rb
@@ -377,6 +381,7 @@ files:
377
381
  - spec/dummy/config/routes.rb
378
382
  - spec/dummy/config/secrets.yml
379
383
  - spec/dummy/db/schema.rb
384
+ - spec/dummy/db/test2_schema.rb
380
385
  - spec/dummy/public/404.html
381
386
  - spec/dummy/public/422.html
382
387
  - spec/dummy/public/500.html
@@ -394,7 +399,7 @@ homepage: http://instructure.com
394
399
  licenses:
395
400
  - MIT
396
401
  metadata: {}
397
- post_install_message:
402
+ post_install_message:
398
403
  rdoc_options: []
399
404
  require_paths:
400
405
  - lib
@@ -409,52 +414,54 @@ required_rubygems_version: !ruby/object:Gem::Requirement
409
414
  - !ruby/object:Gem::Version
410
415
  version: '0'
411
416
  requirements: []
412
- rubygems_version: 3.0.3.1
413
- signing_key:
417
+ rubygems_version: 3.5.16
418
+ signing_key:
414
419
  specification_version: 4
415
420
  summary: LTI mountable engine
416
421
  test_files:
417
- - spec/spec_helper.rb
418
- - spec/dummy/app/controllers/application_controller.rb
419
- - spec/dummy/app/views/layouts/application.html.erb
422
+ - spec/controllers/panda_pal/api_call_controller_spec.rb
423
+ - spec/core/apartment_multidb_spec.rb
424
+ - spec/dummy/README.rdoc
425
+ - spec/dummy/Rakefile
420
426
  - spec/dummy/app/assets/javascripts/application.js
421
427
  - spec/dummy/app/assets/stylesheets/application.css
428
+ - spec/dummy/app/bin/bundle
429
+ - spec/dummy/app/bin/rails
430
+ - spec/dummy/app/bin/rake
431
+ - spec/dummy/app/bin/setup
432
+ - spec/dummy/app/controllers/application_controller.rb
422
433
  - spec/dummy/app/helpers/application_helper.rb
423
- - spec/dummy/bin/rake
424
- - spec/dummy/bin/setup
425
- - spec/dummy/bin/bundle
426
- - spec/dummy/bin/rails
427
- - spec/dummy/config/secrets.yml
428
- - spec/dummy/config/routes.rb
429
- - spec/dummy/config/locales/en.yml
430
- - spec/dummy/config/environments/production.rb
431
- - spec/dummy/config/environments/development.rb
432
- - spec/dummy/config/environments/test.rb
433
- - spec/dummy/config/environment.rb
434
+ - spec/dummy/app/views/layouts/application.html.erb
434
435
  - spec/dummy/config/application.rb
435
- - spec/dummy/config/database.yml
436
436
  - spec/dummy/config/boot.rb
437
+ - spec/dummy/config/database.yml
438
+ - spec/dummy/config/environment.rb
439
+ - spec/dummy/config/environments/development.rb
440
+ - spec/dummy/config/environments/production.rb
441
+ - spec/dummy/config/environments/test.rb
437
442
  - spec/dummy/config/initializers/backtrace_silencers.rb
438
- - spec/dummy/config/initializers/mime_types.rb
443
+ - spec/dummy/config/initializers/cookies_serializer.rb
439
444
  - spec/dummy/config/initializers/filter_parameter_logging.rb
445
+ - spec/dummy/config/initializers/inflections.rb
446
+ - spec/dummy/config/initializers/mime_types.rb
440
447
  - spec/dummy/config/initializers/session_store.rb
441
448
  - spec/dummy/config/initializers/wrap_parameters.rb
442
- - spec/dummy/config/initializers/cookies_serializer.rb
443
- - spec/dummy/config/initializers/inflections.rb
449
+ - spec/dummy/config/locales/en.yml
450
+ - spec/dummy/config/routes.rb
451
+ - spec/dummy/config/secrets.yml
444
452
  - spec/dummy/config.ru
445
- - spec/dummy/Rakefile
446
- - spec/dummy/public/favicon.ico
453
+ - spec/dummy/db/schema.rb
454
+ - spec/dummy/db/test2_schema.rb
455
+ - spec/dummy/public/404.html
447
456
  - spec/dummy/public/422.html
448
457
  - spec/dummy/public/500.html
449
- - spec/dummy/public/404.html
450
- - spec/dummy/db/schema.rb
451
- - spec/dummy/README.rdoc
458
+ - spec/dummy/public/favicon.ico
459
+ - spec/factories/panda_pal_organizations.rb
460
+ - spec/factories/panda_pal_sessions.rb
461
+ - spec/models/panda_pal/api_call_spec.rb
452
462
  - spec/models/panda_pal/organization/settings_validation_spec.rb
453
463
  - spec/models/panda_pal/organization/task_scheduling_spec.rb
454
- - spec/models/panda_pal/session_spec.rb
455
- - spec/models/panda_pal/api_call_spec.rb
456
464
  - spec/models/panda_pal/organization_spec.rb
457
- - spec/factories/panda_pal_sessions.rb
458
- - spec/factories/panda_pal_organizations.rb
459
- - spec/controllers/panda_pal/api_call_controller_spec.rb
465
+ - spec/models/panda_pal/session_spec.rb
460
466
  - spec/rails_helper.rb
467
+ - spec/spec_helper.rb
File without changes
File without changes
File without changes
File without changes